Cours Java
Cours Java
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
La surcharge de méthode
Votre première classe
Structure de base
Les constructeurs
Accesseurs et mutateurs
Les variables de classes
Le principe d'encapsulation
L'héritage
Le principe de l'héritage
Le polymorphisme
Depuis Java 7 : la classe Objects
Modéliser ses objets grâce à UML
Présentation d'UML
Modéliser ses objets
Modéliser les liens entre les objets
Les packages
Création d'un package
Droits d'accès entre les packages
Les classes abstraites et les interfaces
Les classes abstraites
Les interfaces
Le pattern strategy
Les exceptions
Le bloc try{...} catch{...}
Les exceptions personnalisées
La gestion de plusieurs exceptions
Depuis Java 7 : le multi-catch
Les énumérations
Avant les énumérations
Une solution : les enum
Les collections d'objets
Les di érents types de collections
Les objets List
Les objets Map
Les objets Set
La généricité en Java
Principe de base
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Généricité et collections
Les flux d'entrée/sortie
Utilisation de [Link]
Utilisation de [Link]
Depuis Java 7 : nio II
Le pattern decorator
Java et la réflexivité
L'objet Class
Instanciation dynamique
Notre première fenêtre
L'objet JFrame
L'objet JPanel
Les objets Graphics et Graphics2D
Le fil rouge : une animation
Création de l'animation
Améliorations
Positionner des boutons
Utiliser la classe JButton
Positionner son composant : les layout managers
Interagir avec des boutons
Une classe Bouton personnalisée
Interagir avec son bouton
Être à l'écoute de ses objets : le design pattern Observer
Cadeau : un bouton personnalisé optimisé
TP : une calculatrice
Élaboration
Conception
Correction
Générer un .jar exécutable
Exécuter des tâches simultanément
Une classe héritée de Thread
Utiliser l'interface Runnable
Synchroniser ses threads
Contrôler son animation
Depuis Java 7 : le pattern Fork/Join
Les champs de formulaire
Les listes : l'objet JComboBox
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les cases à cocher : l'objet JCheckBox
Les champs de texte : l'objet JTextField
Contrôle du clavier : l'interface KeyListener
Les menus et boîtes de dialogue
Les boîtes de dialogue
Les menus
TP : l'ardoise magique
Cahier des charges
Prérequis
Correction
Améliorations possibles
Conteneurs, sliders et barres de progression
Autres conteneurs
Enjoliver vos IHM
Les arbres et leur structure
La composition des arbres
Des arbres qui vous parlent
Décorez vos arbres
Modifier le contenu de nos arbres
Les interfaces de tableaux
Premiers pas
Gestion de l'a ichage
Interaction avec l'objet JTable
Ajouter des lignes et des colonnes
TP : le pendu
Cahier des charges
Prérequis
Correction
Mieux structurer son code : le pattern MVC
Premiers pas
Le modèle
Le contrôleur
La vue
Le Drag'n Drop
Présentation
Fonctionnement
Créer son propre TransferHandler
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Activer le drop sur un JTree
E et de déplacement
Mieux gérer les interactions avec les composants
Présentation des protagonistes
Utiliser l'EDT
La classe SwingWorker<T, V>
JDBC : la porte d'accès aux bases de données
Rappels sur les bases de données
Préparer la base de données
Se connecter à la base de données
Fouiller dans sa base de données
Le couple Statement - ResultSet
Les requêtes préparées
Modifier des données
Statement, toujours plus fort
Gérer les transactions manuellement
Limiter le nombre de connexions
Pourquoi ne se connecter qu'une seule fois ?
Le pattern singleton
Le singleton dans tous ses états
TP : un testeur de requêtes
Cahier des charges
Quelques captures d'écran
Correction
Lier ses tables avec des objets Java : le pattern DAO
Avant toute chose
Le pattern DAO
Le pattern factory
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Java est un langage de programmation moderne développé par Sun Microsystems (aujourd'hui racheté par Oracle). Il ne faut surtout pas le confondre
avec JavaScript (langage de scripts utilisé principalement 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.
des applets, qui sont des programmes Java incorporés à des pages web ;
Comme vous le voyez, Java permet de réaliser une très grande quantité d'applications di érentes ! Mais... comment apprendre un langage si vaste qui o re
autant de possibilités ? o_O
Heureusement, ce cours est là pour tout vous apprendre de Java à partir de zéro :) .
en Java
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ce cours vous plaît ?
Si vous avez aimé ce cours, vous pouvez retrouver le livre "Apprenez à programmer en Java" du même auteur, en vente sur le Site du Zéro, en librairie et
dans les boutiques en ligne. Vous y trouverez ce cours adapté au format papier.
Plus d'informations
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 ! :)
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Encart de téléchargement
Vous avez sans doute remarqué qu'on vous propose de télécharger soit le JRE, soit le JDK (Java Development Kit). La di érence entre ces deux environnements
est écrite, mais pour les personnes fâchées avec l'anglais, sachez que le JRE contient tout le nécessaire pour que vos programmes Java puissent être exécutés sur
votre ordinateur ; le JDK, en plus de contenir le JRE, contient tout le nécessaire pour développer, compiler…
L'IDE contenant déjà tout le nécessaire pour le développement et la compilation, nous n'avons besoin que du JRE. Une fois que vous avez cliqué sur
Download JRE , vous arrivez sur la page représentée à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Choix du système d'exploitation
Cochez la case : Accept License Agreement puis cliquez sur le lien correspondant à votre système d'exploitation ( x86 pour un système 32 bits et x64 pour
un système 64 bits). Une popup de téléchargement doit alors apparaître.
Je vous ai dit que Java permet de développer di érents types d'applications ; il y a donc des environnements permettant de créer des programmes pour
di érentes plates-formes :
J2SE (Java 2 Standard Edition, celui qui nous intéresse dans cet ouvrage) : permet de développer des applications dites « client lourd », par exemple Word,
Excel, la suite OpenO [Link]… Toutes ces applications sont des « clients lourds » . C'est ce que nous allons faire dans ce cours
J2EE (Java 2 Enterprise Edition) : permet de développer des applications web en Java. On parle aussi de clients légers.
J2ME (Java 2 Micro Edition) : permet de développer des applications pour appareils portables, comme des téléphones portables, des PDA…
Eclipse IDE
Avant toute chose, quelques mots sur le projet Eclipse. « Eclipse IDE » est un environnement de développement libre permettant de créer des programmes dans
de nombreux langages de programmation (Java, C++, PHP…). C'est l'outil que nous allons utiliser pour programmer.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Eclipse IDE est lui-même principalement écrit en Java.
Je vous invite donc à télécharger Eclipse IDE. Une fois la page de téléchargement choisissez Eclipse IDE for Java Developers , en choisissant la version
d'Eclipse correspondant à votre OS (Operating System = système d'exploitation), comme indiqué à la figure suivante.
Sélectionnez maintenant le miroir que vous souhaitez utiliser pour obtenir Eclipse. Voilà, vous n'avez plus qu'à attendre la fin du téléchargement.
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. Notre logiciel va donc permettre de traduire nos futurs programmes Java en langage byte code, compréhensible uniquement par votre JRE, fraîchement
installé.
La spécificité d'Eclipse IDE vient du fait que son architecture est totalement développée autour de la notion de plugin. Cela signifie que toutes ses fonctionnalités
sont développées en tant que plugins. Pour faire court, si vous voulez ajouter des fonctionnalités à Eclipse, vous devez :
Lorsque vous téléchargez un nouveau plugin pour Eclipse, 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'Eclipse. Il vous faut donc copier le contenu des dossiers de
votre plugin vers le dossier correspondant dans Eclipse ( plugins dans plugins et features dans features ).
Vous devez maintenant avoir une archive contenant Eclipse. Décompressez-la où vous voulez, entrez dans ce dossier et lancez Eclipse. Au démarrage, comme le
montre la figure suivante, Eclipse vous demande dans quel dossier vous souhaitez enregistrer vos projets ; sachez que rien ne vous empêche de spécifier un autre
dossier que celui proposé par défaut. Une fois cette étape e ectuée, vous arrivez sur la page d'accueil d'Eclipse. Si vous avez envie d'y jeter un oeil, allez-y !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous devez indiquer où enregistrer vos projets
File : C'est ici que nous pourrons créer de nouveaux projets Java, les enregistrer et les exporter le cas échéant.
Les raccourcis à retenir sont :
Edit : Dans ce menu, nous pourrons utiliser les commandes « copier » , « coller », etc.
Window : Dans celui-ci, nous pourrons configurer Eclipse selon nos besoins.
La barre d'outils
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
La barre d'outils ressemble à la figure suivante.
1. nouveau général : cliquer sur ce bouton revient à faire Fichier > Nouveau ;
5. créer un nouveau projet : revient à faire Fichier > Nouveau > Java Project ;
6. créer une nouvelle classe : créer un nouveau fichier. Cela revient à faire Fichier > Nouveau > Classe .
Maintenant, je vais vous demander de créer un nouveau projet Java, comme indiqué aux figures suivantes.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Création de projet Java - étape 1
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Création de projet Java - étape 2
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Renseignez le nom de votre projet comme je l'ai fait dans le premier encadré de la deuxième figure. Vous pouvez aussi voir où sera enregistré ce projet. Un peu
plus compliqué, maintenant : vous avez 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, comme sur le deuxième encadré de la deuxième figure. Vous pourrez changer ceci à tout moment dans Eclipse en allant
dans Window > Preferences , en dépliant l'arbre Java dans la fenêtre et en choisissant Installed JRE .
Vous devriez avoir un nouveau projet dans la fenêtre de gauche, comme à la figure suivante.
Explorateur de projet
Pour boucler la boucle, ajoutons dès maintenant une nouvelle classe dans ce projet comme nous avons appris à le faire plus tôt via la barre d'outils. La figure
suivante représente la fenêtre sur laquelle vous devriez tomber.
Une classe est un ensemble de codes contenant plusieurs instructions que doit e ectuer votre programme. Ne vous attardez pas trop sur ce terme, nous
aurons l'occasion d'y revenir.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Création d'une classe
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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 choisi « sdz1 ». Dans l'encadré 3,
Eclipse vous demande si cette classe a quelque chose de particulier. Eh bien oui ! Cochez
public static void main(String[] args) (nous reviendrons plus tard sur ce point), puis cliquez sur Finish . La fenêtre principale d'Eclipse se lance,
comme à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Avant de commencer à coder, nous allons explorer l'espace de travail. Dans l'encadré de gauche (le vert), 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é positionné au centre (le bleu), je pense que vous avez
deviné : c'est ici que nous allons écrire nos codes source. Dans l'encadré du bas (le rouge), c'est là que vous verrez apparaître le contenu de vos programmes…
ainsi que les erreurs éventuelles ! Et pour finir, c'est dans l'encadré de droite (le violet), dès que nous aurons appris à coder nos propres fonctions et nos objets,
que la liste des méthodes et des variables sera a ichée.
Ceci, car la machine virtuelle Java se présente di éremment selon qu'on se trouve sous Mac, sous Linux ou encore sous Windows. Par contre, le byte code, lui,
reste le même quel que soit l'environnement avec lequel 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 (il existe plusieurs types de byte code, mais nous parlons ici de celui créé par Java) n'est rien d'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 pour extension .java
et un fichier précompilé a l'extension .class : c'est dans ce dernier que vous trouverez du byte code. Je vous invite à examiner un fichier .class à la fin de
cette partie (vous en aurez au moins un), mais je vous préviens, c'est illisible !
Par contre, vos fichiers .java sont de simples fichiers texte dont l'extension a été changée. Vous pouvez donc les ouvrir, les créer ou encore les mettre à jour
avec le Bloc-notes de Windows, par exemple. Cela implique que, si vous le souhaitez, vous pouvez écrire des programmes Java avec le Bloc-notes ou encore avec
Notepad++.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Reprenons. Vous devez savoir que tous les programmes Java sont composés d'au moins une classe. Elle doit contenir une méthode appelée main : ce sera le
point de démarrage de notre programme.
Une méthode est une suite d'instructions à exécuter. C'est un morceau de logique de notre programme. Une méthode contient :
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.
Je vous avais demandé de créer un projet Java ; ouvrez-le ! Vous voyez la fameuse classe dont je vous parlais ? Ici, elle s'appelle « sdz1 », comme à la figure
suivante. Vous pouvez voir que le mot class est précédé du mot public , dont nous verrons la signification lorsque nous programmerons des objets.
Méthode principale
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 son contenu est
délimité par des accolades ({ }). Nous écrirons nos codes sources entre les accolades de la méthode main . La syntaxe de cette méthode est toujours la même :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Excuse-nous, mais… pourquoi as-tu écrit « //Contenu de votre classe » et pas « Contenu de votre classe » ?
Bonne question ! Je vous ai dit précédemment 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 ! C’est ce qu’on appelle des commentaires, et deux syntaxes sont disponibles pour
commenter son texte :
1. Les commentaires unilignes : introduits par les symboles « // », ils mettent tout ce qui les suit en commentaire, du moment que le texte se trouve sur la même
ligne ;
2. Les commentaires multilignes : ils sont introduits par les symboles « /* » et se terminent par les symboles « */ ».
/*
Un commentaire
Un autre
Encore un autre
*/
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, leurs tailles et le nombre de classes qui les
composeront vont augmenter. 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 e ectue des choses compliquées afin de savoir où vous en êtes dans vos traitements…
Il existe en fait une troisième syntaxe, mais elle a une utilité particulière. Elle permettra de générer une documentation pour votre programme (on l'appelle «
Javadoc » pour « Java Documentation »). Je n'en parlerai que très peu, et pas dans ce chapitre. Nous verrons cela lorsque nous programmerons des objets, mais
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
pour les curieux, je vous conseille le très bon cours de dworkin sur ce sujet disponible sur le Site du Zéro.
À 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 s'exécutera de façon procédurale, c'est-à-dire qui s'e ectue 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 parvenu à la fin du code. Cela 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…).
Hello World
Maintenant, essayons de taper le code suivant :
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 , il vous faut lancer le programme. Si vous vous souvenez bien de la présentation faite
précédemment, vous devez cliquer sur la flèche blanche dans un rond vert, comme à la figure suivante.
Si vous regardez dans votre console, dans la fenêtre du bas sous Eclipse, vous devriez voir quelque chose ressemblant à la figure suivante.
La console d'Eclipse
Littéralement, elle signifie « la méthode print() va écrire « Hello World ! » en utilisant l'objet out de la classe System » . Avant que vous arrachiez les
cheveux, voici quelques précisions :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
System : ceci correspond à l'appel d'une classe qui se nomme « System » . C'est une classe utilitaire qui permet surtout d'utiliser l'entrée et la sortie
standard, c'est-à-dire la saisie clavier et l'a ichage à l'écran.
print : méthode qui écrit dans la console le texte passé en paramètre (entre les parenthèses).
Lorsque vous l'exécutez, vous devriez voir des chaînes de caractères qui se suivent sans saut de ligne. Autrement dit, ceci s'a ichera dans votre console :
Je me doute que vous souhaiteriez insérer un retour à la ligne pour que votre texte soit plus lisible… Pour cela, vous avez plusieurs solutions :
Donc, si nous reprenons notre code précédent et que nous appliquons cela, voici ce que ça donnerait :
Hello World !
My name is
Cysboy
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
lorsque vous utilisez le caractère d'échappement , quelle que soit la méthode appelée, celle-ci ajoute immédiatement un retour à la ligne à son
emplacement ;
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 ;
J'en profite au passage pour vous mentionner deux autres caractères d'échappement :
1. va insérer un retour chariot, parfois utilisé aussi pour les retours à la ligne ;
Vous avez sûrement remarqué que la chaîne de caractères que l'on a iche est entourée par des « " ». En Java, les guillemets doubles sont des délimiteurs
de chaînes de caractères ! Si vous voulez a icher un guillemet double dans la sortie standard, vous devrez « l'échapper » avec un « \ », ce qui donnerait :
"Coucou mon \"chou\" !" . Il n'est pas rare de croiser le terme anglais quote pour désigner les guillemets droits. Cela fait en quelque sorte partie du
jargon du programmeur.
Je vous propose maintenant de passer un peu de temps sur la compilation de vos programmes en ligne de commande. Cette partie n'est pas obligatoire, loin de
là, mais elle ne peut être qu'enrichissante.
Euh… quoi ?
Votre « variable d'environnement ». C'est grâce à elle que Windows trouve des exécutables sans qu'il soit nécessaire de lui spécifier le chemin d'accès complet.
Vous — enfin, Windows — en a plusieurs, mais nous ne 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 à un programme X dans votre variable d'environnement et que, par un malheureux hasard, vous n'avez plus
aucun raccourci vers X, 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 [Link] (en partant du principe que le nom de l'exécutable est « [Link] »</minicode>).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
D'accord, mais comment fait-on ? Et pourquoi doit-on faire ça pour le JDK ?
J'y arrive. Une fois votre JDK installé, ouvrez le répertoire bin de celui-ci, ainsi que celui de votre JRE. Nous allons nous attarder sur deux fichiers.
Dans le répertoire bin de votre JRE, vous devez avoir un fichier nommé [Link] , 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
[Link] (Java compiler). C'est celui-ci qui va précompiler vos programmes Java en byte code.
Alors, pourquoi mettre à jour la variable d'environnement pour le JDK ? Eh bien, compiler et 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 l'on ne met pas à jour la variable d'environnement de Windows, il nous faudrait :
Avec notre variable d'environnement mise à jour, nous n'aurons plus qu'à :
appeler la commande ;
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 voir en bas un bouton
nommé Variables d'environnement : cliquez dessus. Une nouvelle fenêtre s'ouvre. Dans la partie inférieure intitulée Variables système , cherchez la
variable Path . Une fois sélectionnée, cliquez sur Modifier . Encore une fois, une fenêtre, plus petite celle-ci, s'ouvre devant vous. Elle contient le nom de la
variable et sa valeur.
Ne changez pas son nom et n'e acez pas son contenu ! 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 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 .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;
Et maintenant :
%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;C:\Sun\SDK\jdk\bin;
Validez les changements : vous êtes maintenant prêts à compiler en ligne de commande.
Pour bien faire, allez dans le répertoire de votre premier programme et e acez le .class . Ensuite, faites Démarrer > Exécuter (ou encore touche Windows
+ R et tapez « cmd » .
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 nous trouvons, cd .. pour remonter d'un dossier dans la hiérarchie.
Par exemple, lorsque j'ouvre la console, je me trouve dans le dossier C: oto iti et mon application se trouve dans le dossier C:\sdz , je fais donc :
cd ..
cd ..
cd sdz
Après de la première instruction, je me retrouve dans le dossier C: oto . Grâce à la deuxième instruction, j'arrive à la racine de mon disque. Via la troisième
instruction, je me retrouve dans le dossier C:\sdz . Nous sommes maintenant dans le dossier contenant notre fichier Java ! Cela dit, nous pouvions condenser
cela en :
cd ../../sdz
Maintenant, vous pouvez créer votre fichier .class en exécutant la commande suivante :
javac <[Link]>
Si, dans votre dossier, vous avez un fichier [Link] , compilez-le en faisant :
javac [Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Et si vous n'avez aucun message d'erreur, vous pouvez vérifier que le fichier [Link] est présent en utilisant l'instruction dir qui liste le contenu d'un
répertoire. Cette étape franchie, vous pouvez lancer votre programme Java en faisant ce qui suit :
java <nomFichierClassSansExtension>
java test
Et normalement, le résultat de votre programme Java s'a iche sous vos yeux ébahis !
Attention : il ne faut pas mettre l'extension du fichier pour le lancer, mais il faut la mettre pour le compiler.
Voilà : vous avez compilé et exécuté un programme Java en ligne de commande… Vous avez pu voir qu'il n'y a rien de vraiment compliqué et, qui sait, vous en
aurez peut-être besoin un jour.
En résumé
La JVM est le cœur de Java.
Les fichiers contenant le code source de vos programmes Java ont l'extension .java .
Les fichiers précompilés correspondant à vos codes source Java ont l'extension .class .
Le byte code est un code intermédiaire entre celui de votre programme et celui que votre machine peut comprendre.
Un programme Java, codé sous Windows, peut être précompilé sous Mac et enfin exécuté sous Linux.
Votre machine NE PEUT PAS comprendre le byte code, elle a besoin de la JVM.
Tous les programmes Java sont composés d'au moins une classe.
Le point de départ de tout programme Java est la méthode public static void main(String[] args) .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link] , qui a iche un message sans saut de ligne.
Vous ne pourrez pas programmer sans variables. Il est donc indispensable que je vous les présente !
Petit rappel
Avant de commencer, je vous propose un petit rappel sur le fonctionnement d'un ordinateur et particulièrement sur la façon dont ce dernier interprète notre
façon de voir le monde…
Vous n'êtes pas sans savoir que votre ordinateur ne parle qu'une seule langue : le binaire ! Le langage binaire est une simple suite de 0 et de 1. Vous devez vous
rendre compte qu'il nous serait très di icile, en tant qu'êtres humains, d'écrire des programmes informatiques pour expliquer à nos ordinateurs ce qu'ils doivent
faire, entièrement en binaire… Vous imaginez ! Des millions de 0 et de 1 qui se suivent ! Non, ce n'était pas possible ! De ce fait, des langages de programmation
ont été créés afin que nous ayons à disposition des instructions claires pour créer nos programmes. Ces programmes sont ensuite compilés pour que nos
instructions humainement compréhensibles soient, après coup, compréhensible par votre machine.
Le langage binaire est donc une suite de 0 et de 1 qu'on appelle bit. Si vous êtes habitués à la manipulation de fichiers (audio, vidéos, etc.) vous devez savoir qu'il
existe plusieurs catégories de poids de programme (Ko, Mo, Go, etc.). Tous ces poids correspondent au système métrique informatique. Le tableau suivant
présente les poids les plus fréquemment rencontrés :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Raccourcis Traduction Correspondance
Si vous vous posez cette question c'est parce que vous ne savez pas encore compter comme un ordinateur et que vous êtes trop habitués à utiliser un système en
base 10. Je sais, c'est un peu confus… Pour comprendre pourquoi ce découpage est fait de la sorte, vous devez comprendre que votre façon de compter n'est pas
identique à celle de votre machine. En e et, vous avez l'habitude de compter ainsi :
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ... 254, 255, 256 ... 12345678, 12345679, 12345680
Cette façon de compter repose sur une base 10, car elle se décompose en utilisant des puissances de 10. Ainsi, le nombre 1024 peut se décomposer de cette façon
: 1*1000 + 0*100 + 2*10 + 4*1
Pour bien comprendre ce qui suit, vous devez aussi savoir que tout nombre élevé à la puissance 0 vaut 1, donc 10^0 = 1. Partant de ce postulat, nous pouvons
donc réécrire la décomposition du nombre 1024 ainsi : 1*10^3 + 0*10^2 + 2*10^1 + 4*10^0. Nous multiplions donc la base utilisée, ordonnée par puissance, par un
nombre compris entre 0 et cette base moins 1 (de 0 à 9).
Sauf que votre machine parle en binaire, elle compte donc en base 2. Cela revient donc à appliquer la décomposition précédente en remplaçant les 10 par des 2.
Par contre, vous n'aurez que deux multiplicateurs possibles : 0 ou 1 (et oui, vous êtes en base 2). De ce fait, en base 2, nous pourrions avoir ce genre de chose :
1*2^3 + 1*2^2 + 0*2^1 + 1*2^0, qui peut se traduire de la sorte : 1*8 + 1*4 + 0*2 + 1*1 donc 8 + 4 + 0 + 1 soit 13.
Donc, 1101 en base 2 s'écrit 13 en base 10. Et donc pourquoi des paquets de 1024 comme délimiteur de poids ? Car ce nombre correspond à une puissance de 2 :
1024 = 2^1^0.
Dans le monde de l'informatique, il existe une autre façon de compter très répandue : l'hexadécimal. Dans ce cas, nous comptons en base 16 :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1A, 1B, 1C.... 5E, 5F, 60, ... A53A, A53
B, A53C...
Le côté pratique de cette notation c'est qu'elle se base sur une subdivision d'un octet. Pour représenter un nombre de 0 à 15 (donc les seize premiers nombres), 4
bits sont nécessaires : 0*2^3 + 0*2^2 + 0*2^1 + 0*2^0 = 0 et 1*2^3 + 1*2^2 + 1*2^1 + 1*2^0 = 15. En fait, vous savez maintenant qu'un octet est un regroupement de
8 bits. Utiliser l'hexadécimal permet de simplifier la notation binaire car, si vous regroupez votre octet de bits en deux paquets de 4 bits, vous pouvez représenter
chaque paquet avec un caractère hexadécimal. Voici un exemple :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.
Cette opération se termine toujours par un point-virgule « ; » (comme toutes les instructions de ce langage). Ensuite, on l'initialise en entrant une valeur.
En Java, nous avons deux types de variables :
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, et vous allez voir qu'il y a plusieurs façons de déclarer certains de ces types.
byte temperature;
temperature = 64;
Le type short (2 octets) contient les entiers compris entre -32768 et +32767.
short vitesseMax;
vitesseMax = 32000;
Le type int (4 octets) va de -2*109 à 2*109 (2 et 9 zéros derrière… ce qui fait déjà un joli nombre).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
int temperatureSoleil;
temperatureSoleil = 15600000; //La température est exprimée en kelvins
Le type long (8 octets) peut aller de -9*10^18 à 9*10^18 (encore plus gros…).
long anneeLumiere;
anneeLumiere = 9460700000000000L;
Afin d'informer la JVM que le type utilisé est long, vous DEVEZ ajouter un "L" à la fin de votre nombre, sinon le compilateur essaiera d'allouer ce dernier
dans une taille d'espace mémoire de type entier et votre code ne compilera pas si votre nombre est trop grand...
Le type float (4 octets) est utilisé pour les nombres avec une virgule flottante.
float pi;
pi = 3.141592653f;
Ou encore :
float nombre;
nombre = 2.0f;
Vous remarquerez que nous ne mettons pas une virgule, mais un point ! Et vous remarquerez aussi que même si le nombre en question est rond, on écrit «
.0 » derrière celui-ci, le tout suivi de « f ».
Le type double (8 octets) est identique à float , si ce n'est qu'il contient plus de chi res derrière la virgule et qu'il n'a pas de su ixe.
double division;
division = 0.333333333333333333333333333333333333333333334d;
Ici encore, vous devez utiliser une lettre - le « d » - pour parfaire la déclaration de votre variable.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Des variables stockant un caractère
Le type char contient un caractère stocké entre apostrophes (« ' ' »), comme ceci :
char caractere;
caractere = 'A';
boolean question;
question = true;
Il s'agit d'une variable d'un type plus complexe que l'on appelle objet. Vous verrez que celle-ci s'utilise un peu di éremment des variables précédentes :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Attention : String commence par une majuscule ! Et lors de l'initialisation, on utilise des guillemets doubles (« " " »).
Cela a été mentionné plus haut : String n'est pas un type de variable, mais un objet. Notre variable est un objet, on parle aussi d'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 essayiez de la respecter au
maximum. Cette convention, la voici :
si le nom d'une variable est composé de plusieurs mots, le premier commence par une minuscule, le ou les autres par une majuscule, et ce, sans séparation ;
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 en parler à ce moment-là… Donc, à
présent, je vous demanderai de ne pas oublier ces règles !
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 défini par une classe qui s'appelle « String » ; c'est pourquoi String a une majuscule et
pas int , float , etc., qui eux ne sont pas définis par une classe.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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 !
Faites donc bien attention lors de vos déclarations de variables… Une petite astuce quand même (enfin deux, plutôt) : on peut très bien compacter les phases de
déclaration et d'initialisation en une seule phase ! Comme ceci :
Et lorsque nous avons plusieurs variables d'un même type, nous pouvons résumer tout ceci à une déclaration :
Ici, toutes les variables sont des entiers, et toutes sont initialisées.
Avant de nous lancer dans la programmation, nous allons faire un peu de mathématiques avec nos variables.
« + » : permet d'additionner 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 diviser deux variables numériques (mais je crois que vous aviez deviné).
« % » : permet de renvoyer le reste de la division entière de deux variables de type numérique ; cet opérateur s'appelle le modulo.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Quelques exemples de calcul
Ici, nous voyons bien que nous pouvons a ecter des résultats d'opérations sur des nombres à nos variables, mais aussi a ecter des résultats d'opérations sur des
variables de même type.
Je me doute bien que le modulo est assez di icile à assimiler. Voici une utilisation assez simple : pour vérifier qu'un entier est pair, il su it de vérifier que
son modulo 2 renvoie 0.
Maintenant, voici quelque chose que les personnes qui n'ont jamais programmé ont du mal à intégrer. Je garde la même déclaration de variables que ci-dessus.
Et là aussi, il existe une syntaxe qui raccourcit l'écriture de ce genre d'opérations. Regardez bien :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
nbre1 = nbre1 + 1;
nbre1 += 1;
nbre1++;
++nbre1;
Les trois premières syntaxes correspondent exactement à la même opération. La troisième sera certainement celle que vous utiliserez le plus, mais elle ne
fonctionne que pour augmenter d'une unité la valeur de nbre1 ! Si vous voulez augmenter de 2 la valeur d'une variable, utilisez les deux syntaxes précédentes.
On appelle cela l'incrémentation. La dernière fait la même chose que la troisième, mais il y a une subtilité dont nous reparlerons dans le chapitre sur les boucles.
Pour la soustraction, la syntaxe est identique :
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 fonctionnent de la même manière ; regardez plutôt :
nbre1 = nbre1 * 2;
nbre1 *= 2;
nbre1 = nbre1 / 2;
nbre1 /= 2;
Très important : on ne peut faire du 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 ! Ceci est valable pour tous les opérateurs arithmétiques et pour tous les types de
variables numériques. Essayez de garder une certaine rigueur pour vos calculs arithmétiques.
Voici les raisons de ma mise en garde : comme je vous l'ai dit précédemment, chaque type de variable a une capacité di érente et, pour faire simple, nous allons
comparer nos variables à di érents récipients. Une variable de type :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
double serait un baril. Pfiou, on en met là-dedans
À partir de là, ce n'est plus qu'une question de bon sens. Vous devez facilement constater qu'il est possible de mettre le contenu d'un dé à coudre dans un verre ou
un baril. Par contre, si vous versez le contenu d'un baril dans un verre… il y en a plein par terre !
Ainsi, si nous a ectons 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 ne sera
donc pas un réel mais un entier.
Pour a icher le contenu d'une variable dans la console, appelez l'instruction [Link](maVariable); , ou encore
[Link](maVariable); .
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 d'opérateur de
concaténation, c'est-à-dire qu'il permet de mélanger du texte brut et des variables. Voici un exemple d'a ichage avec une perte de précision :
Sachez aussi que vous pouvez tout à fait mettre des opérations dans un a ichage, comme ceci : [Link]("Résultat = " + nbre1/nbre2);
(le « + » joue ici le rôle d'opérateur de concaténation) ; ceci vous permet d'économiser une variable et par conséquent de la mémoire.
Cependant, pour le bien de ce chapitre, nous n'allons pas utiliser cette méthode. Vous allez constater que le résultat a iché est 3 au lieu de 3.33333333333333…
Et je pense que ceci vous intrigue :
.
Avant que je ne vous explique, remplacez la ligne citée précédemment par :
.
Vous allez voir qu'Eclipse n'aime pas du tout ! Pour comprendre cela, nous allons voir les conversions.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les conversions, ou « cast »
Comme expliqué précédemment, 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.
D'un type int en type float :
int i = 123;
float j = (float)i;
int i = 123;
double j = (double)i;
Et inversement :
double i = 1.23;
double j = 2.9999999;
int k = (int)i; //k vaut 1
k = (int)j; //k vaut 2
Pour en revenir à notre problème de tout à l’heure, il est aussi possible de caster le résultat d'une opération mathématique en la mettant entre « ( ) » et en la
précédant du type de cast souhaité. Donc :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Voilà qui fonctionne parfaitement. Pour bien faire, vous devriez mettre le résultat de l'opération en type double . Et si on fait l'inverse : si nous déclarons deux
entiers et que nous mettons le résultat dans un double ? Voici une possibilité :
Vous aurez « 1 » comme résultat. Je ne caste pas ici, car un double peut contenir un int .
Idem… Afin de comprendre pourquoi, 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'a ectation, le calcul, le cast, le test, l'incrémentation… toutes ces choses sont des opérations ! Et Java les fait dans un certain ordre, il y a des
priorités.
un calcul ;
Eh bien, Java exécute notre ligne dans cet ordre ! Il fait le calcul (ici 3/2), il caste le résultat en double , puis il l'a ecte dans notre variable resultat .
Vous vous demandez sûrement pourquoi vous n'avez pas 1.5… C'est simple : lors de la première opération de Java, la JVM voit un cast à e ectuer, 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 a ecte 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 :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
int nbre1 = 3, nbre2 = 2;
double resultat = (double)(nbre1) / (double)(nbre2);
[Link]("Le résultat est = " + resultat);
//affiche : Le résultat est = 1.5
Je ne vais pas trop détailler ce qui suit (vous verrez cela plus en détail dans la partie sur la programmation orientée objet) ; mais vous allez maintenant apprendre
à transformer l'argument d'un type donné, int par exemple, en String .
int i = 12;
String j = new String();
j = [Link](i);
j est donc une variable de type String contenant la chaîne de caractères 12 . Sachez que 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.
int i = 12;
String j = new String();
j = [Link](i);
int k = [Link](j).intValue();
Il existe des équivalents à intValue() pour les autres types numériques : floatValue() , doubleValue() …
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
double nombre = 1000000000000;
//Peut s'écrire ainsi
double nombre = 1____000____000___000_000;
//Le nombre d'underscore n'a pas d'importance, vous pouvez en mettre autant que vous le souhaitez
Les underscore doivent être placés entre deux caractères numériques : ils ne peuvent donc pas être utilisés en début ou en fin de déclaration ni avant ou après un
séparateur de décimal. Ainsi, ces déclarations ne sont pas valides :
double d = 123_.159;
int entier = _123;
int entier2 = 123_;
Avant Java 7, il était possible de déclarer des expressions numériques en hexadécimal, en utilisant le préfixe « 0x » :
Depuis java 7, vous avez aussi la possibilité d'utiliser la notation binaire, en utilisant le préfixe « 0b » :
Certains programmes Java travaillent directement sur les bits, il peut donc être plus pratique de les représenter ainsi avant de les manipuler.
On a ecte une valeur dans une variable avec l'opérateur égal (« = »).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Après avoir a ecté une valeur à une variable, l'instruction doit se terminer par un point-virgule (« ; »).
Vos noms de variables ne doivent contenir ni caractères accentués ni espaces et doivent, dans la mesure du possible, respecter la convention de nommage
Java.
Lorsque vous e ectuez des opérations sur des variables, prenez garde à leur type : vous pourriez perdre en précision.
Vous pouvez caster un résultat en ajoutant un type devant celui-ci : (int) , (double) , etc.
Prenez garde aux priorités lorsque vous castez le résultat d'opérations, faute de quoi ce dernier risque d'être incorrect.
La classe 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 présente des failles. Je vous fais confiance pour ne pas rentrer n'importe quoi n'importe quand !
Je vous ai dit que vos variables de type String sont en réalité 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 . Cet objet peut prendre di érents paramètres, mais ici nous n'en utiliserons qu'un : celui qui correspond à l'entrée
standard en Java. Lorsque vous faites [Link](); , je vous rappelle que vous appliquez la méthode println() sur la sortie standard ; ici, nous
allons utiliser l'entrée standard [Link] . Donc, avant d'indiquer à Java qu'il faut lire ce que nous allons taper au clavier, nous devrons instancier un objet
Scanner . Avant de vous expliquer ceci, créez une nouvelle classe et tapez cette ligne de code dans votre méthode main :
Vous devez avoir une jolie vague rouge sous le mot Scanner . Cliquez sur la croix rouge sur la gauche et faites un double-clic sur
Import 'Scanner' [Link] (figure suivante). Et là, l'erreur disparaît !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Importer la classe Scanner
Maintenant, regardez au-dessus de la déclaration de votre classe, vous devriez voir cette ligne :
import [Link];
Voilà ce que nous avons fait. Je vous ai dit qu'il fallait indiquer à 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 [Link] .
Un package est un ensemble de classes. En fait, c'est un ensemble de dossiers et de sous-dossiers contenant une ou plusieurs classes, mais nous verrons
ceci plus en détail lorsque nous ferons nos propres packages.
Les classes qui se trouvent dans les packages autres que [Link] (package automatiquement importé par Java, on y trouve entre autres la classe System )
sont à importer à la main dans vos classes Java pour pouvoir vous en servir. La façon dont nous avons importé la classe [Link] dans Eclipse est
très commode. Vous pouvez aussi le faire manuellement :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.
Une fois l'application lancée, le message que vous avez écrit auparavant s'a iche dans la console, en bas d'Eclipse. Pensez à cliquer dans la console afin que ce
que vous saisissez y soit écrit et que Java puisse récupérer ce que vous avez inscrit (figure suivante) !
Si vous remplacez la ligne de code qui récupère une chaîne de caractères comme suit :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Scanner sc = new Scanner([Link]);
[Link]("Veuillez saisir un nombre :");
int str = [Link]();
[Link]("Vous avez saisi le nombre : " + str);
… vous devriez constater que lorsque vous introduisez votre variable de type Scanner et que vous introduisez le point permettant d'appeler des méthodes de
l'objet, Eclipse vous propose une liste de méthodes associées à cet objet (ceci s'appelle l'autocomplétion) ; de plus, 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 : vous verrez qu'il fonctionne à la perfection. Sauf… si vous saisissez autre chose qu'un nombre entier !
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 un type de variable, il vous su it
d'appeler next<Type de variable commençant par une majuscule> (rappelez-vous de la convention de nommage Java).
Il y a un type de variables primitives qui n'est pas pris en compte par la classe Scanner : il s'agit du type char .
Qu'avons-nous fait ici ? Nous avons récupéré une chaîne de caractères, puis 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.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous devez vous demander pourquoi charAt(0) et non charAt(1) : nous aborderons ce point lorsque nous verrons les tableaux… Jusqu'à ce qu'on aborde
les exceptions, je vous demanderai d'être rigoureux et de faire attention à ce que vous attendez comme type de données afin d'utiliser la méthode
correspondante.
Une précision s'impose, toutefois : la méthode nextLine() récupère le contenu de toute la ligne saisie et replace la « tête de lecture » au début d'une autre
ligne. Par contre, si vous avez invoqué une méthode comme nextInt() , nextDouble() et que vous invoquez directement après la méthode nextLine() ,
celle-ci ne vous invitera pas à saisir une chaîne de caractères : elle videra la ligne commencée par les autres instructions. En e et, celles-ci ne repositionnent pas la
tête de lecture, l'instruction nextLine() le fait à leur place. Pour faire simple, ceci :
import [Link];
… ne vous demandera pas de saisir une chaîne et a ichera directement « Fin ». Pour pallier ce problème, il su it de vider la ligne après les instructions ne le
faisant pas automatiquement :
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
String str = [Link]();
[Link]("FIN ! ");
}
}
En résumé
La lecture des entrées clavier se fait via l'objet Scanner .
Pour pouvoir récupérer ce vous allez taper dans la console, vous devrez initialiser l'objet Scanner avec l'entrée standard, [Link] .
Il y a une méthode de récupération de données pour chaque type (sauf les char ) : nextLine() pour les String , nextInt() pour les int ...
Les conditions
Nous abordons ici l'un des chapitres les plus importants : les conditions sont une autre notion fondamentale de la programmation. En e et, ce qui va être
développé ici s'applique à énormément de langages de programmation, et pas seulement à Java.
Dans une classe, la lecture et l'exécution se font de façon séquentielle, c'est-à-dire ligne par ligne. Avec les conditions, nous allons pouvoir gérer di érents
cas de figure sans pour autant lire tout le code. Vous vous rendrez vite compte que tous vos projets ne sont que des enchaînements et des imbrications de
conditions et de boucles (notion que l'on abordera au chapitre suivant).
Assez de belles paroles ! Entrons tout de suite dans le vif du sujet.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
« < » : strictement inférieur.
« ? : » : l'opérateur ternaire. Pour celui-ci, vous comprendrez mieux avec un exemple qui sera donné vers la fin de ce chapitre.
Comme je vous l'ai dit dans le chapitre précédent, les opérations en Java sont soumises à des priorités. Tous ces opérateurs se plient à cette règle, de la même
manière que les opérateurs arithmétiques…
Imaginons un programme qui demande à un utilisateur d'entrer un nombre entier relatif (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 :
if(//condition)
{
//Exécution des instructions si la condition est remplie
}
else
{
//Exécution des instructions si la condition n'est pas remplie
}
Le résultat de l'expression évaluée par l'instruction if sera un boolean , donc soit true , soit false . La portion de code du bloc if ne sera exécutée que si
la condition est remplie. Dans le cas contraire, c'est le bloc de l'instruction else qui le sera. Mettons notre petit exemple en pratique :
int i = 10;
if (i < 0)
[Link]("le nombre est négatif");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
else
[Link]("le nombre est positif");
Essayez ce petit code, et vous verrez comment il fonctionne. Dans ce cas, notre classe a iche « le nombre est positif ». Expliquons un peu ce qui se passe.
Dans un premier temps, la condition du if est testée : elle dit « si i est strictement inférieur à 0 alors fais ça ».
Dans un second temps, vu que la condition précédente est fausse, le programme exécute le else .
Attends un peu ! Lorsque tu nous as présenté la structure des conditions, tu as mis des accolades et là, tu n'en mets pas. Pourquoi ?
Bien observé. En fait, les accolades sont présentes dans la structure « normale » des conditions, mais lorsque le code à l'intérieur de l'une d'entre elles n'est
composé que d'une seule ligne, les accolades deviennent facultatives.
Comme nous avons l'esprit perfectionniste, nous voulons que notre programme a iche « le nombre est nul » lorsque i est égal à 0 ; nous allons donc ajouter 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 ou strictement supérieur à 0. Il nous su it d'ajouter une condition à l'intérieur de la
clause else , comme ceci :
int i = 0;
if (i < 0)
{
[Link]("Ce nombre est négatif !");
}
else
{
if(i == 0)
[Link]("Ce nombre est nul !");
else
[Link]("Ce nombre est positif !");
Maintenant que vous avez tout compris, je vais vous présenter une autre façon d'écrire ce code, avec le même résultat : on ajoute juste un petit « sinon si… ».
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
int i = 0;
if (i < 0)
[Link]("Ce nombre est négatif !");
else
[Link]("Ce nombre est nul !");
sinon si i est strictement positif alors le code de cette condition est exécuté.
sinon i est forcément nul alors le code de cette condition est exécuté.
Ici, je vais très fortement insister sur un point : regardez l'a ichage du code et remarquez le petit décalage entre le test et le code à exécuter. On appelle cela
l'indentation !
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. Il sera di icile de distinguer les tests du code. Vous n'êtes pas obligés de le faire, mais je vous assure que vous y viendrez.
Avant de passer à la suite, vous devez savoir qu'on ne peut pas tester l'égalité de chaînes de caractères ! Du moins, pas comme je vous l'ai montré ci-
dessus. Nous aborderons ce point plus tard.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
int i = 58;
if(i < 100 && i > 50)
[Link]("Le nombre est bien dans l'intervalle.");
else
[Link]("Le nombre n'est pas dans l'intervalle.");
Nous avons utilisé l'opérateur && . La condition de notre if est devenue : « si i est inférieur à 100 ET supérieur à 50 ».
Avec l'opérateur « && », la clause est remplie si et seulement si les conditions la constituant sont toutes remplies ; si l'une des conditions n'est pas vérifiée,
la clause sera considérée comme fausse.
Cet opérateur vous initie à la notion d'intersection d'ensembles. Ici, nous avons deux conditions qui définissent un ensemble chacune :
L'opérateur « && » permet de faire l'intersection de ces ensembles. La condition regroupe donc les nombres qui appartiennent à ces deux ensembles, c’est-à-dire
les nombres de 51 à 99 inclus. Réfléchissez bien à l'intervalle que vous voulez définir. Voyez ce code :
int i = 58;
if(i < 100 && i > 100)
[Link]("Le nombre est bien dans l'intervalle.");
else
[Link]("Le nombre n'est pas dans l'intervalle.");
Ici, la condition ne sera jamais remplie, car je ne connais aucun nombre qui soit à la fois plus petit et plus grand que 100 ! Reprenez le code précédent et
remplacez l'opérateur « && » par « || » (petit rappel, il s'agit du OU ). À l'exécution du programme et après plusieurs tests de valeur pour i , vous pourrez vous
apercevoir que tous les nombres remplissent cette condition, sauf 100.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
La structure switch
Le switch est surtout utilisé lorsque nous voulons des conditions « à la carte ». Prenons l'exemple d'une interrogation comportant deux questions : pour
chacune d'elles, on peut obtenir uniquement 0 ou 10 points, ce qui nous donne au final trois notes et donc trois appréciations possibles, comme ceci :
20/20 : bravo !
Dans ce genre de cas, on utilise un switch pour éviter des else if à répétition et pour alléger un peu le code. Je vais vous montrer comment se construit une
instruction switch ; puis nous allons l'utiliser tout de suite après.
Syntaxe
switch (/*Variable*/)
{
case /*Argument*/:
/*Action*/;
break;
default:
/*Action*/;
}
Si la première languette ( case /*Valeur possible de la variable*/: ) correspond à la valeur de /*Variable*/ , l'instruction figurant dans celle-ci
sera exécutée.
Si aucun des cas ne correspond, 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; . Elle permet de sortir du switch si une languette correspond. Pour mieux juger de l'utilité de cette instruction,
enlevez tous les break; et compilez votre programme. Vous verrez le résultat… Voici un exemple de switch que vous pouvez essayer :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
int note = 10; //On imagine que la note maximale est 20
switch (note)
{
case 0:
[Link]("Ouch !");
break;
case 10:
[Link]("Vous avez juste la moyenne.");
break;
case 20:
[Link]("Parfait !");
break;
default:
[Link]("Il faut davantage travailler.");
}
Je n'ai écrit qu'une ligne de code par instruction case , mais rien ne vous empêche d'en mettre plusieurs.
Si vous avez essayé ce programme en supprimant l'instruction break; , vous avez dû vous rendre compte que le switch exécute le code contenu dans le
case 10: , 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 nous verrons une autre utilité à break; dans le chapitre suivant.
Depuis la version 7 de Java, l'instruction switch accepte les objets de type String en paramètre. De ce fait, cette instruction est donc valide :
switch(chaine) {
case "Bonjour":
[Link]("Bonjour monsieur !");
break;
case "Bonsoir":
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("Bonsoir monsieur !");
break;
default:
[Link]("Bonjoir ! :p");
}
La condition ternaire
Les conditions ternaires sont assez complexes et relativement peu utilisées. Je vous les présente ici à titre indicatif. La particularité de ces conditions réside dans
le fait que trois opérandes (c'est-à-dire des variables ou des constantes) sont mis en jeu, mais aussi que ces conditions sont employées pour a ecter des données
à une variable. Voici à quoi ressemble la structure de ce type de condition :
Nous cherchons à a ecter une valeur à notre variable max , mais de l'autre côté de l'opérateur d'a ectation se trouve une condition ternaire…
Ce qui se trouve entre les parenthèses est évalué : x est-il plus petit que y ? Donc, deux cas de figure se profilent à l'horizon :
si la condition renvoie true (vrai), qu'elle est vérifiée, la valeur qui se trouve après le ? sera a ectée ;
L'a ectation est e ective : vous pouvez utiliser votre variable max .
Vous pouvez également faire des calculs (par exemple) avant d'a ecter les valeurs :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
N'oubliez pas que la valeur que vous allez a ecter à votre variable doit être du même type que votre variable. Sachez aussi que rien ne vous empêche d'insérer
une condition ternaire dans une autre condition ternaire :
la structure ? : .
Si un bloc d'instructions contient plus d'une ligne, vous devez l'entourer d'accolades afin de bien en délimiter le début et la fin.
Pour pouvoir mettre une condition en place, vous devez comparer des variables à l'aide d'opérateurs logiques.
Vous pouvez mettre autant de comparaisons renvoyant un boolean que vous le souhaitez dans une condition.
Pour la structure switch , pensez à mettre les instructions break; si vous ne souhaitez exécuter qu'un seul bloc case .
Les boucles
Le rôle des boucles est de répéter un certain nombre de fois les mêmes opérations. Tous les programmes, ou presque, ont besoin de ce type de
fonctionnalité. Nous utiliserons les boucles pour permettre à un programme de recommencer depuis le début, pour attendre une action précise de
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
l'utilisateur, parcourir une série de données, etc.
Une boucle s'exécute tant qu'une condition est remplie. Nous réutiliserons donc des notions du chapitre précédent !
La boucle while
Pour décortiquer précisément ce qui se passe dans une boucle, nous allons voir comment elle se construit ! Une boucle commence par une déclaration : ici
while . Cela veut dire, à peu de chose près, « tant que ». Puis nous avons une condition : c'est elle qui permet à la boucle de s'arrêter. Une boucle n'est utile que
lorsque 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. Ensuite nous avons
une ou plusieurs instructions : c'est ce que va répéter notre boucle (il peut même y avoir des boucles dans une boucle !
Nous allons travailler sur un exemple concret mais d'abord, réfléchissons à « comment notre boucle va travailler ». Pour cela, il faut déterminer notre exemple.
Nous allons a icher « Bonjour, <un prénom> », prénom qu'il faudra taper au clavier ; puis nous demanderons si l'on veut recommencer. Pour cela, il nous faut une
variable qui va recevoir le prénom, donc dont le type sera String , ainsi qu'une variable pour récupérer la réponse. Et là, plusieurs choix s'o rent à nous : soit un
caractère, soit une chaîne de caractères, soit un entier. Ici, nous prendrons une variable de type char . C'est parti !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On affiche notre phrase avec le prénom
[Link]("Bonjour " +prenom+ ", comment vas-tu ?");
//On demande si la personne veut faire un autre essai
[Link]("Voulez-vous réessayer ? (O/N)");
//On récupère la réponse de l'utilisateur
reponse = [Link]().charAt(0);
}
[Link]("Au revoir…");
//Fin de la boucle
Vous avez dû cligner des yeux en lisant reponse = [Link]().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 qu'il se passe. Dans un premier temps, nous avons déclaré et initialisé nos variables. Ensuite, la boucle évalue la condition qui nous dit : tant
que la variable reponse contient « O », on exécute la boucle. Celle-ci contient bien le caractère « O », donc nous entrons dans la boucle. Puis l'exécution des
instructions suivant l'ordre dans lequel elles apparaissent dans la boucle a lieu. À la fin, c'est-à-dire à l'accolade fermante de la boucle, le compilateur nous
ramène 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 s'exécute. Si
nous ne l'avions pas fait, nous n'y serions jamais entrés. Normal, puisque nous testons la condition avant d'entrer dans la boucle !
Voilà. C'est pas mal, mais il faudrait forcer l'utilisateur à ne taper que « O » ou « N ». Comment faire ? C'est très simple : avec une boucle ! Il su it de forcer
l'utilisateur à entrer soit « N » soit « O » avec un while ! Attention, il nous faudra réinitialiser la variable reponse à « ' ' » (caractère vide). Il faudra donc
répéter la phase « Voulez-vous réessayer ? » tant que la réponse donnée n'est pas « O » ou « N ».
Voici notre programme dans son intégralité :
String prenom;
char reponse = 'O';
Scanner sc = new Scanner([Link]);
while (reponse == 'O')
{
[Link]("Donnez un prénom : ");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
prenom = [Link]();
[Link]("Bonjour " +prenom+ ", comment vas-tu ?");
//Sans ça, nous n'entrerions pas dans la deuxième boucle
reponse = ' ';
Vous pouvez tester ce code (c'est d'ailleurs vivement conseillé) : vous verrez que si vous n'entrez pas la bonne lettre, le programme vous posera sans cesse sa
question, comme à la figure suivante !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les instructions dans la boucle se répètent
Attention à écrire correctement 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 d'exécuter le programme précédent sans la réinitialisation de la variable reponse , et vous verrez le résultat ! On n'entre jamais dans la deuxième
boucle, car reponse = 'O' (puisque initialisée ainsi au début du programme). Là, vous ne pourrez jamais changer sa valeur… Le programme ne s'arrêtera donc
jamais ! On appelle ça une « boucle infinie ». En voici un autre exemple.
int a = 1, b = 15;
while (a < b)
{
[Link]("coucou " +a+ " fois !!");
}
Si vous lancez ce programme, vous allez voir une quantité astronomique de « coucou 1 fois !! ». Nous aurions dû ajouter une instruction dans le bloc d'instructions
de notre while pour changer la valeur de a à chaque tour de boucle, comme ceci :
int a = 1, b = 15;
while (a < b)
{
[Link]("coucou " +a+ " fois !!");
a++;
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Correction de la boucle infinie
Une petite astuce : lorsque vous n'avez qu'une instruction dans votre boucle, vous pouvez enlever les accolades, car elles deviennent superflues, tout
comme pour les instructions if , else if ou else .
int a = 1, b = 15;
while (a++ < b)
[Link]("coucou " +a+ " fois !!");
Souvenez-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, essayez ce code :
int a = 1, b = 15;
while (++a < b)
[Link]("coucou " +a+ " fois !!");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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
d'égalité), c'est-à-dire que la boucle incrémente la variable a , et ce n'est qu'après l'avoir fait qu'elle teste la condition !
do{
//Instructions
}while(a < b);
Première di érence
La boucle do… while s'exécutera au moins une fois, contrairement à sa sœur. 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 di érence
C'est une di érence de syntaxe, qui se situe après la condition du while . Vous voyez la di érence ? Oui ? Non ? Il y a un « ; » après le while . C'est tout ! Ne
l'oubliez cependant 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, refaisons notre programme précédent avec une boucle
do… while .
do{
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("Donnez un prénom : ");
prenom = [Link]();
[Link]("Bonjour " +prenom+ ", comment vas-tu ?");
do{
[Link]("Voulez-vous réessayer ? (O/N)");
reponse = [Link]().charAt(0);
}while(reponse != 'O' && reponse != 'N');
[Link]("Au revoir…");
Vous voyez donc que ce code ressemble beaucoup à celui utilisé avec la boucle while , mais il comporte une petite subtilité : ici, plus besoin de réinitialiser la
variable reponse , puisque de toute manière, la boucle s'exécutera au moins une fois !
La boucle for
Cette boucle est un peu particulière puisqu'elle prend tous ses attributs dans sa condition et agit en conséquence. Je m'explique : jusqu'ici, nous avions fait des
boucles avec :
Eh bien on met tout ça dans la condition de la boucle for , et c'est tout. Il existe une autre syntaxe pour la boucle for depuis le JDK 1.5. Nous la verrons lorsque
nous aborderons les tableaux. Mais je sais bien qu'un long discours ne vaut pas un exemple, alors voici une boucle for sous vos yeux ébahis :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("Voici la ligne "+i);
}
Vous aurez sûrement remarqué la présence des « ; » dans la condition pour la séparation des champs. Ne les oubliez surtout pas, sinon le programme ne
compilera pas.
Nous pouvons aussi inverser le sens de la boucle, c'est-à-dire qu'au lieu de partir de 0 pour aller à 10, nous allons commencer à 10 pour atteindre 0 :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Boucle for avec décrémentation
Pour simplifier, la boucle for est un peu le condensé d'une boucle while dont le nombre de tours se détermine via un incrément. Nous avons un nombre de
départ, une condition qui doit être remplie pour exécuter une nouvelle fois la boucle et une instruction de fin de boucle qui incrémente notre nombre de départ.
Remarquez que rien ne nous empêche de cumuler les déclarations, les conditions et les instructions de fin de boucle :
Ici, cette boucle n'e ectuera que deux tours puisque la condition (i < 10 && j < 6) est remplie dès le deuxième tour, la variable j commençant à 2 et étant
incrémentée de deux à chaque tour de boucle.
Les boucles vous permettent simplement d'e ectuer des tâches répétitives.
la boucle while(condition){…} évalue la condition puis exécute éventuellement un tour de boucle (ou plus) ;
la boucle do{…}while(condition); fonctionne exactement comme la précédente, mais e ectue un tour de boucle quoi qu'il arrive ;
la boucle for permet d'initialiser un compteur, une condition et un incrément dans sa déclaration afin de répéter un morceau de code un nombre limité
de fois.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Tout comme les conditions, si une boucle contient plus d'une ligne de code à exécuter, vous devez l'entourer d'accolades afin de bien en délimiter le début et
la fin.
les variables ;
les conditions ;
les boucles ;
Accrochez-vous, car je vais vous demander de penser à des tonnes de choses, et vous serez tout seuls. Lâchés dans la nature… Mais non je plaisante, je vais
vous guider un peu. ;)
Élaboration
Voici les caractéristiques du programme que nous allons devoir réaliser :
le programme demande quelle conversion nous souhaitons e ectuer, Celsius vers Fahrenheit ou l'inverse ;
on n'autorise que les modes de conversion définis dans le programme (un simple contrôle sur la saisie fera l'a aire) ;
enfin, on demande à la fin à l'utilisateur s'il veut faire une nouvelle conversion, ce qui signifie que l'on doit pouvoir revenir au début du programme !
Avant de vous lancer dans la programmation à proprement parler, je vous conseille fortement de réfléchir à votre code… sur papier. Réfléchissez à 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.
À toutes fins utiles, voici la formule de conversion pour passer des degrés Celsius en degrés Fahrenheit : F = \frac{9}{5} imes C + 32 ; pour l'opération inverse, c'est
comme ceci : C = \frac{(F - 32) imes 5 }{9}.
La figure suivante est un aperçu de ce que je vous demande.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Rendu du TP
Je vais également vous donner une fonction toute faite qui vous permettra éventuellement d'arrondir vos résultats. Je vous expliquerai le fonctionnement des
fonctions dans deux chapitres. Vous pouvez très bien ne pas vous en servir. Pour ceux qui souhaitent tout de même l'utiliser, la voici :
Elle est à placer entre les deux accolades fermantes de votre classe, comme à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Emplacement de la fonction
Voici comment utiliser cette fonction : imaginez que vous avez la variable faren à arrondir, et que le résultat obtenu est enregistré dans une variable
arrondFaren ; vous procéderez comme suit :
Quelques dernières recommandations : essayez de bien indenter votre code ! Prenez votre temps. Essayez de penser à tous les cas de figure. Maintenant à vos
papiers, crayons, neurones, claviers… et bon courage !
Correction
STOP ! C'est fini ! Il est temps de passer à 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
vidés. Mais vous allez voir qu'en définitive, ce TP n'était pas si compliqué que ça.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Surtout, n'allez pas croire que ma correction est parole d'évangile. Il y avait di érentes manières d'obtenir le même résultat. Voici tout de même une des solutions
possibles.
import [Link];
class Sdz1 {
public static void main(String[] args) {
//Notre objet Scanner
Scanner sc = new Scanner([Link]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Pensez à vider la ligne lue
[Link]();
}while(reponse == 'O');
//Fin de programme
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Explications concernant ce code
Tout programme commence par une phase de déclaration des variables.
Ensuite, vous voyez deux do{ consécutifs correspondant à deux conditions à vérifier :
Nous a ichons les renseignements à l'écran, et récupérons la température à convertir pour la stocker dans une variable.
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 !
Les tableaux
Comme tout langage de programmation qui se respecte, Java travaille avec des tableaux. Vous verrez que ceux-ci s'avèrent bien pratiques.
Vous vous doutez (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 en e et pouvoir lui a ecter plusieurs valeurs ordonnées séquentiellement que
nous pourrons appeler au moyen d'un indice (ou d'un compteur, si vous préférez). Il nous su ira d'introduire l'emplacement du contenu désiré dans notre
variable tableau pour la sortir, travailler avec, l'a icher…
Assez bavardé : mettons-nous joyeusement au travail !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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 ! C'est très logique : comme nous l'avons vu auparavant, une variable d'un type donné ne peut contenir que des éléments de ce type : une variable
de type int ne peut pas recevoir une chaîne de caractères. Il en va de même pour les tableaux. Voyons tout de suite comment ils se déclarent :
La déclaration ressemble beaucoup à celle d'une variable quelconque, si ce n'est la présence de crochets « [ ] » après le nom de notre tableau et d'accolades « { } »
encadrant l'initialisation de celui-ci. Dans la pratique, ça nous donnerait quelque chose comme ceci :
Vous remarquez bien que la déclaration et l'initialisation d'un tableau se font comme avec une variable ordinaire : il faut utiliser des « ' ' » pour initialiser un
tableau de caractères, des « " " » pour initialiser un tableau de String , etc. Vous pouvez aussi déclarer un tableau vide, mais celui-ci devra impérativement
contenir un nombre de cases bien défini. Par exemple, si vous voulez un tableau vide de six entiers :
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 nous pencher sur une
belle variante de ces tableaux…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Je ne vais pas vous faire de grand laïus sur ce type de tableau, puisque je pense sincèrement qu'un exemple vous en 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 :
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 est
composé de plusieurs tableaux. 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.
Maintenant, je vais vous proposer de vous amuser un peu avec les tableaux…
[Link](tableauCaractere[0]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Cela implique qu'un tableau contenant 4 éléments aura comme indices possibles 0, 1, 2 ou 3. Le 0 correspond au premier élément, le 1 correspond au 2e élément,
le 2 correspond au 3e élément et le 3 correspond au 4e élément.
Une très grande partie des erreurs sur les tableaux sont dues à un mauvais indice dans celui-ci. Donc prenez garde !
Ce que je vous propose, c'est tout simplement d'a icher un des tableaux présentés ci-dessus dans son intégralité. Sachez qu'il existe une instruction qui retourne
la taille d'un tableau : grâce à elle, nous pourrons arrêter notre boucle (car oui, nous allons utiliser une boucle). Il s'agit de l'instruction <mon tableau>.length .
Notre boucle for pourrait donc ressembler à ceci :
Cela a ichera :
Maintenant, nous allons essayer de faire une recherche dans un de ces tableaux. En gros, il va falloir e ectuer une saisie clavier et regarder si celle-ci est présente
dans le tableau… Gardez la partie de code permettant de faire plusieurs fois la même action ; 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 :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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;
[Link]("Rentrez une lettre en minuscule, SVP ");
carac = [Link]().charAt(0);
//Boucle de recherche dans le tableau
while(i < [Link] && carac != tableauCaractere[i])
i++;
//Si i < 7 c'est que la boucle n'a pas dépassé le nombre de cases du tableau
if (i < [Link])
[Link](" La lettre " +carac+ " se trouve bien dans le tableau !");
else //Sinon
[Link](" La lettre " +carac+ " ne se trouve pas dans le tableau !");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Résultat de la recherche
La première correspond au compteur : tant que celui-ci est inférieur ou égal au nombre d'éléments du tableau, on l'incrémente pour regarder la valeur suivante.
Nous passons ainsi en revue tout ce qui se trouve dans notre tableau. 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 à elle, si le caractère saisi se trouve dans le tableau, la
boucle prend fin, et donc i a une valeur inférieure à 7.
À ce stade, notre recherche est terminée. Après cela, les conditions coulent de source ! Si nous avons trouvé une correspondance entre le caractère saisi et notre
tableau, i prendra une valeur inférieure à 7 (vu qu'il y a 7 éléments dans notre tableau, l'indice maximum étant 7-1, soit 6). Dans ce cas, nous a ichons un
message confirmant la présence de l’élément recherché. Dans le cas contraire, c'est l'instruction du else qui s'exécutera.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous avez dû remarquer la présence d'un i = 0; dans une boucle. Ceci est primordial, sinon, lorsque vous reviendrez au début de celle-ci, i ne vaudra
plus 0, mais la dernière valeur à laquelle il aura été a ecté après les di érentes incrémentations. Si vous faites une nouvelle recherche, vous commencerez
par l'indice contenu dans i ; ce que vous ne voulez pas, puisque le but est de parcourir l'intégralité du tableau, donc depuis l’indice 0.
En travaillant avec les tableaux, vous serez confrontés, un jour ou l'autre, au message suivant :
[Link]
Ceci signifie qu'une erreur a été rencontrée, car vous avez essayé de lire (ou d'écrire dans) une case qui n'a pas été définie dans votre tableau ! Voici un exemple
(nous verrons les exceptions lorsque nous aborderons la programmation orientée objet) :
Nous allons maintenant travailler sur le tableau bidimensionnel mentionné précédemment. Le principe est vraiment identique à celui d'un tableau simple, sauf
qu'ici, il y a deux compteurs. Nous allons travailler sur un code permettant d'a icher les données par ligne, c'est-à-dire l'intégralité du sous-tableau de nombres
pairs, puis le sous-tableau de nombres impairs.
while (i < 2)
{
j = 0;
while(j < 5)
{
[Link](premiersNombres[i][j]);
j++;
}
[Link]("");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
i++;
}
A ichage du tableau
On entre ensuite dans la première boucle (qui s'exécutera 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 a icher le contenu du tableau d'indice 0 (notre premier i ).
On entre à nouveau dans la deuxième boucle, où le processus est le même que précédemment (mais là, i vaut 1).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("");
}
Je vous avais parlé d'une nouvelle syntaxe pour cette boucle, la voici :
Ceci signifie qu'à chaque tour de boucle, la valeur courante du tableau est mise dans la variable str . Vous constaterez que cette forme de boucle for est
particulièrement adaptée aux parcours de tableaux !
Attention cependant, 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 etc.).
Concernant les tableaux à deux dimensions, que va retourner l'instruction de la première boucle for ? Un tableau ! Nous devrons donc faire une deuxième
boucle afin de parcourir ce dernier !
Voici un code qui permet d'a icher un tableau à deux dimensions de façon conventionnelle et selon la version du JDK 1.5 (cette syntaxe ne fonctionnera pas sur
les versions antérieures au JDK 1.5) :
String tab[][]={{"toto", "titi", "tutu", "tete", "tata"}, {"1", "2", "3", "4"}};
int i = 0, j = 0;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Je vous laisse le soin d'essayer ce code. Vous pourrez voir que nous récupérons un tableau au cours de la première boucle et 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…
Un tableau est une variable contenant plusieurs données d'un même type.
Pour déclarer un tableau, il faut ajouter des crochets [ ] à la variable ou à son type de déclaration.
Vous pouvez ajouter autant de dimensions à votre tableau que vous le souhaitez, ceci en cumulant des crochets à la déclaration.
Vous pouvez utiliser la syntaxe du JDK 1.5 de la boucle for pour parcourir vos tableaux : for(String str : monTableauDeString) .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
La méthode toLowerCase() permet de transformer tout caractère alphabétique en son équivalent minuscule. Elle n'a aucun e et sur les chi res : ce ne sont
pas des caractères alphabétiques. Vous pouvez donc l'utiliser sans problème sur une chaîne de caractères comportant des nombres.
Elle s'emploie comme ceci :
String chaine = new String("COUCOU TOUT LE MONDE !"), chaine2 = new String();
chaine2 = [Link](); //Donne "coucou tout le monde !"
la méthode toUpperCase() est simple, puisqu'il s'agit de l'opposé de la précédente. Elle transforme donc une chaîne de caractères en capitales, et s'utilise
comme suit :
La méthode length() renvoie la longueur d'une chaîne de caractères (en comptant les espaces).
La méthode equals() permet de vérifier (donc de tester) si deux chaînes de caractères sont identiques. C'est avec cette fonction que vous e ectuerez vos tests
de condition sur les String . Exemple concret :
if ([Link](str2))
[Link]("Les deux chaînes sont identiques !");
else
[Link]("Les deux chaînes sont différentes !");
Vous pouvez aussi demander la vérification de l'inégalité grâce à l'opérateur de négation. Vous vous en souvenez ? Il s'agit de « ! ». Cela nous donne :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
if ()
[Link]("Les deux chaînes sont différentes !");
else
[Link]("Les deux chaînes sont identiques !");
Ce genre de condition fonctionne de la même façon pour les boucles. Dans l'absolu, cette fonction retourne un booléen, c'est pour cette raison que nous pouvons
y recourir dans les tests de condition.
Le résultat de la méthode charAt() sera un caractère : il s'agit d'une méthode d'extraction de caractère. Elle ne peut s'opérer que sur des String ! Par ailleurs,
elle présente 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.
La méthode substring() extrait une partie d'une chaîne de caractères. Elle prend deux entiers en arguments : le premier définit le premier caractère (inclus) de
la sous-chaîne à extraire, le second correspond au dernier caractère (exclu) à extraire. Là encore, le premier caractère porte le numéro 0.
La méthode indexOf() explore une chaîne de caractères à la recherche d'une suite donnée de caractères, et renvoie la position (ou l'index) de la sous-chaîne
passée en argument. la méthode indexOf() explore à partir du début de la chaîne, lastIndexOf() explore en partant de la fin, mais renvoie l'index à partir
du début de la chaîne. Ces deux méthodes prennent un caractère ou une chaîne de caractères comme argument, et renvoient un int . Tout comme charAt()
et substring() , le premier caractère porte le numéro 0. Je crois qu'ici, un exemple s'impose, plus encore que pour les autres fonctions :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les méthodes que nous allons voir nécessitent la classe Math , présente dans [Link] . Elle fait donc partie des fondements du langage. Par conséquent,
aucun import particulier n'est nécessaire pour utiliser la classe Math qui regorge de méthodes utiles :
double X = 0.0;
X = [Link]();
//Retourne un nombre aléatoire
//compris entre 0 et 1, comme 0.0001385746329371058
Je ne vais pas vous faire un récapitulatif de toutes les méthodes présentes dans Java, sinon j'y serai encore dans mille ans… Toutes ces méthodes sont très utiles,
croyez-moi. Cependant, les plus utiles sont encore celles que nous écrivons nous-mêmes ! C'est tellement mieux quand cela vient de nous.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Décortiquons un peu cela :
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.
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 !
Vient ensuite le nom de la méthode. C'est avec ce nom que nous l'appellerons.
Puis arrivent les arguments de la méthode. Ce sont en fait les paramètres dont la méthode a besoin pour travailler. Ici, nous demandons d'arrondir le
double A avec B chi res derrière la virgule.
Finalement, vous pouvez voir une instruction return à l'intérieur de la méthode. C'est elle qui e ectue le renvoi de la valeur, ici un double .
Nous verrons dans ce chapitre les di érents types de renvoi ainsi que les paramètres que peut accepter une méthode.
Vous devez savoir deux choses concernant les méthodes :
les méthodes qui ne renvoient rien. Les méthodes de ce type n'ont pas d'instruction return , et elles sont de type void ;
les méthodes qui retournent des types primitifs ( double , int etc.). Elles sont de type double , int , char etc. Celles-ci possèdent une instruction
return ;
les méthodes qui retournent des objets. Par exemple, une méthode qui retourne un objet de type String . Celles-ci aussi comportent une instruction
return .
Jusque-là, nous n'avons écrit que des programmes comportant une seule classe, ne disposant elle-même que d'une méthode : la méthode main . Le moment est
donc venu de créer vos propres méthodes. Que vous ayez utilisé ou non la méthode arrondi dans votre TP, vous avez dû voir que celle-ci se place à l'extérieur
de la méthode main , mais tout de même dans votre classe !
Pour rappel, jetez un œil à la capture d'écran du premier TP, à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Emplacement des méthodes
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.
Puisque nous venons d'étudier les tableaux, nous allons créer des méthodes pour eux. Vous devez certainement vous souvenir de la façon de parcourir un
tableau. Et si nous faisions une méthode qui permet d'a icher 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 exception faite des plus courageux d'entre vous, vous n'avez utilisé qu'un ou deux
tableaux dans votre main du chapitre précédent. Si je vous demande de déclarer vingt-deux tableaux et que je vous dis : « Allez, bande de Zéros ! Parcourez-moi
tout ça ! », vous n'allez tout de même pas écrire vingt-deux boucles for ! De toute façon, je vous l'interdis. Nous allons écrire une méthode. Celle-ci va :
ne rien renvoyer.
Avec ce que nous avons défini, nous savons que notre méthode sera de type void et qu'elle prendra un tableau en paramètre. Voici un exemple de code complet
:
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class Sdz1
{
public static void main(String[] args)
{
String[] tab = {"toto", "tata", "titi", "tete"};
parcourirTableau(tab);
}
Je sais que cela 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 dans la
partie sur 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 sur ces derniers, vous vous
apercevrez que la méthode a iche le contenu de chaque tableau !
Voici un exemple ayant le même e et que la méthode parcourirTableau , à la di érence que celle-ci retourne une valeur : ici, ce sera une chaîne de caractères.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
{
for(String str : tab)
[Link](str);
}
return retour;
}
}
Vous voyez que la deuxième méthode retourne une chaîne de caractères, que nous devons a icher à l'aide de l'instruction [Link]() . Nous
a ichons la valeur renvoyée par la méthode toString() . La méthode parcourirTableau , quant à 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 au sein de la méthode toString() , afin de vous montrer où elle était appelée.
Il nous reste un point important à aborder. Imaginez un instant que vous ayez plusieurs types d'éléments à parcourir : des tableaux à une dimension, d'autres à
deux dimensions, et même des objets comme des ArrayList (nous les verrons plus tard, ne vous inquiétez pas). Sans aller aussi loin, vous n'allez pas donner
un nom di érent à la méthode parcourirTableau pour 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 un tableau d' int ou de long , par
exemple. Si seulement nous pouvions utiliser la même méthode pour di érents types de tableaux… C'est là qu'entre en jeu ce qu'on appelle la surcharge.
La surcharge de méthode
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
La surcharge de méthode consiste à garder le nom d'une méthode (donc un type de traitement à faire : pour nous, lister un tableau) et à 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. Nous
allons donc surcharger notre méthode afin qu'elle puisse aussi travailler avec des int , comme le montre cet exemple :
Vous pouvez faire de même avec les tableaux à deux dimensions. Voici à quoi pourrait ressembler le code d'une telle méthode (je ne rappelle pas le code des deux
méthodes ci-dessus) :
La surcharge de méthode fonctionne également en ajoutant des paramètres à la méthode. Cette méthode est donc valide :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
static void parcourirTableau(String[][] tab, int i)
{
for(String tab2[] : tab)
{
for(String str : tab2)
[Link](str);
}
}
En fait, c'est la JVM qui va se charger d'invoquer l'une ou l'autre méthode : vous pouvez donc créer des méthodes ayant le même nom, mais avec des paramètres
di érents, en nombre ou en type. La machine virtuelle fait le reste. Ainsi, si vous avez bien défini toutes les méthodes ci-dessus, ce code fonctionne :
Vous venez de créer une méthode qui vous permet de centraliser votre code afin de ne pas avoir à retaper sans arrêt les mêmes instructions. Dans la partie
suivante, vous apprendrez à créer vos propres objets. Elle sera très riche en informations, mais ne vous inquiétez pas : nous apprendrons tout à partir de zéro. ;)
En résumé
Une méthode est un morceau de code réutilisable qui e ectue une action bien définie.
Les méthodes ne peuvent pas être imbriquées. Elles sont déclarées les unes après les autres.
Une méthode peut être surchargée en modifiant le type de ses paramètres, leur nombre, ou les deux.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Pour Java, le fait de surcharger une méthode lui indique qu'il s'agit de deux, trois ou X méthodes di érentes, car les paramètres d'appel sont di érents. Par
conséquent, Java ne se trompe jamais d'appel de méthode, puisqu'il se base sur les paramètres passés à cette dernière.
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 ? :p
Ici str est un objet String . Vous avez utilisé un objet de la classe String : on dit que vous avez créé une instance de la classe String() . Le moment
est venu pour vous de créer vos propres classes.
Structure de base
Une classe peut être comparée à un moule qui, lorsque nous le remplissons, nous donne un objet ayant la forme du moule ainsi que toutes ses caractéristiques.
Comme quand vous étiez enfants, lorsque vous vous amusiez avec de la pâte à modeler.
Si vous avez bien suivi la première partie de ce cours, vous devriez savoir que notre classe contenant la méthode main ressemble à ceci :
class ClasseMain{
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Créez cette classe et cette méthode main (vous savez le faire, maintenant). Puisque nous allons faire de la POO (Programmation Orientée Objet), nous allons
créer une seconde classe dans ce fameux projet ! Créons sans plus tarder une classe Ville . Allez dans File > New > Class ou utilisez le raccourci dans la
barre d'outils, comme sur la figure suivante.
Nommez votre classe « Ville » (avec un « V » majuscule, convention de nommage oblige). Cette fois, vous ne devez pas y créer la méthode main .
Il ne peut y avoir qu'une seule méthode main active par projet ! Souvenez-vous que celle-ci est le point de départ de votre programme. Pour être tout à
fait précis, plusieurs méthodes main peuvent cohabiter dans votre projet, mais une seule sera considérée comme le point de départ de votre programme
!
Classe Ville
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
tâche en ajoutant automatiquement ce mot clé, qui correspond à la portée de la classe. Retenez pour l'instant que public class UneClasse{} et
class UneClasse{} sont presque équivalents !
En programmation, la portée détermine qui peut faire appel à une classe, une méthode ou une variable. Vous avez déjà rencontré la portée public : cela signifie
que tout le monde peut faire appel à l'élément. Ici dans le cas qui nous intéresse il s'agit d'une méthode. Une méthode marquée comme public peut donc être
appelée depuis n'importe quel endroit du programme.
Nous allons ici utiliser une autre portée : private . Elle signifie que notre méthode ne pourra être appelée que depuis l'intérieur de la classe dans laquelle elle se
trouve ! Les méthodes déclarées private correspondent souvent à des mécanismes internes à une classe que les développeurs souhaitent « cacher » ou
simplement ne pas rendre accessibles de l'extérieur de la classe…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Il en va de même pour les variables. Nous allons voir 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 alors accessibles que dans la classe où elles seront nées…
Bon. Toutes les conditions sont réunies pour commencer activement la programmation orientée objet ! Et si nous allions créer notre première ville ?
Les constructeurs
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
possède :
Nous allons faire ceci en mettant des variables d'instance (de simples variables identiques à celles que vous manipulez habituellement) 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 ! Voici à quoi ressemble
notre classe Ville à présent :
Contrairement aux classes, les variables d'instance présentes dans une classe sont public si vous ne leur spécifiez pas de portée. Alors, on parle de variable
d'instance, parce que dans nos futures classes Java qui définiront des objets, il y aura plusieurs types de variables (nous approfondirons ceci dans ce chapitre).
Pour le moment, sachez qu'il y a trois grands types de variables dans une classe objet :
1. Les variables d'instance : ce sont elles qui définiront les caractéristiques de notre objet.
2. Les variables de classe : celles-ci sont communes à toutes les instances de votre classe.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
3. 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'instance afin de créer des objets di érents. Il ne nous reste plus qu'à créer notre premier objet, pour
ce faire, nous allons devoir utiliser ce qu'on appelle des constructeurs.
Un constructeur est une méthode d'instance 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 signaler à la JVM (Java Virtual Machine) qu'il faut réserver de la mémoire pour notre futur objet et donc, par extension, d'en réserver pour toutes ses variables.
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 y initialiser nos variables d'instance. Voici votre premier constructeur :
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 !
Son corollaire est qu'un objet peut avoir plusieurs constructeurs. 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 . Cependant, commencez par vous rappeler qu'une instance d'objet se fait grâce au mot clé new ,
comme lorsque vous créez une variable de type String .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Maintenant, vu que nous allons créer des objets Ville , nous allons procéder comme avec les String . Vérifions que l'instanciation s’e ectue comme il faut.
Allons dans notre classe contenant la méthode main et instancions un objet Ville . Je suppose que vous avez deviné que le type de notre objet sera Ville !
Exécutez ce code, vous devriez avoir l'équivalent de la figure suivante sous les yeux.
Maintenant, nous devons mettre des données dans notre objet, ceci afin de pouvoir commencer à travailler… Le but sera de parvenir à une déclaration d'objet se
faisant comme ceci :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ville ville1 = new Ville("Marseille", 123456789, "France");
Vous avez remarqué qu'ici, les paramètres sont renseignés : eh bien il su it de créer une méthode qui récupère ces paramètres et initialise les variables de notre
objet, ce qui achèvera notre constructeur d'initialisation.
Voici le constructeur de notre objet Ville , celui qui permet d'avoir des objets avec des paramètres di érents :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
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 :
Par contre, notre objet présente un gros défaut : les variables d'instance qui le caractérisent sont accessibles dans votre classe contenant votre main ! Ceci
implique que vous pouvez directement modifier les attributs de la classe. Testez ce code et vous verrez que le résultat est identique à la figure suivante :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Modification des données de notre objet
Vous constatez que nous pouvons accéder aux variables d'instance 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. Dans la majorité des cas, nous allons contrôler les modifications des variables de
classe, de manière à ce qu'un code extérieur ne fasse pas n'importe quoi avec nos objets ! En plus de ça, imaginez que vous souhaitiez faire quelque chose à
chaque fois qu'une valeur change ; si vous ne protégez pas vos données, ce sera impossible à réaliser… C'est pour cela que nous protégeons nos variables
d'instance en les déclarant private , comme ceci :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private String nomVille;
private String nomPays;
private int nbreHabitants;
//…
}
Désormais, ces attributs ne sont plus accessibles en dehors de la classe où ils sont déclarés ! Nous allons maintenant voir comment accéder tout de même à nos
données.
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 nous permettra d'en faire de même en
écriture ! Grâce aux accesseurs, vous pourrez a icher les variables de vos objets, et grâce aux mutateurs, vous pourrez les modifier :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
Nos accesseurs sont bien des méthodes, et elles sont public pour que vous puissiez y accéder depuis une autre classe que celle-ci : depuis le main , par
exemple. Les accesseurs sont du même type que la variable qu'ils doivent retourner. Les mutateurs sont, par contre, de type void . Ce mot clé signifie « rien » ; en
e et, ces méthodes ne retournent aucune valeur, elles se contentent de les mettre à jour.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Je vous ai fait faire la di érence entre accesseurs et mutateurs, mais généralement, lorsqu'on parle d'accesseurs, ce terme inclut également 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 parfois de Getters et de Setters.
/*
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;
/*
Nous allons maintenant interchanger leurs noms
cette fois par le biais de leurs mutateurs.
*/
[Link]("Hong Kong");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("Djibouti");
Vous voyez bien que les constructeurs ont fonctionné, que les accesseurs tournent à merveille et que vous pouvez commencer à travailler avec vos objets Ville .
Par contre, pour a icher le contenu, on pourrait faire plus simple, comme par exemple créer une méthode qui se chargerait de faire tout ceci… Je sais ce que vous
vous dites : « Mais les accesseurs, ce ne sont pas des méthodes ? ». Bien sûr que si, mais il vaut mieux bien distinguer les di érents types de méthodes dans un
objet :
les accesseurs -> méthodes servant à accéder aux données des objets ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Avec nos objets Ville , notre choix est un peu limité par le nombre de méthodes possibles, 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 est déterminé à la construction ou à la
redéfinition du nombre d'habitants : ajoutons donc une variable d'instance de type char à notre classe et appelons-la categorie . Pensez à ajouter le
traitement aux bons endroits ;
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 les méthodes d'un objet comme ceci : [Link](0,4); . Cependant,
vu qu'il va falloir qu'on travaille depuis l'intérieur de notre objet, vous allez encore avoir un mot clé à retenir… Cette fois, il s'agit du mot clé this . Voici tout
d'abord le code de notre classe Ville en entier, c'est-à-dire comportant les méthodes dont on vient de parler :
public Ville(){
[Link]("Création d'une ville !");
nomVille = "Inconnu";
nomPays = "Inconnu";
nbreHabitants = 0;
[Link]();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("Création d'une ville avec des paramètres !");
nomVille = pNom;
nomPays = pPays;
nbreHabitants = pNbre;
[Link]();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
nomVille = pNom;
}
int bornesSuperieures[] = {0, 1000, 10000, 100000, 500000, 1000000, 5000000, 10000000};
char categories[] = {'?', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'};
int i = 0;
while (i < [Link] && [Link] >= bornesSuperieures[i])
i++;
[Link] = categories[i];
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
else
str = [Link]+" est une ville plus peuplée que "+[Link]();
return str;
}
}
Pour simplifier, this fait référence à l'objet courant ! Bien que la traduction anglaise exacte soit « ceci », il faut comprendre « moi ». À l'intérieur d'un
objet, ce mot clé permet de désigner une de ses variables ou une de ses méthodes.
Pour expliciter le fonctionnement du mot clé this , prenons l'exemple de la méthode comparer(Ville V1) . La méthode va s'utiliser comme suit :
[Link](V2);
Dans cette méthode, nous voulons comparer le nombre d'habitants de chacun des deux objets Ville . Pour accéder à la variable nbreHabitants de l'objet
V2 , il su it d'utiliser la syntaxe [Link]() ; nous ferons donc référence à la propriété nbreHabitants de l'objet V2 .
Mais l'objet V , lui, est l'objet appelant de cette méthode. Pour se servir de ses propres variables, on utilise alors [Link] , ce qui a pour e et de
faire appel à la variable nbreHabitants de l'objet exécutant la méthode comparer(Ville V) .
Explicitons un peu les trois méthodes qui ont été décrites précédemment.
La méthode categorie()
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Elle ne prend aucun paramètre, et ne renvoie rien : elle se contente de mettre la variable de classe categorie à jour. Elle détermine dans quelle tranche se
trouve la ville grâce au nombre d'habitants de l'objet appelant, obtenu au moyen du mot clé this . Selon le nombre d'habitants, le caractère renvoyé changera.
Nous l'appelons lorsque nous construisons un objet Ville (que ce soit avec ou sans paramètre), mais aussi lorsque nous redéfinissons le nombre d'habitants :
de cette manière, la catégorie est automatiquement mise à jour, sans qu'on ait besoin de faire appel à la méthode.
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.
Elle prend une ville en paramètre, pour pouvoir comparer les variables nbreHabitants de l'objet appelant la méthode et de celui passé en paramètre pour nous
dire quelle ville est la plus peuplée ! Et si nous faisions un petit test ?
[Link]("\n\n"+[Link]());
[Link]([Link]());
[Link]([Link]()+"\n\n");
[Link]([Link](v2));
Je viens d'avoir une idée : et si nous essayions de savoir combien de villes nous avons créées ? Comment faire ? Avec une variable de classe !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.
La particularité de ce type de variable, c'est qu'elles seront communes à toutes les instances de la classe !
Créons sans plus attendre notre compteur d'instances. Il s'agira d'une variable de type int que nous appellerons nbreInstance , et qui sera public ; nous
mettrons aussi son homologue en private en place et l'appellerons nbreInstanceBis (il sera nécessaire de mettre un accesseur en place pour cette
variable). Afin qu'une variable soit une variable de classe, elle doit être précédée du mot clé static . Cela donnerait dans notre classe Ville :
public Ville(){
//On incrémente nos variables à chaque appel aux constructeurs
nbreInstances++;
nbreInstancesBis++;
//Le reste ne change pas.
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public Ville(String pNom, int pNbre, String pPays)
{
//On incrémente nos variables à chaque appel aux constructeurs
nbreInstances++;
nbreInstancesBis++;
//Le reste ne change pas
}
public static int getNombreInstancesBis()
{
return nbreInstancesBis;
}
//Le reste du code est le même qu'avant
}
Vous avez dû remarquer que l'accesseur de notre variable de classe déclarée privée est aussi déclaré static : ceci est une règle ! Toutes les méthodes de classe
n'utilisant que des variables de classe doivent être déclarées static . On les appelle des méthodes de classe, car il n'y en a qu'une pour toutes vos instances. Par
contre ce n’est plus une méthode de classe si celle-ci utilise des variables d'instance en plus de variables de classe…
À présent, si vous testez le code suivant, vous allez constater l'utilité des variables de classe :
Le résultat, visible à la figure suivante, montre que le nombre augmente à chaque instanciation.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Utilisation de variables de classe
Lorsque vous avez vu les méthodes, vous les avez déclarées public . Vous auriez également pu les déclarer private , mais attention, dans les deux cas, il faut
aussi qu'elles soient static , car elles sont exécutées dans un contexte static : la méthode main .
Le principe d'encapsulation
Voilà, vous venez de construire votre premier objet « maison ». Cependant, sans le savoir, vous avez fait plus que ça : vous avez créé un objet dont les variables
sont protégées de l'extérieur. En e et, depuis l'extérieur de la classe, elles ne sont accessibles que via les accesseurs et mutateurs que nous avons défini. C'est le
principe d'encapsulation !
En fait, lorsqu'on procède de la sorte, on s'assure que le fonctionnement interne à l'objet est intègre, car toute modification d'une donnée de l'objet est maîtrisée.
Nous avons développé des méthodes qui s'assurent qu'on ne modifie pas n'importe comment les variables.
Prenons l'exemple de la variable nbreHabitants . L'encapsuler nous permet, lors de son a ectation, de déduire automatiquement la catégorie de l'objet
Ville , chose qui n'est pas facilement faisable sans encapsulation. Par extension, si vous avez besoin d'e ectuer des opérations déterminées lors de l'a ectation
du nom d'une ville par exemple, vous n'aurez pas à passer en revue tous les codes source utilisant l'objet Ville : vous n'aurez qu'à modifier l'objet (ou la
méthode) en question, et le tour sera joué.
Si vous vous demandez l'utilité de tout cela, dites-vous que vous ne serez peut-être pas seuls à développer vos logiciels, et que les personnes utilisant vos classes
n'ont pas à savoir ce qu'il s'y passe : seules les fonctionnalités qui leurs sont o ertes comptent. Vous le verrez en continuant la lecture de cet ouvrage, Java est
souple parce qu'il o re beaucoup de fonctionnalités pouvant être retravaillées selon les besoins, mais gardez à l'esprit que certaines choses vous seront
volontairement inaccessibles, pour éviter que vous ne « cassiez » quelque chose.
Une classe permet de définir des objets. Ceux-ci ont des attributs (variables d'instance) et des méthodes (méthodes d’instance + accesseurs).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les objets permettent d'encapsuler du code et des données.
Le ou les constructeurs d'une classe doivent porter le même nom que la classe et n'ont pas de type de retour.
Il est recommandé de déclarer ses variables d'instance private , pour les protéger d'une mauvaise utilisation par le programmeur.
On crée des accesseurs et mutateurs (méthodes getters et setters) pour permettre une modification sûre des variables d'instance.
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 classe doivent elles aussi être déclarées static .
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 di érente de
celle que vous connaissez, bien qu'elle en soit tout de même proche. C'est l'un des fondements de la programmation orientée objet !
Imaginons que, dans le programme réalisé précédemment, nous voulions créer un autre type d'objet : des objets Capitale . Ceux-ci ne seront rien d'autre
que des objets Ville avec un paramètre en plus... disons un monument. Vous n'allez tout de même pas recoder tout le contenu de la classe Ville dans
la nouvelle classe ! Déjà, ce serait vraiment contraignant, mais en plus, si vous aviez à modifier le fonctionnement de la catégorisation de nos objets
Ville , vous auriez aussi à e ectuer la modification dans la nouvelle classe… Ce n'est pas terrible.
Heureusement, l'héritage permet à des objets de fonctionner de la même façon que d'autres.
Le principe de l'héritage
Comme je vous l'ai dit dans 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 (aussi appelées classes classes dérivées) de nos classes mères (aussi appelées classes classes de base). Nous pourrons créer autant de classes
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
dérivées, par rapport à notre classe de base, que nous le souhaitons. De plus, nous pourrons nous servir d'une classe dérivée comme d'une classe de base pour
élaborer encore une autre classe dérivée.
Reprenons l'exemple dont je vous parlais dans l'introduction. Nous allons 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 toutes les méthodes associés aux objets Ville !
C'est le mot clé extends qui informe Java que la classe Capitale est héritée de Ville . Pour vous le prouver, essayez ce morceau de code dans votre main :
Objet Capitale
C'est bien la preuve que notre objet Capitale possède les propriétés de notre objet Ville . Les objets hérités peuvent accéder à toutes les méthodes public
(ce n'est pas tout à fait vrai… Nous le verrons avec le mot clé protected ) de leur classe mère, dont la méthode decrisToi() dans le cas qui nous occupe.
En fait, lorsque vous déclarez une classe, si vous ne spécifiez pas de constructeur, le compilateur (le programme qui transforme vos codes sources en byte code)
créera, au moment de l'interprétation, le constructeur par défaut. En revanche, dès que vous avez créé un constructeur, n'importe lequel, la JVM ne crée plus le
constructeur par défaut.
Notre classe Capitale hérite de la classe Ville , par conséquent, 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'instance ont pu être initialisées ! Par contre, essayez ceci dans votre classe :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Vous allez avoir une belle erreur de compilation ! Dans notre classe Capitale , nous ne pouvons pas utiliser directement les attributs de la classe Ville .
Pourquoi cela ? Tout simplement parce les variables de la classe Ville sont déclarées private . C'est ici que le nouveau mot clé protected fait son entrée.
En fait, seules les méthodes et les variables déclarées public ou protected 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 !
Remplacer private par protected dans la déclaration de variables ou de méthodes de la classe Ville aura pour e et de les protéger des utilisateurs de la
classe tout en permettant aux objets enfants d'y accéder. Donc, une fois les variables et méthodes privées de la classe mère déclarées en protected , notre objet
Capitale aura accès à celles-ci ! Ainsi, voici la déclaration de nos variables dans notre classe Ville revue et corrigée :
Notons un point important avant de continuer. Contrairement au C++, Java ne gère pas les héritages multiples : une classe dérivée (aussi appelée classe fille) ne
peut hériter que d'une seule classe mère ! Vous n'aurez donc jamais ce genre de classe :
La raison est toute simple : si nous admettons que nos classes AgrafeuseAirComprime et AgrafeuseManuelle ont toutes les deux une méthode agrafer()
et que vous ne redéfinissez pas cette méthode dans l'objet AgrafeuseBionique , la JVM ne saura pas quelle méthode utiliser et, plutôt que de forcer le
programmeur à gérer les cas d'erreur, les concepteurs du langage ont préféré interdire l'héritage multiple.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
À présent, continuons la construction de notre objet hérité : nous allons agrémenter notre classe Capitale . Comme je vous l'avais dit, ce qui di érenciera nos
objets Capitale de nos objets Ville sera la présence d'un nouveau champ : le nom d'un monument. Cela implique 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 grâce au mot clé
super . Cela aura pour e et de récupérer les éléments de l'objet de base, et de les envoyer à notre objet hérité. Démonstration :
Si vous essayez à 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 : ici, super() appelle le constructeur par défaut de l'objet Ville dans le constructeur de Capitale . Nous avons ensuite ajouté un monument
par défaut.
Cependant, la méthode decrisToi() ne prend pas en compte le nom d'un monument. Eh bien le mot clé super() fonctionne aussi pour les méthodes de
classe, ce qui nous donne une méthode decrisToi() un peu di érente, car nous allons lui ajouter le champ monument pour notre description :
public Capitale(){
//Ce mot clé appelle le constructeur de la classe mère
super();
monument = "aucun";
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public String decrisToi(){
String str = [Link]() + "\n \t ==>>" + [Link]+ " en est un monument";
[Link]("Invocation de [Link]()");
return str;
}
}
Si vous relancez les instructions présentes dans le main depuis le début, vous obtiendrez quelque chose comme sur la figure suivante.
Utilisation de super
J'ai ajouté les instructions [Link] afin de bien vous montrer comment les choses se passent.
Bon, d'accord : nous n'avons toujours pas fait le constructeur d'initialisation de Capitale . Eh bien ? Qu'attendons-nous ?
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
super(nom, hab, pays);
[Link] = monument;
}
/**
* Description d'une capitale
* @return String retourne la description de l'objet
*/
public String decrisToi(){
String str = [Link]() + "\n \t ==>>" + [Link] + "en est un monument";
return str;
}
/**
* @return le nom du monument
*/
public String getMonument() {
return monument;
}
Les commentaires que vous pouvez voir sont ce que l'on appelle des commentaires JavaDoc (souvenez-vous, je vous en ai parlé dans le tout premier
chapitre de ce cours) : ils permettent de créer une documentation pour votre code. Vous pouvez faire le test avec Eclipse en allant dans le menu
Project/Generate JavaDoc .
Dans le constructeur d'initialisation de notre Capitale , vous remarquez la présence de super(nom, hab, pays); . Cette ligne de code joue le même rôle que
celui que nous avons précédemment vu avec le constructeur par défaut. Sauf qu'ici, le constructeur auquel super fait référence prend trois paramètres : ainsi,
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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 .
Je vais vous interpeller une fois de plus : vous venez de faire de la méthode decrisToi() une méthode polymorphe, ce qui nous conduit sans détour à ce qui
suit.
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, et
vous allez voir que le polymorphisme est plus simple qu'il n'y paraît. Pour faire court, nous pouvons le définir en disant qu'il permet de manipuler des objets sans
vraiment connaître leur type.
Dans notre exemple, vous avez vu qu'il su isait d'utiliser la méthode decrisToi() sur un objet Ville ou sur un objet Capitale . On pourrait construire un
tableau d'objets et appeler decrisToi() sans se soucier de son contenu : villes, capitales, ou les deux.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Définition d'un tableau de noms de villes et un autre de nombres d'habitants
String[] tab = {"Marseille", "lille", "caen", "lyon", "paris", "nantes"};
int[] tab2 = {123456, 78456, 654987, 75832165, 1594, 213};
else{
Capitale C = new Capitale(tab[i], tab2[i], "france", "la tour Eiffel");
tableau[i] = C;
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Test de polymorphisme
Nous créons un tableau de villes contenant des villes et des capitales (nous avons le droit de faire ça, car les objets Capitale sont aussi des objets Ville )
grâce à notre première boucle for . Dans la seconde, nous a ichons la description de ces objets… et vous voyez que la méthode polymorphe decrisToi() fait
bien son travail !
Vous aurez sans doute remarqué que je n'utilise que des objets Ville dans ma boucle : 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 superclasse de Capitale . La covariance est e icace dans le cas où la classe héritant redéfinit certaines méthodes de sa
superclasse.
Une méthode surchargée di ère de la méthode originale par le nombre ou le type des paramètres qu'elle prend en entrée.
Une méthode polymorphe a un squelette identique à la méthode de base, mais traite les choses di éremment. Cette méthode se trouve dans une autre
classe et donc, par extension, dans une autre instance de cette autre classe.
Vous devez savoir encore une chose sur l'héritage. Lorsque vous créez une classe ( Ville , par exemple), celle-ci hérite, de façon tacite, de la classe Object
présente dans Java.
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 livre.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Donc, en redéfinissant une méthode de la classe Object dans la classe Ville , nous pourrions utiliser la covariance.
La méthode de la classe Object la plus souvent redéfinie est toString() : elle retourne un String décrivant l'objet en question (comme notre méthode
decrisToi() ). Nous allons donc copier la procédure de la méthode decrisToi() dans une nouvelle méthode de la classe Ville : toString() . Voici son
code :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
else{
Capitale C = new Capitale(tab[i], tab2[i], "france", "la tour Eiffel");
tableau[i] = C;
}
}
Vous pouvez constater qu'il fait exactement la même chose que le code précédent ; nous n'avons pas à nous soucier du type d'objet pour a icher sa description.
Je pense que vous commencez à entrevoir la puissance de Java !
Attention : si vous ne redéfinissez pas ou ne « polymorphez » pas la 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 s'impose : 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
:
[Link](v);
… vous appellerez automatiquement la méthode toString() de la classe Object ! Mais ici, comme vous avez redéfini la méthode toString() dans votre
classe Ville , ces deux instructions sont équivalentes :
[Link]([Link]());
//Est équivalent à
[Link](v);
Pour plus de clarté, je conserverai la première syntaxe, mais il est utile de connaître cette alternative.
Pour clarifier un peu tout ça, vous avez accès aux méthodes public et protected de la classe Object dès que vous créez une classe objet (grâce à l'héritage
tacite). Vous pouvez donc utiliser lesdites 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
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
mère.
Si vous voulez un exemple concret de ce que je viens de vous dire, vous n'avez qu'à retirer la méthode toString() dans les classes Ville et Capitale : vous
verrez que le code de la méthode main fonctionne toujours, mais que le résultat n'est plus du tout pareil, car à l'appel de la méthode toString() , la JVM va
regarder si celle-ci existe dans la classe appelante et, comme elle ne la trouve pas, elle remonte dans la hiérarchie jusqu'à arriver à la classe Object …
Vous devez savoir qu'une méthode n'est « invocable » par un objet que si celui-ci définit ladite méthode.
else{
Capitale C = new Capitale(tab[i], tab2[i], "france", "la tour Eiffel");
tableau[i] = C;
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Pour qu'il 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 :
((Ville)v).decrisToi(); . Vous transtypez la référence v en Ville par cette syntaxe. Ici, l'ordre des opérations s'e ectue comme ceci :
vous appliquez la méthode decrisToi() à la référence appelante, c'est-à-dire, ici, une référence Object changée en Ville .
Vous voyez donc l'intérêt des méthodes polymorphes : grâce à elles, vous n'avez plus à vous soucier du type de variable appelante. Cependant, n'utilisez le type
Object qu'avec parcimonie.
public boolean equals(Object o) , qui permet de vérifier si un objet est égal à un autre ;
public int hashCode() , qui attribue un code de hashage à un objet. En gros, elle donne un identifiant à un objet. Notez que cet identifiant sert plus à
catégoriser votre objet qu'à l'identifier formellement.
Il faut garder en tête que ce n'est pas parce que deux objets ont un même code de hashage qu'ils sont égaux (en e et, deux objets peuvent avoir la même «
catégorie » et être di érents…) ; par contre, deux objets égaux ont forcément le même code de hashage ! En fait, la méthode hashcode() est utilisée par
certains objets (que nous verrons avec les collections) afin de pouvoir classer les objets entre eux.
La bonne nouvelle, c'est qu'Eclipse vous permet de générer automatiquement ces deux méthodes, via le menu Source/Generate hashcode and equals .
Voilà à quoi pourraient ressembler ces deux méthodes pour notre objet Ville .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Lorsque vous devez gérer des hashcodes avec des objets dans le mode de calcul
//Vous devez vérifier si l'objet n'est pas null, sinon vous aurez une erreur
result = prime * result + ((nomPays == null) ? 0 : [Link]());
result = prime * result + ((nomVille == null) ? 0 : [Link]());
return result;
}
//On s'assure que les objets sont du même type, ici de type Ville
//La méthode getClass retourne un objet Class qui représente la classe de votre objet
//Nous verrons ça un peu plus tard...
if (getClass() != [Link]())
return false;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
else if ()
return false;
if (nomVille == null) {
if ([Link] != null)
return false;
}
else if ()
return false;
return true;
}
Il existe encore un type de méthodes dont je ne vous ai pas encore parlé : le type final . Une méthode signée final est figée, vous ne pourrez jamais la
redéfinir (la méthode getClass() de la classe Object est un exemple de ce type de méthode : vous ne pourrez pas la redéfinir).
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 !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Avec Java 7, il existe une classe qui permet de mieux gérer la redéfinitions de ces méthodes : [Link] . Attention, il ne s'agit pas de la classe
[Link] dont tous les objets héritent ! Ici il s'agit d' Objects avec un « s » ! Ce nouvel objet ajoute deux fonctionnalités qui permettent de simplifier
la redéfinition des méthodes vues précédemment.
Nous allons commencer par la plus simple : hashcode() . La classe Objects propose une méthode hash(Object… values) . Cette méthode s'occupe de
faire tout le nécessaire au calcul d'un code de hashage en vérifiant si les attributs sont null ou non et tutti quanti. C'est tout de même sympa. Voici à quoi
ressemblerait notre méthode hashcode() avec cette nouveauté :
Ce nouvel objet intègre aussi une méthode equals() qui se charge de vérifier si les valeurs passées en paramètre sont null ou non. Du coup, nous aurons un
code beaucoup plus clair et lisible. Voici à quoi ressemblerait notre méthode equals() de l'objet Ville :
//On s'assure que les objets sont du même type, ici de type Ville
if (getClass() != [Link]())
return false;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]([Link](), [Link]());
}
Une classe hérite d'une autre classe par le biais du mot clé extends .
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 les propriétés private d'une classe mère ne sont pas accessibles dans la classe fille.
On peut redéfinir une méthode héritée, c'est-à-dire qu'on peut changer tout son code.
On peut utiliser le comportement d'une classe mère par le biais du mot clé super .
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 utilisée.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
partition de musique pour le musicien.
Le but de ce chapitre n'est pas de vous transformer en experts UML, mais de vous donner su isamment de bases pour mieux appréhender la modélisation
et ensuite bien cerner certains concepts de la POO.
Présentation d'UML
Je sais que vous êtes des Zéros 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 connaissances dans le domaine. Il fallait trouver un langage commun aux commerciaux, aux responsables de projets informatiques et aux
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 étapes du développement d'une application informatique, de sa conception à la mise en route, grâce à des
diagrammes. Il est vrai que certains de ces diagrammes sont plus adaptés pour les informaticiens, mais il en existe qui permettent de voir comment interagit
l'application avec son contexte de fonctionnement… Et dans ce genre de cas, il est indispensable de bien connaître l'entreprise pour laquelle l'application est
prévue. On recourt donc à un mode de communication compréhensible par tous : UML.
Il existe bien sûr des outils de modélisation pour créer de tels diagrammes. En ce qui me concerne, j'utilise argoUML. Il a le mérite d'être gratuit et écrit en Java,
donc multi-plates-formes.
Cependant, il en existe d'autres, comme :
boUML,
Together,
Poseidon,
Pyut
etc.
Avec ces outils, vous pouvez réaliser les di érents diagrammes qu'UML vous propose :
le diagramme de use case (cas d'utilisation) permet de déterminer les di érents cas d'utilisation d'un programme informatique ;
le diagramme de classes ; c'est de celui-là que nous allons nous servir. Il permet de modéliser des classes ainsi que les interactions entre elles ;
les diagrammes de séquences, eux, permettent de visualiser le déroulement d'une application dans un contexte donné ;
et d'autres encore…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Exemple de diagramme de classes
Vous avez dû remarquer qu'il représente les classes que nous avons rencontrées dans les chapitres précédents. Je ne vous cache pas qu'il s'agit d'une version
simplifiée. En e et, vous pouvez constater que je n'ai pas fait figurer les méthodes déclarées public de la classe Object , ni celles des classes que nous avons
codées.
Je ne vais pas vous apprendre à utiliser argoUML, mais plutôt à lire un diagramme. En e et, dans certains cas, il est utile de modéliser les classes et l'interaction
entre celles-ci, ne serait-ce que pour disposer de plus de recul sur son travail. Une autre raison à cela est que certains concepts de programmation sont plus faciles
à expliquer avec un diagramme qu'avec de longs discours...
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Classe en UML
La portée des attributs et des méthodes n'est pas représentée ici. Vous voyez, la modélisation d'un objet est toute simple et très compréhensible !
Maintenant, intéressons-nous aux interactions entre objets.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Sur le diagramme représenté à la figure suivante, vous remarquez un deuxième objet qui dispose, lui aussi, de paramètres. Ne vous y trompez pas, ObjetB
possède également les attributs et les méthodes de la classe ObjetA . D'après vous, pourquoi ? C'est parce que 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 .
Représentation de l'héritage
Nous allons voir une autre flèche d'interaction. Je sais que nous n'avons pas encore rencontré ce cas de figure, mais il est simple à comprendre.
De la même façon que nous pouvons utiliser des objets de type String dans des classes que nous développons, nous pouvons aussi utiliser comme variable
d'instance, ou de classe, un objet que nous avons codé. La figure suivante modélise ce cas.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Représentation de l'appartenance
Dans cet exemple simpliste, nous avons toujours notre héritage entre un objet A et un objet B, mais dans ce cas, l' ObjetA (et donc l' ObjetB ) possède une
variable de classe de type ObjetC , ainsi qu'une méthode dont le type de retour est ObjetC (car la méthode retourne un ObjetC ). Vous pouvez lire ce
diagramme comme suit : l' ObjetA a un ObjetC (donc une seule instance d' ObjetC est présente dans ObjetA ).
Fichier [Link]
Fichier [Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class ObjetB extends ObjetA{
Fichier [Link]
Il reste une dernière flèche que nous pouvons mentionner, car elle ne di ère que légèrement de la première. Un diagramme la mettant en œuvre est représenté
sur la figure suivante.
Représentation de la composition
Ce diagramme est identique au précédent, à l'exception de l' ObjetD . Nous devons le lire comme ceci : l' ObjetA est composé de plusieurs instances d' ObjetD .
Vous pouvez d'ailleurs remarquer que la variable d'instance correspondante est de type tableau…
Voici le code Java correspondant :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Fichier [Link]
Fichier [Link]
Fichier [Link]
Fichier [Link]
Il est bien évident que ces classes ne font strictement rien. Je les ai utilisées à titre d'exemple pour la modélisation.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Voilà, c'en est fini pour le moment. Attendez-vous donc à rencontrer des diagrammes dans les prochains chapitres !
Vous pouvez représenter la composition avec une flèche signifiant « est composé de ».
Les packages
Lorsque nous avons été confrontés pour la première fois aux packages, c'était pour importer la classe Scanner via l'instruction
import [Link]; . Le fonctionnement des packages est simple à comprendre : ce sont comme des dossiers permettant de ranger nos classes.
Charger un package nous permet d'utiliser les classes qu'il contient.
Il n'y aura rien de franchement compliqué dans ce chapitre si ce n'est que nous reparlerons un peu de la portée des classes Java.
Nouveau package
Une boîte de dialogue va s'ouvrir et vous demander le nom de votre package, comme à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Nom du package
les caractères autorisés sont alphanumériques (de a à z, de 0 à 9) et peuvent contenir des points (.) ;
tout package doit commencer par com, edu, gov, mil, net, org ou les deux lettres identifiant un pays (ISO Standard 3166, 1981) ; « fr » correspond à la France, «
en » correspond à l'Angleterre (pour England)etc.
aucun mot clé Java ne doit être présent dans le nom, sauf si vous le faites suivre d'un underscore (« _ »), comme ceci : [Link].package_ .
Comme ce cours est issu du Site du Zéro, j'ai pris le nom à l'envers : « [Link] » nous donne « [Link] ». Pour le cas qui nous occupe, appelons-le
[Link] . Cliquez sur Finish pour créer le package. Et voilà : celui-ci est prêt à l'emploi.
Je vous invite à aller voir dans le dossier où se trouvent vos codes sources : vous constaterez qu'il y a l'arborescence du dossier com/sdz/test dans votre
dossier src .
Vous conviendrez que la création d'un package est très simple. Cependant, je ne peux pas vous laisser sans savoir que la portée de vos classes est a ectée par les
packages…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.
Afin de vous prouver mes dires, je vous invite à créer un second package : je l'ai appelé « [Link].test2 ». Dans le premier package, [Link] , créez une
classe A de portée public et une classe B de portée default , comme ceci (j'ai volontairement déclaré les variables d'instance public afin d'alléger
l'exemple) :
package [Link];
class B {
public String str ="";
}
package [Link];
public class A {
public B b = new B();
}
Vous aurez remarqué que les classes contenues dans un package ont en toute première instruction la déclaration de ce package.
Maintenant que cela est fait, afin de faire le test, créez une classe contenant la méthode main , toujours dans le même package, comme ceci :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
package [Link];
Ce code, bien qu'il ne fasse rien, fonctionne très bien : aucun problème de compilation, entre autres. Maintenant, faites un copier-coller de la classe ci-dessus
dans le package [Link].test2 . Vous devriez avoir le résultat représenté à la figure suivante.
Vous pouvez constater qu'Eclipse n'aime ni l'instruction import [Link].B , ni l'instruction B b = new B(); . Cela est dû à la déclaration de notre
classe. J'irai même plus loin : si vous essayez de modifier la variable d'instance de l'objet A , vous aurez le même problème. Donc, ceci : [Link] = "toto";
n'est pas non plus autorisé dans ce package ! La seule façon de corriger le problème est de déclarer la classe B public . Rappelez-vous que seule la classe A
avait été déclarée ainsi.
Si vous voulez utiliser un mot clé Java dans le nom de votre package, vous devez le faire suivre d'un underscore (« _ »).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les classes déclarées public sont visibles depuis l'extérieur du package qui les contient.
Les classes n'ayant pas été déclarées public ne sont pas visibles depuis l'extérieur du package qui les contient.
Si une classe déclarée public dans son package a une variable d'un type ayant une portée default , cette dernière ne pourra pas être modifiée depuis
l'extérieur de son package.
Comment obtenir une structure assez souple pour pallier les problèmes de programmation les plus courants ?
La réponse est dans ce chapitre.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Pour bien en comprendre l'utilité, il vous faut un exemple de situation (de programme, en fait) qui le requiert. Imaginez que vous êtes en train de réaliser un
programme qui gère di é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 aurez des loups, des chiens, des chats, des lions et des tigres. Mais vous n'allez tout de même pas faire toutes vos classes bêtement : il
va de soi que tous ces animaux ont des points communs ! Et qui dit points communs dit héritage. Que pouvons-nous définir de commun à tous ces animaux ? Le
fait qu'ils aient une couleur, un poids, un cri, une façon de se déplacer, qu'ils mangent et boivent quelque chose.
Nous pouvons donc créer 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. La figure suivante représente nos classes.
Classe Animal
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 , qu'est censé faire un objet Animal ? Quel est son poids, sa couleur, que mange-t-il ? Je sais, cela fait plus qu'une question. :p
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Animal ani = new Animal();
((Loup)ani).manger(); //Que doit-il faire ?
}
}
Personnellement, je ne sais pas ce que mange un objet Animal . Vous conviendrez que toutes les classes ne sont pas bonnes à être instanciées !
C'est là qu'entrent en jeu nos classes abstraites. En fait, ces classes servent à définir une superclasse : par là, vous pouvez comprendre qu'elles servent
essentiellement à créer un nouveau type d'objets. Voyons maintenant comment créer une telle classe.
Une telle classe peut contenir la même chose qu'une classe normale. Ses enfants pourront utiliser tous ses éléments déclarés (attributs et méthodes déclarés
public ou protected , nous sommes d'accord). Cependant, ce type de classe permet de définir des méthodes abstraites qui présentent une particularité : elle
n'ont pas de corps ! En voici un exemple :
Vous voyez pourquoi on dit « méthode abstraite » : di icile de voir ce que cette méthode sait faire.
Retenez bien qu'une méthode abstraite n'est composée que de l'en-tête de la méthode suivie d'un point-virgule « ; ».
Il faut que vous sachiez 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 étant abstraite.
Voyons à quoi cela peut servir. Vous avez vu les avantages de l'héritage et du polymorphisme. Eh bien nos classes enfants hériteront aussi des méthodes
abstraites, mais étant donné que celles-ci n'ont pas de corps, nos classes enfants seront obligées de redéfinir ces méthodes ! Elles présentent donc des méthodes
polymorphes, ce qui implique que la covariance des variables pointe à nouveau le bout de son nez :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class Test{
public static void main(String args[]){
Animal loup = new Loup();
Animal chien = new Chien();
[Link]();
[Link]();
}
}
Et je maintiens mes dires : 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é – ici, Animal – n'est une instanciation ! Ici, nous instancions un Loup et un Chien .
Vous pouvez aussi utiliser une variable de type Object comme référence à un objet Loup , à un objet Chien etc. Vous saviez déjà que ce code fonctionne :
Eh oui ! Nous essayons 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 a ecter
à votre objet de type Loup est un Loup , vous devez utiliser le transtypage ! Revoyons notre code :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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 pouvez bien évidemment instancier directement un objet Loup , un objet Chien , etc.
Pour le moment, nous n'avons de code dans aucune classe ! Les exemples que je vous ai fournis ne font rien du tout, mais ils fonctionneront lorsque nous
aurons ajouté des morceaux de code à nos classes.
Nos objets seront probablement tous de couleur et de poids di érents. Nos classes auront donc le droit de modifier ceux-ci.
Ici, nous partons du principe que tous nos animaux mangent 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 (je vous voyais venir).
Ils ne crieront pas et ne se déplaceront pas de la même manière. Nous emploierons donc des méthodes polymorphes et déclarerons les méthodes
deplacement() et crier() abstraites dans la classe Animal .
La figure suivante représente le diagramme des classes de nos futurs objets. Ce diagramme permet de voir si une classe est abstraite : son nom est alors en
italique.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Hiérarchie de nos classes
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 donc que ces deux méthodes doivent être abstraites. Nous ajouterons deux constructeurs à nos classes filles : un par défaut et
un autre comprenant les deux paramètres d'initialisation. À cela, nous ajouterons aussi les accesseurs d'usage. Mais dites donc… 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 eux-mêmes 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. La figure suivante correspond à notre diagramme mis à jour (vous avez remarqué ? J'ai
ajouté une méthode toString() ).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Nouvelle architecture des classes
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
[Link]
[Link]
[Link]
public Chien(){
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
void crier() {
[Link]("J'aboie sans raison !");
}
}
[Link]
public Loup(){
void crier() {
[Link]("Je hurle à la Lune en faisant ouhouh !");
}
}
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class Lion extends Felin {
public Lion(){
void crier() {
[Link]("Je rugis dans la savane !");
}
}
[Link]
public Tigre(){
}
public Tigre(String couleur, int poids){
[Link] = couleur;
[Link] = poids;
}
void crier() {
[Link]("Je grogne très fort !");
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]
public Chat(){
}
public Chat(String couleur, int poids){
[Link] = couleur;
[Link] = poids;
}
void crier() {
[Link]("Je miaule sur les toits !");
}
}
Dis donc ! Une classe abstraite ne doit-elle pas comporter une méthode abstraite ?
Je n'ai jamais dit ça ! Une classe déclarée abstraite n'est pas « instanciable », mais rien ne l'oblige à comprendre des méthodes abstraites. En revanche, une classe
contenant une méthode abstraite doit être déclarée abstraite ! Je vous invite maintenant à faire des tests :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Le jeu d'essai de ce code correspond à la figure suivante.
Dans la méthode toString() de la classe Animal , j'ai utilisé la méthode getClass() qui — je vous le donne en mille — se trouve dans la classe
Object . Celle-ci retourne « class <nom de la classe> ».
Dans cet exemple, nous pouvons constater que nous avons un objet Loup :
À l'appel de la méthode deplacement() : c'est la méthode de la classe Canin qui est invoquée ici.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
À 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 , essayez avec des objets Chien , etc. Vous verrez que tout fonctionne.
Les interfaces
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 très commode d'utiliser
un objet (voire une architecture) que nous avons déjà créé pour une nouvelle application.
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
demandait d'utiliser vos objets dans un autre type d'application ? Ici, 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é, elle, développe une application pour un chenil.
La contrainte principale, c'est que vos chiens devront apprendre à faire de nouvelles choses telles que :
faire le beau ;
Je ne vois pas le problème… Tu n'as qu'à ajouter ces méthodes dans la classe Animal !
Ouh là ! Vous vous rendez compte que vous obtiendrez des lions qui auront la possibilité de faire le beau ? Dans ce cas, on n'a qu'à mettre ces méthodes dans la
classe Chien , mais j'y vois deux problèmes :
1. vous allez devoir mettre en place une convention de nommage entre le programmeur qui va utiliser vos objets et vous. Vous ne pourrez pas utiliser la
méthode faireCalin() , alors que le programmeur oui ;
2. si vous faites cela, adieu au polymorphisme ! Vous ne pourrez pas appeler vos objets par le biais d'un supertype. Pour pouvoir accéder à ces méthodes, vous
devrez obligatoirement passer par une référence à un objet Chien . Pas terrible, tout ça !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Tu nous as dit que pour utiliser au mieux le polymorphisme, nous devions définir les méthodes au plus haut niveau de la hiérarchie. Alors du coup, il faut
redéfinir un supertype 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 le gère pas ! Il faudrait pouvoir développer un
nouveau supertype 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 supertype ; on peut même en ajouter autant que l'on le veut dans une seule classe ! Quant à 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 aux faits !
public class A{ }
… il vous su it de faire :
public interface I{ }
Voilà : vous venez d'apprendre à déclarer une interface. Vu qu'une interface est une classe 100 % abstraite, il ne vous reste qu'à y ajouter des méthodes abstraites,
mais sans le mot clé abstract .
public interface I{
public void A();
public String B();
}
Et pour faire en sorte qu'une classe utilise une interface, il su it d'utiliser le mot clé implements . Ce qui nous donnerait :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class X implements I{
public void A(){
//…
}
public String B(){
//…
}
}
C'est tout. On dit que la classe X implémente l'interface I. Comme je vous le disais, vous pouvez implémenter plusieurs interfaces, et voilà comment ça se passe :
Par contre, lorsque vous implémentez une interface, vous devez obligatoirement redéfinir les méthodes de l'interface ! Ainsi, le polymorphisme vous permet de
faire ceci :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
I2 var2 = new X();
var.A();
var2.C();
}
Comme le titre de cette sous-section 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 , comme à la figure suivante. Voici son code :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
À présent, il ne nous reste plus qu'à implémenter l'interface dans notre classe Chien :
public Chien(){
}
public Chien(String couleur, int poids){
[Link] = couleur;
[Link] = poids;
}
void crier() {
[Link]("J'aboie sans raison !");
}
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 :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class Test {
[Link]("--------------------------------------------");
//Les méthodes de l'interface
[Link]();
[Link]();
[Link]();
[Link]("--------------------------------------------");
//Utilisons le polymorphisme de notre interface
Rintintin r = new Chien();
[Link]();
[Link]();
[Link]();
}
}
Objectif atteint ! Nous sommes parvenus à définir deux superclasses afin de les utiliser comme supertypes et de jouir pleinement du polymorphisme.
Dans la suite de ce chapitre, nous verrons qu'il existe une façon très intéressante d'utiliser les interfaces grâce à une technique de programmation appelée «
pattern strategy ». Sa lecture n'est pas indispensable, mais cela vous permettra de découvrir à travers un cas concret comment on peut faire évoluer au mieux un
programme Java.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Le pattern strategy
Nous allons partir du principe que vous avez un code qui fonctionne, c'est-à-dire un ensemble de classes liées par l'héritage, par exemple. Nous allons voir ici que,
en dépit de la puissance de l'héritage, celui-ci atteint ses limites lorsque vous êtes amenés à modifier la hiérarchie de vos classes afin de répondre à une demande
(de votre chef, d'un client etc.).
Le fait de toucher à votre hiérarchie peut amener des erreurs indésirables, voire des absurdités : tout cela parce que vous allez changer une structure qui
fonctionne à cause de contraintes que l'on vous impose. Pour remédier à ce problème, il existe un concept simple (il s'agit même d'un des fondements de la
programmation orientée objet) : l'encapsulation !
Nous allons parler de cette solution en utilisant un design pattern (ou « modèle de conception » en français). Un design pattern est un patron de conception, une
façon de construire une hiérarchie des classes permettant de répondre à un problème. Nous aborderons le pattern strategy, qui va nous permettre de remédier à
la limite de l'héritage. En e et, même si l'héritage o re beaucoup de possibilités, il a ses limites.
Posons le problème
Mettez-vous dans la peau de développeurs jeunes et ambitieux d'une toute nouvelle société qui crée des jeux vidéo. Le dernier titre en date, « Z-Army », un jeu de
guerre très réaliste, a été un succès international ! Votre patron est content et vous aussi. Vous vous êtes basés sur une architecture vraiment simple afin de créer
et utiliser des personnages, comme le montre la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Hiérarchie des classes
Les guerriers savent se battre tandis que les médecins soignent les blessés sur le champ de bataille. Et c'est maintenant que commencent les ennuis !
Votre patron vous a confié le projet « Z-Army 2 : The return of the revenge », et vous vous dites : « Yes ! Mon architecture fonctionne à merveille, je la garde. » Un
mois plus tard, votre patron vous convoque dans son bureau et vous dit : « Nous avons fait une étude de marché, et il semblerait que les joueurs aimeraient se
battre aussi avec les médecins ! » Vous trouvez l'idée séduisante et avez déjà pensé à une solution : déplacer la méthode combattre() dans la superclasse
Personnage , afin de la redéfinir dans la classe Medecin et jouir du polymorphisme ! La figure suivante schématise le tout.
À la seconde étude de marché, votre patron vous annonce que vous allez devoir créer des civils, des snipers, des chirurgiens etc. Toute une panoplie de
personnages spécialisés dans leur domaine, comme le montre la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Nouveaux personnages spécialisés
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
[Link]
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
[Link]
[Link]
le code contenu dans la méthode seDeplacer() est dupliqué dans toutes les classes ; il est identique dans toutes celles citées ci-dessus ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
le code de la méthode combattre() des classes Chirurgien et Civil est lui aussi dupliqué !
La duplication de code est une chose qui peut générer des problèmes dans le futur. Je m'explique.
Pour le moment, votre chef ne vous a demandé que de créer quelques classes supplémentaires. Qu'en serait-il si beaucoup de classes avaient ce même code
dupliqué ? Il ne manquerait plus que votre chef vous demande de modifier à nouveau la façon de se déplacer de ces objets, et vous courrez le risque d'oublier d'en
modifier un. Et voilà les incohérences qui pointeront le bout de leur nez !
No problemo ! Tu vas voir ! Il su it de mettre un comportement par défaut pour le déplacement et pour le combat dans la superclasse Personnage .
E ectivement, votre idée se tient. Donc, cela nous donne ce qui suit :
[Link]
[Link]
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class Medecin extends Personnage{
public void combattre() {
[Link]("Vive le scalpel !");
}
[Link]
[Link]
[Link]
Voici une classe contenant un petit programme afin de tester nos classes :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static void main(String[] args) {
Personnage[] tPers = {new Guerrier(), new Chirurgien(), new Civil(), new Sniper(), new Medecin()};
for(Personnage p : tPers){
[Link]("\nInstance de " + [Link]().getName());
[Link]("***************************************");
[Link]();
[Link]();
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Résultat du code
Apparemment, ce code vous donne ce que vous voulez ! Mais une chose me chi onne : vous ne pouvez pas utiliser les classes Medecin et Chirurgien de façon
polymorphe, vu que la méthode soigner() leur est propre ! On pourrait définir un comportement par défaut (ne pas soigner) dans la superclasse Personnage
et le tour serait joué.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
public void soigner(){
[Link]("Je ne soigne pas.");
}
}
Au même moment, votre chef rentre dans votre bureau et vous dit : « Nous avons bien réfléchi, et il serait de bon ton que nos guerriers puissent administrer les
premiers soins. » À ce moment précis, vous vous délectez de votre capacité d'anticipation ! Vous savez que, maintenant, il vous su it de redéfinir la méthode
soigner() dans la classe concernée, et tout le monde sera content !
Seulement voilà ! Votre chef n'avait pas fini son speech : « Au fait, il faudrait a ecter un comportement à nos personnages en fonction de leurs armes, leurs habits,
leurs trousses de soin… Enfin, vous voyez ! Les comportements figés pour des personnages de jeux, de nos jours, c'est un peu ringard ! »
Vous commencez à voir ce dont il retourne : vous devrez apporter des modifications à votre code, encore et encore. Bon : pour des programmeurs, cela est le train-
train quotidien, j'en conviens. Cependant, si nous suivons les consignes de notre chef et que nous continuons sur notre lancée, les choses vont se compliquer.
Un problème supplémentaire
Attelons-nous à appliquer les modifications dans notre programme. Selon les directives de notre chef, nous devons gérer des comportements di érents selon les
accessoires de nos personnages : il faut utiliser des variables d'instance pour appliquer l'un ou l'autre comportement.
Afin de simplifier l'exemple, nous n'allons utiliser que des objets String .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Modification de nos classes
Vous avez remarqué que nos personnages posséderont des accessoires. Selon ceux-ci, nos personnages feront des choses di érentes. Voici les recommandations
de notre chef bien-aimé :
le sniper peut utiliser son fusil de sniper ainsi qu'un fusil à pompe ;
le médecin a une trousse simple pour soigner, mais peut utiliser un pistolet ;
le chirurgien a une grosse trousse médicale, mais ne peut pas utiliser d'arme ;
tous les personnages hormis le chirurgien peuvent avoir des baskets pour courir;
Il va nous falloir des mutateurs (inutile de mettre les méthodes de renvoi ( getXXX ), nous ne nous servirons que des mutateurs !) pour ces variables, insérons-les
dans la superclasse ! Bon ! Les modifications sont faites, les caprices de notre cher et tendre chef sont satisfaits ? Voyons cela tout de suite.
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public abstract class Personnage {
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public void combattre() {
if([Link]("pistolet"))
[Link]("Attaque au pistolet !");
else if([Link]("fusil de sniper"))
[Link]("Attaque au fusil de sniper !");
else
[Link]("Attaque au couteau !");
}
}
[Link]
[Link]
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class Medecin extends Personnage{
public void combattre() {
if([Link]("pistolet"))
[Link]("Attaque au pistolet !");
else
[Link]("Vive le scalpel !");
}
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
for(int i = 0; i < [Link]; i++){
[Link]("\nInstance de " + tPers[i].getClass().getName());
[Link]("*****************************************");
tPers[i].combattre();
tPers[i].setArmes(tArmes[i]);
tPers[i].combattre();
tPers[i].seDeplacer();
tPers[i].soigner();
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Résultat du test d'accessoires
Vous constatez avec émerveillement que votre code fonctionne très bien. Les actions par défaut sont respectées, les a ectations d'actions aussi. Tout est parfait !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vraiment ? Vous êtes sûrs de cela ? Pourtant, je vois du code dupliqué dans certaines classes ! En plus, nous n'arrêtons pas de modifier nos classes. Dans le
premier opus de « Z-Army », celles-ci fonctionnaient pourtant très bien ! Qu'est-ce qui ne va pas ?
Là-dessus, votre patron rentre dans votre bureau pour vous dire : « Les actions de vos personnages doivent être utilisables à la volée et, en fait, les personnages
peuvent très bien apprendre au fil du jeu. » Les changements s'accumulent, votre code devient de moins en moins lisible et réutilisable, bref c'est l'enfer sur Terre.
Faisons un point de la situation :
à chaque modification du comportement de vos personnages, vous êtes obligés de retoucher le code source de la (ou des) classe(s) concernée(s) ;
Le pattern strategy est basé sur ce principe simple. Bon, vous avez compris que le pattern strategy consiste à créer des objets avec des données, des méthodes
(voire les deux) : c'est justement ce qui change dans votre programme !
Le principe de base de ce pattern est le suivant : « isolez ce qui varie dans votre programme et encapsulez-le ! »
Déjà, quels sont les éléments qui ne cessent de varier dans notre programme ?
La méthode combattre() .
La méthode seDeplacer() .
La méthode soigner() .
Ce qui serait vraiment grandiose, ce serait d'avoir la possibilité de ne modifier que les comportements et non les objets qui ont ces comportements ! Non ?
Là, je vous arrête un moment : vous venez de fournir la solution. Vous avez dit : « ce qui serait vraiment grandiose, ce serait d'avoir la possibilité de ne modifier
que les comportements et non les objets qui ont ces comportements ».
Lorsque je vous ai présenté les diagrammes UML, je vous ai fourni une astuce pour bien di érencier les liens entre les objets. Dans notre cas, nos classes héritant
de Personnage héritent aussi de ses comportements et, par conséquent, on peut dire que nos classes filles sont des Personnage .
Les comportements de la classe mère semblent ne pas être au bon endroit dans la hiérarchie. Vous ne savez plus quoi en faire et vous vous demandez s'ils ont
vraiment leur place dans cette classe ? Il vous su it de sortir ces comportements de la classe mère, de créer une classe abstraite ou une interface symbolisant ce
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
comportement et d'ordonner à votre classe Personnage d'avoir ces comportements. Le nouveau diagramme des classes se trouve sur la figure suivante.
Vous apercevez une nouvelle entité sur ce diagramme, l'interface, facilement reconnaissable, ainsi qu'une nouvelle flèche symbolisant l'implémentation
d'interface entre une classe concrète et une interface. N'oubliez pas que votre code doit être souple et robuste et que — même si ce chapitre vous montre les
limites de l'héritage — le polymorphisme est inhérent à l'héritage (ainsi qu'aux implémentations d'interfaces).
Il faut vous rendre compte qu'utiliser une interface de cette manière revient à créer un supertype de variable ; du coup, nous pourrons utiliser les classes héritant
de ces interfaces de façon polymorphe sans nous soucier de savoir la classe dont sont issus nos objets ! Dans notre cas, notre classe Personnage comprendra
des objets de type EspritCombatif , Soin et Deplacement !
Avant de nous lancer dans le codage de nos nouvelles classes, vous devez observer que leur nombre a considérablement augmenté depuis le début. Afin de
pouvoir gagner en clarté, nous allons gérer nos di érentes classes avec di érents packages .
Comme nous l'avons remarqué tout au long de ce chapitre, les comportements de nos personnages sont trop épars pour être définis dans notre superclasse
Personnage . Vous l'avez dit vous-mêmes : il faudrait que l'on ne puisse modifier que les comportements et non les classes héritant de notre superclasse !
Les interfaces nous servent à créer un supertype d'objet ; grâce à elles, nous utiliserons des objets de type :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Deplacement qui présentent une méthode deplace() .
Dans notre classe Personnage , nous avons ajouté une instance de chaque type de comportement, vous avez dû les remarquer : il y a ces attributs dans notre
schéma ! Nous allons développer un comportement par défaut pour chacun d'entre eux et a ecter cet objet dans notre superclasse. Les classes filles, elles,
comprendront des instances di érentes correspondant à leurs besoins.
Nous les gardons, mais plutôt que de redéfinir ces dernières, la superclasse va invoquer la méthode de comportement de chaque objet. Ainsi, nous n'avons plus à
redéfinir ou à modifier nos classes ! La seule chose qu'il nous reste à faire, c'est d'a ecter une instance de comportement à nos objets. Vous comprendrez mieux
avec un exemple. Voici quelques implémentations de comportements.
package [Link];
package [Link];
package [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public void combat() {
[Link]("Je me bats au couteau !");
}
}
package [Link];
package [Link];
package [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
package [Link];
package [Link];
La figure suivante représente ce que vous devriez avoir dans votre nouveau package.
Maintenant que nous avons défini des objets de comportements, nous allons pouvoir remanier notre classe Personnage . Ajoutons les variables d'instance, les
mutateurs et les constructeurs permettant d'initialiser nos objets :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link].*;
//Méthode de soin
public void soigner(){
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On utilise les objets de déplacement de façon polymorphe
[Link]();
}
Que de changements depuis le début ! Maintenant, nous n'utilisons plus de méthodes définies dans notre hiérarchie de classes, mais des implémentations
concrètes d'interfaces ! Les méthodes que nos objets appellent utilisent chacune un objet de comportement. Nous pouvons donc définir des guerriers, des civils,
des médecins… tous personnalisables, puisqu'il su it de modifier l'instance de leur comportement pour que ceux-ci changent instantanément. La preuve par
l'exemple.
Je ne vais pas vous donner les codes de toutes les classes. En voici seulement quelques-unes.
[Link]
import [Link].*;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public Guerrier(EspritCombatif esprit, Soin soin, Deplacement dep) {
super(esprit, soin, dep);
}
}
[Link]
import [Link].*;
[Link]
import [Link].*;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
class Test{
public static void main(String[] args) {
Personnage[] tPers = {new Guerrier(), new Civil(), new Medecin()};
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous pouvez voir que nos personnages ont tous un comportement par défaut qui leur convient bien ! Nous avons spécifié, dans le cas où cela s'avère nécessaire,
le comportement par défaut d'un personnage dans son constructeur par défaut :
le médecin soigne.
Voyons maintenant comment indiquer à nos personnages de faire autre chose. Eh oui, la façon dont nous avons arrangé tout cela va nous permettre de changer
dynamiquement le comportement de chaque Personnage . Que diriez-vous de faire faire une petite opération chirurgicale à notre objet Guerrier ?
Pour ce faire, vous pouvez redéfinir son comportement de soin avec son mutateur présent dans la superclasse en lui passant une implémentation correspondante
!
import [Link].*;
class Test{
public static void main(String[] args) {
Personnage pers = new Guerrier();
[Link]();
[Link](new Operation());
[Link]();
}
}
En testant ce code, vous constaterez que le comportement de soin de notre objet a changé dynamiquement sans que nous ayons besoin de changer la moindre
ligne de son code source ! Le plus beau dans le fait de travailler comme cela, c'est qu'il est tout à fait possible d'instancier des objets Guerrier avec des
comportements di érents.
Une classe est définie comme abstraite avec le mot clé abstract .
Les classes abstraites sont à utiliser lorsqu'une classe mère ne doit pas être instanciée.
Si une classe contient une méthode abstraite, cette classe doit alors être déclarée abstraite.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Une interface est une classe 100 % abstraite.
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 des interfaces) dans votre classe.
Le pattern strategy vous permet de rendre une hiérarchie de classes plus souple.
Préférez encapsuler des comportements plutôt que de les mettre d'o ice dans l'objet concerné.
Les exceptions
Voici encore une notion très importante en programmation. Une exception est une erreur se produisant dans un programme qui conduit le plus souvent à
l'arrêt de celui-ci. Il vous est sûrement déjà arrivé d'obtenir un gros message a iché en rouge dans la console d'Eclipse : eh bien, cela a été généré par une
exception… qui n'a pas été capturée.
Le fait de gérer les exceptions s'appelle aussi « la capture d'exception ». Le principe consiste à repérer un morceau de code (par exemple, une division par
zéro) qui pourrait générer une exception, de capturer l'exception correspondante et enfin de la traiter, c'est-à-dire d'a icher un message personnalisé et de
continuer l'exécution.
Bon, vous voyez maintenant ce que nous allons aborder dans ce chapitre… Donc, allons-y !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
int j = 20, i = 0;
[Link](j/i);
[Link]("coucou toi !");
… vous verrez apparaître un joli message d'erreur Java (en rouge) comme celui de la figure suivante.
ArithmeticException
Mais surtout, vous devez avoir constaté que lorsque l'exception a été levée, le programme s'est arrêté ! D'après le message a iché dans la console, 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, avec un bloc try{…}catch{…} , puis réaliser un traitement en conséquence. Ce que je vous propose maintenant, c'est d'a icher un
message personnalisé lors d'une division par 0. Pour ce faire, tapez le code suivant dans votre main :
int j = 20, i = 0;
try {
[Link](j/i);
} catch (ArithmeticException e) {
[Link]("Division par zéro !");
}
[Link]("coucou toi !");
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Capture d'exception
Nous initialisons deux variables de type int , l'une à 0 et l'autre à un nombre quelconque.
Une exception de type ArithmeticException est levée lorsque le programme atteint cette ligne.
Notre bloc catch contient justement un objet de type ArithmeticException en paramètre. Nous l'avons appelé e .
Vous vous demandez sûrement à quoi sert le paramètre de la clause catch . Il permet de connaître le type d'exception qui doit être capturé. Et l'objet — ici, e —
peut servir à préciser notre message grâce à l'appel de la méthode getMessage() . Faites à nouveau ce test, en remplaçant l'instruction du catch par celle-ci :
Vous verrez que la fonction getMessage() de notre objet ArithmeticException nous précise la nature de l'erreur.
Je vous disais aussi que le principe de capture d'exception permettait de ne pas interrompre l'exécution du programme. En e et, lorsque nous capturons une
exception, le code présent dans le bloc catch(){…} est exécuté, mais le programme suit son cours !
Avant de voir comment créer nos propres exceptions, sachez que le bloc permettant de capturer ces dernières o re une fonctionnalité importante. En fait, vous
avez sans doute compris que lorsqu'une ligne de code lève une exception, l'instruction dans le bloc try est interrompue et le programme se rend dans le bloc
catch correspondant à l'exception levée.
Prenons un cas de figure très simple : imaginons que vous souhaitez e ectuer une action, qu'une exception soit levée ou non (nous verrons lorsque nous
travaillerons avec les fichiers qu'il faut systématiquement fermer ceux-ci). Java vous permet d'utiliser une clause via le mot clé finally . Voyons ce que donne
ce code :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static void main(String[] args){
try {
[Link](" =>" + (1/0));
} catch (ClassCastException e) {
[Link]();
}
finally{
[Link]("action faite systématiquement");
}
}
Lorsque vous l'exécutez, vous pouvez constater que, même si nous tentons d'intercepter une ArithmeticException (celle-ci se déclenche lors d'un problème
de cast), grâce à la clause finally , un morceau de code est exécuté quoi qu'il arrive. Cela est surtout utilisé lorsque vous devez vous assurer d'avoir fermé un
fichier, clos votre connexion à une base de données ou un socket (une connexion réseau). Maintenant que nous avons vu cela, nous pouvons aller un peu plus loin
dans la gestion de nos exceptions.
La procédure pour faire ce tour de force est un peu particulière. En e et, nous devons :
1. créer une classe héritant de la classe Exception : NombreHabitantException (par convention, les exceptions ont un nom se terminant par « Exception »)
;
Pour faire tout cela, je vais encore vous apprendre deux mots clés :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
throws : ce mot clé permet de signaler à la JVM qu'un morceau de code, une méthode, une classe… est potentiellement dangereux et qu'il faut utiliser un
bloc try{…}catch{…} . Il est suivi du nom de la classe qui va gérer l'exception.
throw : celui-ci permet tout simplement de lever une exception manuellement en instanciant un objet de type Exception (ou un objet hérité). Dans
l'exemple de notre ArithmeticException , il y a quelque part dans les méandres de Java un throw new ArithmeticException() .
Pour mettre en pratique ce système, commençons par créer une classe qui va gérer nos exceptions. Celle-ci, je vous le rappelle, doit hériter d' Exception :
Reprenez votre projet avec vos classes Ville et Capitale et créez ensuite une classe NombreHabitantException , comme je viens de le faire. Maintenant,
c'est dans le constructeur de nos objets que nous allons ajouter une condition qui, si elle est remplie, lèvera une exception de type NombreHabitantException .
En gros, nous devons dire à notre constructeur de Ville : « si l'utilisateur crée une instance de Ville avec un nombre d'habitants négatif, créer un objet de
type NombreHabitantException ».
nomVille = pNom;
nomPays = pPays;
nbreHabitant = pNbre;
[Link]();
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
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 nous renseigne sur le type de l'erreur en question. Elle indique aussi à la JVM que le constructeur de notre objet Ville est
potentiellement dangereux et qu'il faudra gérer les exceptions possibles.
Si la condition if(nbre < 0) est remplie, throw new NombreHabitantException(); instancie la classe NombreHabitantException . Par conséquent, si
un nombre d'habitants est négatif, l'exception est levée.
Maintenant que vous avez apporté cette petite modification, retournez dans votre classe main , e acez son contenu, puis créez un objet Ville de votre choix.
Vous devez tomber sur une erreur persistante, comme à la figure suivante ; c'est tout à fait normal et dû à l'instruction throws .
Cela signifie qu'à partir de maintenant, vu les changements dans le constructeur, il vous faudra gérer les exceptions qui pourraient survenir dans cette instruction
avec un bloc try{…}catch{} .
Ainsi, pour que l'erreur disparaisse, il nous faut entourer notre instanciation avec un bloc try{…}catch{…} , comme à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Correction du bug
Vous pouvez constater que l'erreur a disparu, que notre code peut être compilé et qu'il s'exécute correctement.
Attention, il faut que vous soyez préparés à une chose : le code que j'ai utilisé fonctionne très bien, mais il y a un autre risque, l'instance de mon objet Ville a
été déclarée dans le bloc try{…}catch{…} et cela peut causer beaucoup de problèmes.
Ce code :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]([Link]());
}
… ne fonctionnera pas, tout simplement parce que la déclaration de l'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 celui-ci ! Ici, la variable v n'existe pas en dehors de l'instruction try{…} . Pour pallier
ce problème, il nous su it de déclarer notre objet en dehors du bloc try{…} et de l'instancier à l'intérieur :
[Link]([Link]());
}
Mais que se passera-t-il si nous déclarons une Ville avec un nombre d'habitants négatif pour tester notre exception ? En remplaçant « 12000 » par « -12000 »
dans l'instanciation de notre objet ? C'est simple : en plus d'une exception levée pour le nombre d'habitants négatif, vous obtiendrez aussi une
NullPointerException .
Lorsque nous arrivons sur l'instruction « [Link]([Link]()); », notre objet est null !
Ce qui signifie que si l'instanciation échoue dans notre bloc try{} , le programme plante ! Pour résoudre ce problème, on peut utiliser une simple clause
finally avec, à l'intérieur, l'instanciation d'un objet Ville par défaut si celui-ci est null :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static void main(String[] args)
{
Ville v = null;
try {
v = new Ville("Rennes", 12000, "France");
} catch (NombreHabitantException e) { }
finally{
if(v == null)
v = new Ville();
}
[Link]([Link]());
}
Pas besoin de capturer une exception sur l'instanciation de notre objet ici : le code n'est considéré comme dangereux que sur le constructeur avec paramètres.
Maintenant que nous avons vu la création d'une exception, il serait de bon ton de pouvoir récolter plus de renseignements la concernant. Par exemple, il serait
peut-être intéressant de réa icher le nombre d'habitants que l'objet a reçu. Pour ce faire, nous n'avons qu'à créer un deuxième constructeur dans notre classe
NombreHabitantException qui prend un nombre d'habitants en paramètre :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Le code est identique à précédemment
}
}
Et si vous exécutez le même code que précédemment, vous pourrez voir le nouveau message de notre exception s'a icher.
Ce n'est pas mal, avouez-le ! Sachez également que l'objet passé en paramètre de la clause catch a des méthodes héritées de la classe Exception : 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. Je vais vous faire
peur : ici, nous avons capturé une exception, mais nous pouvons en capturer plusieurs !
Vous avez remarqué que nous avons utilisé super ? Avec cette redéfinition, nous pourrons a icher notre message d'erreur en utilisant la méthode
getMessage() .
Dans le code suivant, nous ajoutons une condition dans le constructeur Ville :
public Ville(String pNom, int pNbre, String pPays) throws NombreHabitantException, NomVilleException
{
if(pNbre < 0)
throw new NombreHabitantException(pNbre);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
if([Link]() < 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;
[Link]();
}
}
Vous remarquez que les di érentes erreurs dans l'instruction throws sont séparées par une virgule. Nous sommes maintenant parés pour la capture de deux
exceptions personnalisées. Regardez comment on gère deux exceptions sur une instruction :
Ville v = null;
try {
v = new Ville("Re", 12000, "France");
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
if(v == null)
v = new Ville();
}
[Link]([Link]());
Constatez qu'un deuxième bloc catch{} s'est glissé… Eh bien, c'est comme cela que nous gérerons plusieurs exceptions !
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 la première condition dans notre constructeur. Lorsque plusieurs exceptions sont gérées par une portion de code, pensez bien à mettre les
blocs catch dans un ordre pertinent.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]([Link]());
}
Ce morceau de code nous remonte donc une erreur impliquant le nom de la ville (celui-ci fait moins de 3 caractères). Je vous invite à modifier le nombre
d'habitants en le passant en négatif et vous verrez que l'exception concernant le nombre d'habitants est bien capturée. :magicien:
Lorsqu'un événement que la JVM ne sait pas gérer apparaît, une exception est levée (exemple : division par zéro). Une exception correspond donc à une
erreur.
Vous pouvez créer une classe d'exception personnalisée : faites-lui hériter de la classe Exception .
Si une exception est levée dans le bloc try , les instructions figurant dans le bloc catch seront exécutées pour autant que celui-ci capture la bonne
exception levée.
Vous pouvez ajouter autant de blocs catch que vous le voulez à la suite d'un bloc try , mais respectez l'ordre : du plus pertinent au moins pertinent.
Dans une classe objet, vous pouvez prévenir la JVM qu'une méthode est dite « à risque » grâce au mot clé throws .
Vous pouvez définir plusieurs risques d'exceptions sur une même méthode. Il su it 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 suivi de
l'instanciation.
Une instanciation lancée par le biais de l'instruction throw doit être déclarée avec throws au préalable !
Les énumérations
Les énumérations constituent une notion nouvelle depuis Java 5. Ce sont des structures qui définissent une liste de valeurs possibles. Cela vous permet de
créer des types de données personnalisés. Nous allons par exemple construire le type Langage qui ne peut prendre qu'un certain nombre de valeurs :
JAVA, PHP, C, etc.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Le principe est très simple, vous allez voir !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Avant les énumérations, des erreurs étaient possibles
Je viens de vous montrer non seulement le principe dont je vous parlais, mais aussi sa faiblesse. Vous voyez que rien ne vous empêche de passer un paramètre
inattendu à une méthode : c'est ce qui s'est passé à la dernière ligne de notre test. Ici, rien de méchant, mais vous conviendrez tout de même que le
comportement de notre méthode est faussé !
Bien sûr, vous pourriez créer un objet qui vous sert de paramètre de la méthode. Eh bien c'est à cela que servent les enum : fabriquer ce genre d'objet de façon
plus simple et plus rapide.
Rien de di icile ! Avec cela, vous obtenez une structure de données qui encapsule quatre « objets ». En fait, c'est comme si vous aviez un objet JAVA , un objet C ,
un objet CPlus et un objet PHP partageant tous les mêmes méthodes issues de la classe [Link] comme n'importe quel autre objet :
equals(), toString() , etc.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous constatez aussi qu'il n'y a pas de déclaration de portée, ni de type : les énumérations s'utilisent comme des variables statiques déclarées public : on écrira
par exemple [Link] . De plus, vous pouvez recourir à la méthode values() retournant la liste des déclarations de l'énumération dont vous trouverez
un exemple à la figure suivante et sur son code :
Vous disposez ainsi d'un petit aperçu de l'utilisation des énumérations. Vous aurez pu constater que la méthode toString() retourne le nom de l'objet défini
dans l'énumération.
À présent, éto ons tout cela en redéfinissant justement cette méthode. Pour ce faire, nous allons ajouter un paramètre dans notre énumération, un constructeur
et ladite méthode redéfinie. Voici notre nouvelle énumération (résultat en figure suivante) :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private String name = "";
//Constructeur
Langage(String name){
[Link] = name;
}
Même remarque pour le constructeur : pas de déclaration de portée, pour une raison simple ; il est toujours considéré comme private afin de préserver les
valeurs définies dans l' enum . Vous noterez par ailleurs que les données formant notre énumération sont directement construites dans la classe.
Voici le code du début de chapitre, revu pour préférer les énumérations aux variables statiques :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static void main(String args[]){
AvantEnumeration ae = new AvantEnumeration();
[Link]([Link]);
[Link]([Link]);
[Link](4);
}
}
Une belle exception ! Normal, puisque la méthode attend un certain type d'argument, et que vous lui en passez un autre : supprimez la dernière ligne, le code
fonctionnera très bien.
Maintenant, nous avons un mécanisme protégé : seuls des arguments valides peuvent être passés en paramètres de la méthode.
Voici un exemple plus complet :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Constructeur
Langage(String name, String editor){
[Link] = name;
[Link] = editor;
}
[Link]();
[Link]();
}
}
Vous voyez ce que je vous disais : les énumérations ne sont pas très di iciles à utiliser et nos programmes y gagnent en rigueur et en clarté.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Une énumération se construit grâce au mot clé enum .
Vous pouvez compléter les comportements des objets d'une énumération en ajoutant des méthodes.
Ce chapitre vous sera d'une grande utilité, car les collections sont primordiales dans les programmes Java.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Hiérarchie d'interfaces
Vous pouvez voir qu'il existe plusieurs types de collections, que les interfaces List et Set implémentent directement l'interface Collection et que
l'interface Map gravite autour de cette hiérarchie, tout en faisant partie des collections Java.
En lisant la suite de ce chapitre, vous constaterez que ces interfaces ont des particularités correspondant à des besoins spécifiques. Les objets de type List
servent à stocker des objets sans condition particulière sur la façon de les stocker. Ils acceptent toutes les valeurs, même les valeurs null . Les types Set sont
un peu plus restrictifs, car ils n'autorisent pas deux fois la même valeur (le même objet), ce qui est pratique pour une liste d'éléments uniques, par exemple. Les
Map sont particulières, car elles fonctionnent avec un système clé - valeur pour ranger et retrouver les objets qu'elles contiennent.
Maintenant que je vous ai brièvement expliqué les di érences entre ces types, voyons comment utiliser ces objets.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les objets List
Les objets appartenant à la catégorie List sont, pour simplifier, des tableaux extensibles à volonté. On y trouve les objets Vector , LinkedList et
ArrayList . Vous pouvez y insérer autant d'éléments que vous le souhaitez sans craindre de dépasser la taille de votre tableau. Ils fonctionnent tous de la même
manière : vous pouvez récupérer les éléments de la liste via leurs indices. De plus, les List contiennent des objets. Je vous propose de voir deux objets de ce
type qui, je pense, vous seront très utiles.
L'objet LinkedList
Une liste chaînée (LinkedList en anglais) est une liste dont chaque élément est lié aux éléments adjacents par une référence à ces derniers. Chaque élément
contient une référence à l'élément précédent et à l'élément suivant, exceptés le premier, dont l'élément précédent vaut null , et le dernier, dont l'élément
suivant vaut également null .
La figure suivante représente un un schéma qui vous permettra de mieux vous représenter le fonctionnement de cet objet :
Fonctionnement de la LinkedList
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static void main(String[] args) {
List l = new LinkedList();
[Link](12);
[Link]("toto ! !");
[Link](12.20f);
Si vous essayez ce code, vous constaterez que tous les éléments s'a ichent !
Il y a autre chose que vous devez savoir sur ce genre d'objet : ceux-ci implémentent l'interface Iterator . Ainsi, nous pouvons utiliser cette interface pour lister
notre LinkedList .
Un itérateur est un objet qui a pour rôle de parcourir une collection. C'est d'ailleurs son unique raison d'être. Pour être tout à fait précis, l'utilisation des itérateurs
dans Java fonctionne de la même manière que le pattern du même nom. Tout comme nous avons pu le voir avec la pattern strategy, les design patterns sont en
fait des modèles de conception d'objets permettant une meilleure stabilité et une réutilisabilité accrue. Les itérateurs en font partie.
Dans le code suivant, j'ai ajouté le parcours avec un itérateur :
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("\n \tParcours avec un itérateur ");
[Link]("-----------------------------------");
ListIterator li = [Link]();
while([Link]())
[Link]([Link]());
}
}
L'objet ArrayList
Voici un objet bien pratique. ArrayList est un de ces objets qui n'ont pas de taille limite et qui, en plus, acceptent n'importe quel type de données, y compris
null ! Nous pouvons mettre tout ce que nous voulons dans un ArrayList , voici un morceau de code qui le prouve :
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("donnée à l'indice " + i + " = " + [Link](i));
}
}
}
contains(Object element) retourne « vrai » si l'élément passé en paramètre est dans l' ArrayList .
Contrairement aux LinkedList , les ArrayList sont rapides en lecture, même avec un gros volume d'objets. Elles sont cependant plus lentes si vous devez
ajouter ou supprimer des données en milieu de liste. Pour résumer à l'extrême, si vous e ectuez beaucoup de lectures sans vous soucier de l'ordre des éléments,
optez pour une ArrayList ; en revanche, si vous insérez beaucoup de données au milieu de la liste, optez pour une Linkedlist .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les objets Map
Une collection de type Map est une collection qui fonctionne avec un couple clé - valeur. On y trouve les objets Hashtable , HashMap , TreeMap ,
WeakHashMap … La clé, qui sert à identifier une entrée dans notre collection, est unique. La valeur, au contraire, peut être associée à plusieurs clés.
Ces objets ont comme point faible majeur leur rapport conflictuel avec la taille des données à stocker. En e et, plus vous aurez de valeurs à mettre dans un objet
Map , plus celles-ci seront lentes et lourdes : logique, puisque par rapport aux autres collections, il stocke une donnée supplémentaire par enregistrement. Une
donnée c'est de la mémoire en plus et, même si les ordinateurs actuels en ont énormément, gardez en tête que « la mémoire, c'est sacré » (je vous rappelle que
les applications Java ne sont pas forcément destinées aux appareils bénéficiant de beaucoup de mémoire).
L'objet Hashtable
Vous pouvez également dire « table de hachage », si vous traduisez mot à mot… On parcourt cet objet grâce aux clés qu'il contient en recourant à la classe
Enumeration . L'objet Enumeration contient notre Hashtable et permet de le parcourir très simplement. Regardez, le code suivant insère les quatre saisons
avec des clés qui ne se suivent pas, et notre énumération récupère seulement les valeurs :
import [Link];
import [Link];
Enumeration e = [Link]();
while([Link]())
[Link]([Link]());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
contains(Object value) retourne « vrai » si la valeur est présente. Identique à containsValue(Object value) ;
containsKey(Object key) retourne « vrai » si la clé passée en paramètre est présente dans la Hashtable ;
put(Object key, Object value) ajoute le couple key - value dans l'objet ;
De plus, il faut savoir qu'un objet Hashtable n'accepte pas la valeur null et qu'il est Thread Safe , c'est-à-dire qu'il est utilisable dans plusieurs threads
(cela signifie que plusieurs éléments de votre programme peuvent l'utiliser simultanément ; nous y reviendrons) simultanément sans qu'il y ait un risque de
conflit de données.
L'objet HashMap
En fait, les deux objets de type Map sont, à peu de choses près, équivalents.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les Set sont particulièrement adaptés pour manipuler une grande quantité de données. Cependant, les performances de ceux-ci peuvent être amoindries en
insertion. Généralement, on opte pour un HashSet , car il est plus performant en temps d'accès, mais si vous avez besoin que votre collection soit constamment
triée, optez pour un TreeSet .
L'objet HashSet
C'est sans nul doute la plus utilisée des implémentations de l'interface Set . On peut parcourir ce type de collection avec un objet Iterator ou extraire de cet
objet un tableau d' Object :
import [Link];
import [Link];
Iterator it = [Link]();
while([Link]())
[Link]([Link]());
Voici une liste des méthodes que l'on trouve dans cet objet :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
contains(Object value) retourne « vrai » si l'objet contient value ;
Voilà ! Nous avons vu quelque chose d'assez intéressant que nous pourrons utiliser dans peu de temps, mais avant, nous avons encore du pain sur la planche.
Dans le chapitre suivant nous verrons d'autres aspects de nos collections.
Il y a principalement trois types de collection : les List , les Set et les Map .
Les Collection stockent des objets alors que les Map stockent un couple clé - valeur.
Si vous insérez fréquemment des données en milieu de liste, utilisez une LinkedList .
Si vous voulez rechercher ou accéder à une valeur via une clé de recherche, optez pour une collection de type Map .
Si vous avez une grande quantité de données à traiter, tournez-vous vers une liste de type Set .
La généricité en Java
Pour assimiler ce concept, ajouté au JDK depuis la version 1.5, nous allons essentiellement 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'objets ou de données de façon dynamique !
Avec ce que nous avons appris au chapitre précédent, vous avez sûrement poussé un soupir de soulagement lorsque vous avez vu que ces objets acceptent
tous les 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 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 !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Principe de base
Bon, pour vous montrer la puissance de la généricité, nous allons tout de suite voir un cas de classe qui ne l'utilise pas.
Il existe un exemple très simple que vous pourrez retrouver aisément sur Internet, car il s'agit d'un des cas les plus faciles permettant d'illustrer 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 de cette dernière en
figure suivante.
Vous pouvez voir que le code de cette classe est très rudimentaire. On a ecte une valeur, on peut la mettre à jour et la récupérer… Maintenant, si je vous demande
de me faire une classe qui permet de travailler avec n'importe quel type de données, j'ai une vague idée de ce que vous allez faire. Ne serait-ce pas quelque chose
s'approchant de la figure suivante ?
J'en étais sûr. Créez la classe Solo , ainsi qu'une classe avec une méthode main . Si vous voulez utiliser les données de l'objet Solo , vous allez devoir faire un
cast . Testez ce code dans votre main :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Vous constatez que vous essayez vainement de mettre un objet de type Object dans un objet de type Integer : c'est interdit ! La classe Object est plus
globale que la classe Integer , vous ne pouvez donc pas e ectuer cette opération, sauf si vous castez votre objet en Integer comme ceci :
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 donc
sans doute tentés d'écrire une classe par type de donnée ( SoloInt , SoloString , etc.). Et c'est là que la généricité s'avère utile, car avec cette dernière, vous
pourrez savoir ce que contient votre objet Solo et n'aurez qu'une seule classe à développer ! Voilà le diagramme de classe de cet objet en figure suivante.
Objet générique
//Variable d'instance
private T valeur;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link] = val;
}
Impressionnant, n'est-ce pas ? Dans cette classe, le T n'est pas encore défini. Vous vous en occuperez à l'instanciation de la 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 :
… ou encore ceci :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](12.2f);
//Ici, on essaie de mettre un nombre à virgule flottante à la place d'un entier
}
… vous obtiendrez une erreur dans la zone de saisie. Ceci vous indique que votre objet ne reçoit pas le bon type d'argument, il y a donc un 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 dans 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 ce que j'avance
:
Vous avez certainement remarqué que je n'ai pas utilisé ici les types de données que vous employez pour déclarer des variables de type primitif ! Ce sont les
classes de ces types primitifs.
En e et, lorsque vous déclarez une variable de type primitif, vous pouvez utiliser ses classes enveloppes (on parle aussi de classe wrapper) ; elles ajoutent les
méthodes de la classe Object à vos types primitifs ainsi que des méthodes permettant de caster leurs valeurs, etc. À ceci, je dois ajouter que depuis Java 5, est
géré ce qu'on appelle l' autoboxing , une fonctionnalité du langage permettant de transformer automatiquement un type primitif en classe wrapper (on appelle
ça le boxing ) et inversement, c'est-à-dire une classe wrapper en type primitif (ceci s'appelle l' unboxing ). Ces deux fonctionnalités forment l' autoboxing . Par
exemple :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//…
}
//Retourne la valeur T
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public T getValeur1() {
return valeur1;
}
//Définit la valeur T
public void setValeur1(T valeur1) {
this.valeur1 = valeur1;
}
//Retourne la valeur S
public S getValeur2() {
return valeur2;
}
//Définit la valeur S
public void setValeur2(S valeur2) {
this.valeur2 = valeur2;
}
}
Vous voyez que cette classe prend deux types de références qui ne sont pas encore définis.
Afin de mieux comprendre son fonctionnement, voici un code que vous pouvez tester :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Test de la classe Duo
Vous voyez qu'il n'y a rien de bien méchant ici. Ce principe fonctionne exactement comme dans l'exemple précédent. La seule di érence réside 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 !
… vous violez la contrainte que vous avez émise lors de la déclaration du type de référence ! Vous ne pourrez donc pas modifier la déclaration générique d'un
objet. Donc si vous suivez bien, on va pouvoir encore corser la chose !
Généricité et collections
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 e et, lorsque vous listiez le contenu d'un ArrayList par exemple, vous n'étiez jamais sûrs à 100 % du type de référence sur lequel vous alliez tomber
(normal, puisqu'un ArrayList accepte tous les types d'objets)… Eh bien ce calvaire est terminé et le polymorphisme va pouvoir réapparaître, plus puissant que
jamais !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Voyez comment utiliser la généricité avec les collections :
[Link]("Liste de String");
[Link]("------------------------------");
List<String> listeString= new ArrayList<String>();
[Link]("Une chaîne");
[Link]("Une autre");
[Link]("Encore une autre");
[Link]("Allez, une dernière");
[Link]("\nListe de float");
[Link]("------------------------------");
for(float f : listeFloat)
[Link](f);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
ArrayList et généricité
La généricité sur les listes est régie par les lois vues précédemment : pas de type float dans un ArrayList<String> .
Héritage et généricité
Là où les choses sont pernicieuses, c'est quand vous employez des classes usant de la généricité avec des objets comprenant la notion d'héritage ! L'héritage dans
la généricité est l'un des concepts les plus complexes en Java. Pourquoi ? Tout simplement parce qu'il va à l'encontre de ce que vous avez appris jusqu'à
présent…
Hiérarchie de classes
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static void main(String[] args) {
List<Voiture> listVoiture = new ArrayList<Voiture>();
List<VoitureSansPermis> listVoitureSP = new ArrayList<VoitureSansPermis>();
Si vous avez l'habitude de la covariance des variables, sachez que cela n'existe pas avec la généricité ! En tout cas, pas sous la même forme.
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'y ajouter une voiture. Là où le problème prend toute son envergure, c'est lorsque vous voudrez sortir toutes les voitures sans permis de votre variable
listVoiture . Eh oui ! Vous y avez ajouté une voiture ! Lors du balayage de la liste, vous aurez, à un moment, une référence de type VoitureSansPermis à
laquelle vous tentez d'a ecter une référence de type Voiture . Voilà pourquoi ceci est interdit.
Une des solutions consiste à utiliser le wildcard : « ? ». Le fait de déclarer une collection avec le wildcard , comme ceci :
ArrayList<?> list;
… revient à indiquer que notre collection accepte n'importe quel type d'objet. Cependant, nous allons voir un peu plus loin qu'il y a une restriction.
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 l’élargir ! 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 !
Une application de ceci consiste à écrire des méthodes génériques, par exemple une méthode qui permet de lister toutes les valeurs de notre ArrayList cité
précédemment :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static void main(String[] args) {
//Méthode générique !
static void afficher(ArrayList<? extends Voiture> list){
for(Voiture v : list)
[Link]([Link]());
}
Eh, attends ! On a voulu ajouter des objets dans notre collection et le programme ne compile plus !
Oui… Ce que je ne vous avais pas dit, c'est que dès que vous utilisez le wildcard , vos listes sont verrouillées en insertion : elles se transforment en collections
en lecture seule..
En fait, il faut savoir que c'est à la compilation du programme que Java ne vous laisse pas faire : le wildcard signifie « tout objet », et dès l'utilisation de celui-ci,
la JVM verrouillera la compilation du programme afin de prévenir les risques d'erreurs. Dans notre exemple, il est combiné avec extends (signifiant héritant),
mais cela n'a pas d'incidence directe : c'est le wildcard la cause du verrou (un objet générique comme notre objet Solo déclaré Solo<?> solo; sera
également bloqué en écriture).
//Liste de voiture
List<Voiture> listVoiture = new ArrayList<Voiture>();
[Link](new Voiture());
[Link](new Voiture());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](new VoitureSansPermis());
affiche(listVoiture);
affiche(listVoitureSP);
}
//Avec cette méthode, on accepte aussi bien les collections de Voiture que les collection de VoitureSansPermis
static void affiche(List<? extends Voiture> list){
for(Voiture v : list)
[Link]([Link]());
}
Avant que vous ne posiez la question, non, déclarer la méthode affiche(List<Voiture> list) {…} ne vous permet pas de parcourir des listes de
VoitureSansPermis , même si celle-ci hérite de la classe Voiture .
Les méthodes déclarées avec un type générique sont verrouillées afin de n'être utilisées qu'avec ce type bien précis, toujours pour les mêmes raisons ! Attendez :
ce n'est pas encore tout. Nous avons vu comment élargir le contenu de nos collections (pour la lecture), nous allons voir comment restreindre les collections
acceptées par nos méthodes.
La méthode :
… autorise n'importe quel objet de type List dont Voiture est la superclasse.
La signification de l'instruction suivante est donc que la méthode autorise un objet de type List de n'importe quelle superclasse de la classe Voiture (y
compris Voiture elle-même).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ce code fonctionne donc parfaitement :
affiche(listVoiture);
}
//Avec cette méthode, on accepte aussi bien les collections de Voiture que les collections d'Object : superclasse de toutes les
classes
L'utilité du wildcard est surtout de permettre de retrouver le polymorphisme avec les collections. Afin de mieux cerner l'intérêt de tout cela, voici un petit
exemple de code :
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
for(Voiture v : listVoiture)
[Link](v);
Essayez donc : ce code fonctionne parfaitement et vous permettra de constater que le polymorphisme est possible avec les collections. Je conçois bien que ceci
est un peu di icile à comprendre, mais vous en aurez sûrement besoin dans une de vos prochaines applications !
La généricité est un concept très utile pour développer des objets travaillant avec plusieurs types de données.
Vous passerez donc moins de temps à développer des classes traitant de façon identique des données di érentes.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Le wildcard (?) permet d'indiquer que n'importe quel type peut être traité et donc accepté !
Dès que le wildcard (?) est utilisé, cela revient à rendre ladite collection en lecture seule !
Vous pouvez élargir le champ d'acceptation d'une collection générique grâce au mot-clé extends .
L'instruction ? extends MaClasse autorise toutes les collections de classes ayant pour supertype MaClasse .
L'instruction ? super MaClasse autorise toutes les collections de classes ayant pour type MaClasse et tous ses supertypes !
Pour ce genre de cas, les méthodes génériques sont particulièrement adaptées et permettent d'utiliser le polymorphisme dans toute sa splendeur !
les objets travaillant avec des flux d'entrée ( in ), pour la lecture de flux ;
les objets travaillant avec des flux de sortie ( out ), pour l'écriture de flux.
Utilisation de [Link]
L'objet File
Avant de commencer, créez un fichier avec l'extension que vous voulez et enregistrez-le à la racine de votre projet Eclipse. Personnellement, je me suis fait un
fichier [Link] dont voici le contenu :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Voici une ligne de test.
Voici une autre ligne de test.
Et comme je suis motivé, en voici une troisième !
Dans votre projet Eclipse, faites un clic droit sur le dossier de votre projet, puis New > File . Vous pouvez nommer votre fichier ainsi qu'y taper du texte !
Le nom du dossier contenant mon projet s'appelle « IO » et mon fichier texte est à cette adresse : D:\Mes documents\Codage\SDZ\Java-SDZ\IO [Link] .
Nous allons maintenant voir ce dont l'objet File est capable. Vous remarquerez que cet objet est très simple à utiliser et que ses méthodes sont très explicites.
if((i%4) == 0){
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("\n");
}
i++;
}
[Link]("\n");
} catch (NullPointerException e) {
//L'instruction peut générer une NullPointerException
//s'il n'y a pas de sous-fichier !
}
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous conviendrez que les méthodes de cet objet peuvent s'avérer très utiles ! Nous venons d'en essayer quelques-unes et nous avons même listé les sous-fichiers
et sous-dossiers de nos lecteurs à la racine du PC.
Vous pouvez aussi e acer le fichier grâce la méthode delete() , créer des répertoires avec la méthode mkdir() (le nom donné à ce répertoire ne pourra
cependant pas contenir de point (« . »)) etc.
Maintenant que vous en savez un peu plus sur cet objet, nous pouvons commencer à travailler avec notre fichier !
C'est par le biais des objets FileInputStream et FileOutputStream que nous allons pouvoir :
Ces classes héritent des classes abstraites InputStream et OutputStream , présentes dans le package [Link] .
Comme vous l'avez sans doute deviné, il existe une hiérarchie de classes pour les traitements in et une autre pour les traitements out . Ne vous y trompez pas,
les classes héritant d' InputStream sont destinées à la lecture et les classes héritant d' OutputStream se chargent de l'écriture !
Vous auriez dit le contraire ? Comme beaucoup de gens au début. Mais c'est uniquement parce que vous situez les flux par rapport à vous, et non à votre
programme ! Lorsque ce dernier va lire des informations dans un fichier, ce sont des informations qu'il reçoit, et par conséquent, elles s'apparentent à une entrée :
in (sachez tout de même que lorsque vous tapez au clavier, cette action est considérée comme un flux d'entrée !).
Au contraire, lorsqu'il va écrire dans un fichier (ou à l'écran, souvenez-vous de [Link] ), par exemple, il va faire sortir des informations ; donc,
pour lui, ce flux de données correspond à une sortie : out .
Nous allons enfin commencer à travailler avec notre fichier. Le but est d'aller en lire le contenu et de le copier dans un autre, dont nous spécifierons le nom dans
notre programme, par le biais d'un programme Java.
Ce code est assez compliqué, donc accrochez-vous à vos claviers !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static void main(String[] args) {
// Nous déclarons nos objets en dehors du bloc try/catch
FileInputStream fis = null;
FileOutputStream fos = null;
try {
// On instancie nos objets :
// fis va lire le fichier
// fos va écrire dans le nouveau !
fis = new FileInputStream(new File("[Link]"));
fos = new FileOutputStream(new File("[Link]"));
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
[Link]("Copie terminée !");
} catch (FileNotFoundException e) {
// Cette exception est levée si l'objet FileInputStream ne trouve
// aucun fichier
[Link]();
} catch (IOException e) {
// Celle-ci se produit lors d'une erreur d'écriture ou de lecture
[Link]();
} finally {
// On ferme nos flux de données dans un bloc finally pour s'assurer
// que ces instructions seront exécutées dans tous les cas même si
// une exception est levée !
try {
if (fis != null)
[Link]();
} catch (IOException e) {
[Link]();
}
try {
if (fos != null)
[Link]();
} catch (IOException e) {
[Link]();
}
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Pour que l'objet FileInputStream fonctionne, le fichier doit exister ! Sinon l'exception FileNotFoundException est levée. Par contre, si vous ouvrez
un flux en écriture ( FileOutputStream ) vers un fichier inexistant, celui-ci sera créé automatiquement !
Notez bien les imports pour pouvoir utiliser ces objets. Mais comme vous le savez déjà, vous pouvez taper votre code et faire ensuite CTRL + SHIFT + O pour
que les imports soient automatiques.
À l'exécution de ce code, vous pouvez voir que le fichier [Link] a bien été créé et qu'il contient exactement la même chose que [Link] ! De plus, j'ai
ajouté dans la console les données que votre programme va utiliser (lecture et écriture).
La figure suivante représente le résultat de ce code.
Copie de fichier
Le bloc finally permet de s'assurer que nos objets ont bien fermé leurs liens avec leurs fichiers respectifs, ceci afin de permette à Java de détruire ces objets
pour ainsi libérer un peu de mémoire à votre ordinateur.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
En e et, les objets utilisent des ressources de votre ordinateur que Java ne peut pas libérer de lui-même, vous devez être sûr que la vanne est fermée !
Ainsi, même si une exception est levée, le contenu du bloc finally sera exécuté et nos ressources seront libérées. Par contre, pour alléger la lecture, je
ne mettrai plus ces blocs dans les codes à venir mais pensez bien à les mettre dans vos codes.
Les objets FileInputStream et FileOutputStream sont assez rudimentaires, car ils travaillent avec un nombre déterminé d'octets à lire. Cela explique
pourquoi ma condition de boucle était si tordue…
Lorsque vous voyez des caractères dans un fichier ou sur votre écran, ils ne veulent pas dire grand-chose pour votre PC, car il ne comprend que le binaire (vous
savez, les suites de 0 et de 1). Ainsi, afin de pouvoir a icher et travailler avec des caractères, un système d'encodage (qui a d'ailleurs fort évolué) a été mis au
point.
Sachez que chaque caractère que vous saisissez ou que vous lisez dans un fichier correspond à un code binaire, et ce code binaire correspond à un code décimal.
Voyez la table de correspondance (on parle de la table ASCII).
Cependant, au début, seuls les caractères de a à z, de A à Z et les chi res de 0 à 9 (les 127 premiers caractères de la table ASCII) étaient codés (UNICODE 1),
correspondant aux caractères se trouvant dans la langue anglaise. Mais ce codage s'est rapidement avéré trop limité pour des langues comportant des caractères
accentués (français, espagnol…). Un jeu de codage de caractères étendu a donc été mis en place afin de pallier ce problème.
Chaque code binaire UNICODE 1 est codé sur 8 bits, soit 1 octet. Une variable de type byte , en Java, correspond en fait à 1 octet et non à 1 bit !
Les objets que nous venons d'utiliser emploient la première version d'UNICODE 1 qui ne comprend pas les caractères accentués, c'est pourquoi ces caractères ont
un code décimal négatif dans notre fichier. Lorsque nous définissons un tableau de byte à 8 entrées, cela signifie que nous allons lire 8 octets à la fois.
Vous pouvez voir qu'à chaque tour de boucle, notre tableau de byte contient huit valeurs correspondant chacune à un code décimal qui, lui, correspond à un
caractère (valeur entre parenthèses à côté du code décimal).
Vous pouvez voir que les codes décimaux négatifs sont inconnus, car ils sont représentés par des « ? » ; de plus, il y a des caractères invisibles (les 32 premiers
caractères de la table ASCII sont invisibles !) dans notre fichier :
Vous voyez que les traitements des flux suivent une logique et une syntaxe précises ! Lorsque nous avons copié notre fichier, nous avons récupéré un certain
nombre d'octets dans un flux entrant que nous avons passé à un flux sortant. À chaque tour de boucle, les données lues dans le fichier source sont écrites dans le
fichier défini comme copie.
Il existe à présent des objets beaucoup plus faciles à utiliser, mais qui travaillent néanmoins avec les deux objets que nous venons d'étudier. Ces objets font
également partie de la hiérarchie citée précédemment. Seulement, il existe une superclasse qui les définit.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les objets FilterInputStream et FilterOutputStream
Ces deux classes sont en fait des classes abstraites. Elles définissent un comportement global pour leurs classes filles qui, elles, permettent d'ajouter des
fonctionnalités aux flux d'entrée/sortie !
La figure suivante représente un diagramme de classes schématisant leur hiérarchie.
Vous pouvez voir qu'il existe quatre classes filles héritant de FilterInputStream (de même pour FilterOutputStream (les classes dérivant de
FilterOutputStream ont les mêmes fonctionnalités, mais en écriture)):
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
DataInputStream : o re la possibilité de lire directement des types primitifs ( double , char , int ) grâce à des méthodes comme readDouble() ,
readInt() …
BufferedInputStream : cette classe permet d'avoir un tampon à disposition dans la lecture du flux. En gros, les données vont tout d'abord remplir le
tampon, et dès que celui-ci est plein, le programme accède aux données.
Ces classes prennent en paramètre une instance dérivant des classes InputStream (pour les classes héritant de FilterInputStream ) ou de OutputStream
(pour les classes héritant de FilterOutputStream ).
Puisque ces classes acceptent une instance de leur superclasse en paramètre, vous pouvez cumuler les filtres et obtenir des choses de ce genre :
Afin de vous rendre compte des améliorations apportées par ces classes, nous allons lire un énorme fichier texte (3,6 Mo) de façon conventionnelle avec l'objet vu
précédemment, puis grâce à un bu er.
Télécharger le fichier
Récupérez le fichier compressé grâce à un logiciel de compression/décompression et remplacez le contenu de votre fichier [Link] par le contenu de ce
fichier. Maintenant, voici un code qui permet de tester le temps d'exécution de la lecture :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
//On réinitialise
startTime = [Link]();
//Inutile d'effectuer des traitements dans notre boucle
while([Link](buf) != -1);
//On réaffiche
[Link]("Temps de lecture avec BufferedInputStream : " + [Link]() - startTime));
} catch (FileNotFoundException e) {
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]();
} catch (IOException e) {
[Link]();
}
}
}
Et le résultat, visible à la figure suivante, est encore une fois blu ant.
La di érence de temps est vraiment énorme : 1,578 seconde pour la première méthode et 0,094 seconde pour la deuxième ! Vous conviendrez que l'utilisation
d'un bu er permet une nette amélioration des performances de votre code. Faisons donc sans plus tarder le test avec l’écriture :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
try {
fis = new FileInputStream(new File("[Link]"));
fos = new FileOutputStream(new File("[Link]"));
bis = new BufferedInputStream(new FileInputStream(new File("[Link]")));
bos = new BufferedOutputStream(new FileOutputStream(new File("[Link]")));
byte[] buf = new byte[8];
while([Link](buf) != -1){
[Link](buf);
}
//On affiche le temps d'exécution
[Link]("Temps de lecture + écriture avec FileInputStream et FileOutputStream : " + ([Link]
() - startTime));
//On réinitialise
startTime = [Link]();
while([Link](buf) != -1){
[Link](buf);
}
//On réaffiche
[Link]("Temps de lecture + écriture avec BufferedInputStream et BufferedOutputStream : " + ([Link]
eMillis() - startTime));
} catch (FileNotFoundException e) {
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]();
} catch (IOException e) {
[Link]();
}
}
}
Là, la di érence est encore plus nette, comme le montre la figure suivante.
Nous allons donc créer un fichier de toutes pièces pour le lire par la suite.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static void main(String[] args) {
//Nous déclarons nos objets en dehors du bloc try/catch
DataInputStream dis;
DataOutputStream dos;
try {
dos = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(
new File("[Link]"))));
[Link]([Link]());
[Link]([Link]());
[Link]([Link]());
[Link]([Link]());
[Link]([Link]());
[Link]([Link]());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]([Link]());
[Link]([Link]());
} catch (FileNotFoundException e) {
[Link]();
} catch (IOException e) {
[Link]();
}
}
}
Le code est simple, clair et concis. Vous avez pu constater que ce type d'objet ne manque pas de fonctionnalités ! Jusqu'ici, nous ne travaillions qu'avec des types
primitifs, mais il est également possible de travailler avec des objets !
Vous devez savoir que lorsqu'on veut écrire des objets dans des fichiers, on appelle ça la « sérialisation » : c'est le nom que porte l'action de sauvegarder des
objets ! Cela fait quelque temps déjà que vous utilisez des objets et, j'en suis sûr, vous avez déjà souhaité que certains d'entre eux soient réutilisables. Le moment
est venu de sauver vos objets d'une mort certaine ! Pour commencer, nous allons voir comment sérialiser un objet de notre composition.
Voici la classe avec laquelle nous allons travailler :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Package à importer
import [Link];
Qu'est-ce que c'est que cette interface ? Tu n'as même pas implémenté de méthode !
En fait, cette interface n'a pas de méthode à redéfinir : l'interface Serializable est ce qu'on appelle une « interface marqueur ». Rien qu'en implémentant cette
interface dans un objet, Java sait que cet objet peut être sérialisé. Et j'irai même plus loin : si vous n'implémentez pas cette interface dans vos objets, ceux-ci ne
pourront pas être sérialisés ! En revanche, si une superclasse implémente l'interface Serializable , ses enfants seront considérés comme sérialisables.
Vous avez sûrement déjà senti comment vous allez vous servir de ces objets, mais travaillons tout de même sur l’exemple que voici :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Packages à importer afin d'utiliser l'objet File
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
new BufferedInputStream(
new FileInputStream(
new File("[Link]"))));
try {
[Link]("Affichage des jeux :");
[Link]("*************************\n");
[Link](((Game)[Link]()).toString());
[Link](((Game)[Link]()).toString());
[Link](((Game)[Link]()).toString());
} catch (ClassNotFoundException e) {
[Link]();
}
[Link]();
} catch (FileNotFoundException e) {
[Link]();
} catch (IOException e) {
[Link]();
}
}
}
La désérialisation d'un objet peut engendrer une ClassNotFoundException , pensez donc à la capturer !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Sérialisation — désérialisation
Ce qu'il se passe est simple : les données de vos objets sont enregistrées dans le fichier. Mais que se passerait-il si notre objet Game avait un autre objet de votre
composition en son sein ? Voyons ça tout de suite. Créez la classe Notice comme suit :
Nous allons maintenant implémenter une notice par défaut dans notre objet Game . Voici notre classe modifiée :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
Réessayez votre code sauvegardant vos objets Game . La figure suivante nous montre le résultat obtenu.
Erreur de sérialisation
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Eh non, votre code ne compile plus ! Il y a une bonne raison à cela : votre objet Notice n'est pas sérialisable, une erreur de compilation est donc levée.
Maintenant, deux choix s'o rent à vous :
2. soit vous spécifiez dans votre classe Game que la variable notice n'a pas à être sérialisée.
Pour la première option, c'est simple, il su it d'implémenter l'interface sérialisable dans notre classe Notice . Pour la seconde, il su it de déclarer votre variable
: transient ; comme ceci :
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous aurez sans doute remarqué que nous n'utilisons pas la variable notice dans la méthode toString() de notre objet Game . Si vous faites ceci,
que vous sérialisez puis désérialisez vos objets, la machine virtuelle vous renverra l’exception NullPointerException à l'invocation de ladite méthode.
Eh oui ! L'objet Notice est ignoré : il n'existe donc pas !
CharArray(Writer/Reader) ;
String(Writer/Reader) .
Ces deux types jouent quasiment le même rôle. De plus, ils ont les mêmes méthodes que leur classe mère. Ces deux objets n'ajoutent donc aucune nouvelle
fonctionnalité à leur objet mère.
Leur principale fonction est de permettre d'écrire un flux de caractères dans un bu er adaptatif : un emplacement en mémoire qui peut changer de taille selon les
besoins (nous n'en avons pas parlé dans le chapitre précédent afin de ne pas l'alourdir, mais il existe des classes remplissant le même rôle que ces classes-ci :
ByteArray(Input/Output)Stream ).
try {
[Link]("Coucou les Zéros");
//Appel à la méthode toString de notre objet de manière tacite
[Link](caw);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//[Link]() n'a aucun effet sur le flux
//Seul [Link]() peut tout effacer
[Link]();
[Link](str);
} catch (IOException e) {
[Link]();
}
}
}
Je vous laisse le soin d'examiner ce code ainsi que son e et. Il est assez commenté pour que vous en compreniez toutes les subtilités. L'objet
String(Writer/Reader) fonctionne de la même façon :
try {
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("Coucou les Zéros");
//Appel à la méthode toString de notre objet de manière tacite
[Link](sw);
[Link](str);
} catch (IOException e) {
[Link]();
}
}
}
En fait, il s'agit du même code, mais avec des objets di érents ! Vous savez à présent comment écrire un flux de texte dans un tampon de mémoire. Je vous
propose maintenant de voir comment traiter les fichiers de texte avec des flux de caractères.
Comme nous l'avons vu, les objets travaillant avec des flux utilisent des flux binaires.
La conséquence est que même si vous ne mettez que des caractères dans un fichier et que vous le sauvegardez, les objets étudiés précédemment traiteront votre
fichier de la même façon que s’il contenait des données binaires ! Ces deux objets, présents dans le package [Link] , servent à lire et écrire des données dans
un fichier texte.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
try {
//Création de l'objet
fw = new FileWriter(file);
String str = "Bonjour à tous, amis Zéros !\n";
str += "\tComment allez-vous ? \n";
//On écrit la chaîne
[Link](str);
//On ferme le flux
[Link]();
//Affichage
[Link](str);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
} catch (FileNotFoundException e) {
[Link]();
} catch (IOException e) {
[Link]();
}
}
}
Vous pouvez voir que l'a ichage est bon et qu'un nouveau fichier (la lecture d'un fichier inexistant entraîne l’exception FileNotFoundException , et l'écriture
peut entraîner une IOException ) vient de faire son apparition dans le dossier contenant votre projet Eclipse !
Depuis le JDK 1.4, un nouveau package a vu le jour, visant à améliorer les performances des flux, bu ers, etc. traités par [Link] . En e et, vous ignorez
probablement que le package que nous explorons depuis le début existe depuis la version 1.1 du JDK. Il était temps d'avoir une remise à niveau afin d'améliorer
les résultats obtenus avec les objets traitant les flux. C'est là que le package [Link] a vu le jour !
Utilisation de [Link]
Vous l'avez sûrement deviné, nio signifie « New I/O ». Comme je vous l'ai dit précédemment, ce package a été créé afin d'améliorer les performances sur le
traitement des fichiers, du réseau et des bu ers. Il permet de lire les données (nous nous intéresserons uniquement à l'aspect fichier) d'une façon di érente. Vous
avez constaté que les objets du package [Link] traitaient les données par octets. Les objets du package [Link] , eux, les traitent par blocs de données : la
lecture est donc accélérée !
Tout repose sur deux objets de ce nouveau package : les channels et les bu ers. Les channels sont en fait des flux, tout comme dans l'ancien package, mais ils
sont amenés à travailler avec un bu er dont vous définissez la taille. Pour simplifier au maximum, lorsque vous ouvrez un flux vers un fichier avec un objet
FileInputStream , vous pouvez récupérer un canal vers ce fichier. Celui-ci, combiné à un bu er, vous permettra de lire votre fichier encore plus vite qu'avec un
BufferedInputStream !
Reprenez le gros fichier que je vous ai fait créer dans la sous-section précédente : nous allons maintenant le relire avec ce nouveau package en comparant le
bu er conventionnel et la nouvelle façon de faire.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
try {
//Création des objets
fis = new FileInputStream(new File("[Link]"));
bis = new BufferedInputStream(fis);
//Démarrage du chrono
long time = [Link]();
//Lecture
while([Link]() != -1);
//Temps d'exécution
[Link]("Temps d'exécution avec un buffer conventionnel : " + ([Link]() - time));
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Démarrage du chrono
time = [Link]();
//Démarrage de la lecture
[Link](bBuff);
//On prépare à la lecture avec l'appel à flip
[Link]();
//Affichage du temps d'exécution
[Link]("Temps d'exécution avec un nouveau buffer : " + ([Link]() - time));
//Puisque nous avons utilisé un buffer de byte afin de récupérer les données
//Nous pouvons utiliser un tableau de byte
//La méthode array retourne un tableau de byte
byte[] tabByte = [Link]();
} catch (FileNotFoundException e) {
[Link]();
} catch (IOException e) {
[Link]();
}
}
}
Vous constatez que les gains en performance ne sont pas négligeables. Sachez aussi que ce nouveau package est le plus souvent utilisé pour traiter les flux
circulant sur les réseaux. Je ne m'attarderai pas sur le sujet, mais une petite présentation est de mise. Ce package o re un bu er par type primitif pour la lecture
sur le channel, vous trouverez donc ces classes :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
IntBuffer ;
CharBuffer ;
ShortBuffer ;
ByteBuffer ;
DoubleBuffer ;
FloatBuffer ;
LongBuffer .
Je ne l'ai pas fait durant tout le chapitre afin d'alléger un peu les codes, mais si vous voulez être sûrs que votre flux est bien fermé, utilisez la clause finally . Par
exemple, faites comme ceci :
try {
//On travaille avec nos objets
} catch (FileNotFoundException e) {
//Gestion des exceptions
} catch (IOException e) {
//Gestion des exceptions
}
finally{
if(ois != null)[Link]();
if(oos != null)[Link]();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Avec l'arrivée de Java 7, quelques nouveautés ont vu le jour pour la gestion des exceptions sur les flux. Contrairement à la gestion de la mémoire (vos variables,
vos classes, etc.) qui est déléguée au garbage collector (ramasse miette), plusieurs types de ressources doivent être gérées manuellement. Les flux sur des fichiers
en font parti mais, d'un point de vue plus général, toutes les ressources que vous devez fermer manuellement (les flux réseaux, les connexions à une base de
données…). Pour ce genre de flux, vous avez vu qu'il vous faut déclarer une variable en dehors d'un bloc try{…}catch{…} afin qu'elle soit accessible dans les
autres blocs d'instructions, le bloc finally par exemple.
Java 7 initie ce qu'on appelle vulgairement le « try-with-resources ». Ceci vous permet de déclarer les ressources utilisées directement dans le bloc try(…) , ces
dernières seront automatiquement fermées à la fin du bloc d'instructions ! Ainsi, si nous reprenons notre code de début de chapitre qui copie notre fichier
[Link] vers [Link] , nous aurons ceci :
[Link]("");
}
} catch (IOException e) {
[Link]();
}
Notez bien que les di érentes ressources utilisées sont séparées par un « ; » dans le bloc try !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
C'est tout de même beaucoup plus clair et plus lisible qu'avant, surtout que vous n'avez plus à vous soucier de la fermeture dans le bloc finally . Il faut
cependant prendre quelques précautions notamment pour ce genre de déclaration :
Le fait d'avoir des ressources encapsulées dans d'autres ne rend pas « visible » les ressources encapsulées. Dans le cas précédent, si une exception est levée, le
flux correspondant à l'objet FileInputStream ne sera pas fermé. Pour pallier ce problème il su it de bien découper toutes les ressources à utiliser, comme ceci
:
Eh ! Avant tu utilisais l'objet File dans l'instanciation de tes objets FileInputStream et FileOutputStream !
Rien ne vous échappe ! Si j'ai changé de façon de faire c'est parce qu'il y a une restriction sur ce mode de fonctionnement. Pour rendre la fermeture automatique
possible, les développeurs de la plateforme Java 7 ont créé une nouvelle interface : [Link] . Seuls les objets implémentant cette interface
peuvent être utilisés de la sorte ! Vous pouvez voir la liste des classes autorisées à cette adresse (et vous constaterez que la classe File n'en fait pas parti).
une meilleure gestion des exceptions : la plupart des méthodes de la classe File se contentent de renvoyer une valeur nulle en cas de problème, avec ce
nouveau package, des exceptions seront levées permettant de mieux cibler la cause du (ou des) problème(s) ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
un accès complet au système de fichiers (support des liens/liens symboliques, etc.) ;
l'ajout de méthodes utilitaires tels que le déplacement/la copie de fichier, la lecture/écriture binaire ou texte…
Je vous propose maintenant de jouer avec quelques nouveautés. Commençons par le commencement : ce qui finira par remplacer la classe File . Afin d'être le
plus souple et complet possible, les développeurs de la plateforme ont créé une interface [Link] dont le rôle est de récupérer et manipuler des
chemins de fichiers de dossier et une une classe [Link] qui contient tout un tas de méthodes qui simplifient certaines actions (copie,
déplacement, etc.) et permet aussi de récupérer tout un tas d'informations sur un chemin.
Afin d'illustrer ce nouveau mode de fonctionnement, je vous propose de reprendre le premier exemple de ce chapitre, celui qui a ichait di érentes informations
sur notre fichier de test.
La classe Files vous permet aussi de lister le contenu d'un répertoire mais via un objet DirectoryStream qui est un itérateur. Ceci évite de charger tous les
fichiers en mémoire pour récupérer leurs informations. Voici comment procéder :
//On récupère maintenant la liste des répertoires dans une collection typée
//Via l'objet FileSystem qui représente le système de fichier de l'OS hébergeant la JVM
Iterable<Path> roots = [Link]().getRootDirectories();
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
int i = 0;
for(Path nom : listing){
[Link]("\t\t" + (([Link](nom)) ? nom+"/" : nom));
i++;
if(i%4 == 0)[Link]("\n");
}
} catch (IOException e) {
[Link]();
}
}
Vous avez également la possibilité d'ajouter un filtre à votre listing de répertoire afin qu'il ne liste que certains fichiers :
C'est vrai que cela change grandement la façon de faire et elle peut paraître plus complexe. Mais l'objet Files simplifie aussi beaucoup de choses. Voici
quelques exemple de méthodes utilitaires qui, je pense, vont vous séduire.
La copie de fichier
Pour copier le fichier [Link] vers un fichier [Link] , il su it de faire :
Le troisième argument permet de spécifier les options de copie. Voici celles qui sont disponibles :
StandardCopyOption.COPY_ATTRIBUTES : copie les attributs du fichier source sur le fichier cible (droits en lecture etc.) ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
StandardCopyOption.ATOMIC_MOVE : copie atomique ;
Le déplacement de fichier
Pour déplacer le fichier [Link] vers un fichier [Link] , il su it de faire :
//Ouverture en écriture :
try ( OutputStream output = [Link](source) ) { … }
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Pour en savoir plus sur ce que permet la nouvelle classe [Link] , je vous invite à regarder la documentation Java.
Java 7 vous permet également de gérer les fichier ZIP grâce à l'objet FileSystem :
Il est également possible d'être averti via l'objet WatchService lorsqu'un un fichier est modifié, de gérer des entrées/sorties asynchrones via les objets
AsynchronousFileChannel , AsynchronousSocketChannel ou AsynchronousServerSocketChannel . Ceci permet de faire les actions en tâche de fond,
sans bloquer le code pendant l'exécution. Il est aussi possible d'avoir accès aux attributs grâce à 6 vues permettant de voir plus ou moins d'informations, à savoir :
BasicFileAttributeView permet un accès aux propriétés généralement communes à tous les systèmes de fichiers ;
DosFileAttributeView ajoute le support des attributs MS-DOS ( readonly , hidden , system , archive ) à l'objet ci-dessus ;
PosixFileAttributeView ajoute les permissions POSIX du monde Unix au premier objet cité ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
FileOwnerAttributeView permet de manipuler le propriétaire du fichier ;
Le pattern decorator
Vous avez pu remarquer que les objets de ce chapitre utilisent des instances d'objets de même supertype dans leur constructeur. Rappelez-vous cette syntaxe :
La raison d'agir de la sorte est simple : c'est pour ajouter de façon dynamique des fonctionnalités à un objet. En fait, dites-vous qu'au moment de récupérer les
données de notre objet DataInputStream , celles-ci vont d'abord transiter par les objets passés en paramètre. Ce mode de fonctionnement suit une certaine
structure et une certaine hiérarchie de classes : c'est le pattern decorator.
Ce pattern de conception permet d'ajouter des fonctionnalités à un objet sans avoir à modifier son code source. Afin de ne pas trop vous embrouiller avec les
objets étudiés dans ce chapitre, je vais vous fournir un autre exemple, plus simple, mais gardez bien en tête que les objets du package [Link] utilisent ce
pattern. Le but du jeu est d'obtenir un objet auquel nous pourrons ajouter des choses afin de le « décorer »… Vous allez travailler avec un objet Gateau qui
héritera d'une classe abstraite Patisserie . Le but du jeu est de pouvoir ajouter des couches à notre gâteau sans avoir à modifier son code source.
Vous avez vu avec le pattern strategy que la composition (« A un ») est souvent préférable à l'héritage (« Est un ») : vous aviez défini de nouveaux comportements
pour vos objets en créant un supertype d'objet par comportement. Ce pattern aussi utilise la composition comme principe de base : vous allez voir que nos objets
seront composés d'autres objets. La di érence réside dans le fait que nos nouvelles fonctionnalités ne seront pas obtenues uniquement en créant de nouveaux
objets, mais en associant ceux-ci à des objets existants. Ce sera cette association qui créera de nouvelles fonctionnalités !
Nous allons procéder de la façon suivante :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
nous allons aussi lui ajouter une CoucheCaramel ;
Tout cela démarre avec un concept fondamental : l'objet de base et les objets qui le décorent doivent être du même type, et ce, toujours pour la même raison, le
polymorphisme, le polymorphisme, et le polymorphisme !
Vous allez comprendre. En fait, les objets qui vont décorer notre gâteau posséderont la même méthode preparer() que notre objet principal, et nous allons
faire fondre cet objet dans les autres. Cela signifie que nos objets qui vont servir de décorateurs comporteront une instance de type Patisserie ; ils vont
englober les instances les unes après les autres et du coup, nous pourrons appeler la méthode preparer() de manière récursive !
Vous pouvez voir les décorateurs comme des poupées russes : il est possible de mettre une poupée dans une autre. Cela signifie que si nous décorons notre
gateau avec un objet CoucheChocolat et un objet CoucheCaramel , la situation pourrait être symbolisée par la figure suivante.
L'objet CoucheCaramel contient l'instance de la classe CoucheChocolat qui, elle, contient l'instance de Gateau : en fait, on va passer notre instance d'objet
en objet ! Nous allons ajouter les fonctionnalités des objets « décorants » en appelant la méthode preparer() de l'instance se trouvant dans l'objet avant
d'e ectuer les traitements de la même méthode de l'objet courant, comme à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Invocation des méthodes
Nous verrons, lorsque nous parlerons de la classe Thread , que ce système ressemble fortement à la pile d'invocations de méthodes. La figure suivante montre à
quoi ressemble le diagramme de classes de notre exemple.
Diagramme de classes
Vous remarquez sur ce diagramme que notre classe mère Patisserie est en fait la strategy (une classe encapsulant un comportement fait référence au pattern
strategy : on peut dire qu'elle est la strategy de notre hiérarchie) de notre structure, c'est pour cela que nous pourrons appeler la méthode preparer() de façon
récursive afin d'ajouter des fonctionnalités à nos objets. Voici les di érentes classes que j'ai utilisées (je n'ai utilisé que des String afin de ne pas surcharger les
sources, et pour que vous vous focalisiez plus sur la logique que sur le code).
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]
[Link]
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]
[Link]
Et voici un code de test ainsi que son résultat, représenté à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Résultat du test
J'ai agrémenté l'exemple d'une couche de biscuit, mais je pense que tout cela est assez représentatif de la façon dont fonctionnent des flux d'entrée/sortie en
Java. Vous devriez réussir à saisir tout cela sans souci. Le fait est que vous commencez maintenant à avoir en main des outils intéressants pour programmer, et
c'est sans compter les outils du langage : vous venez de mettre votre deuxième pattern de conception dans votre mallette du programmeur.
Vous avez pu voir que l'invocation des méthodes se faisait en allant jusqu'au dernier élément pour remonter ensuite la pile d'invocations. Pour inverser ce
fonctionnement, il vous su it d'inverser les appels dans la méthode preparer() : a ecter d'abord le nom de la couche et ensuite le nom du décorateur.
Les classes que nous avons étudiées dans ce chapitre sont héritées des classes suivantes :
La façon dont on travaille avec des flux doit respecter la logique suivante :
ouverture de flux ;
lecture/écriture de flux ;
fermeture de flux.
La gestion des flux peut engendrer la levée d'exceptions : FileNotFoundException , IOException etc.
Si un objet sérialisable comporte un objet d'instance non sérialisable, une exception sera levée lorsque vous voudrez sauvegarder votre objet.
L'une des solutions consiste à rendre l'objet d'instance sérialisable, l'autre à le déclarer transient afin qu'il soit ignoré à la sérialisation.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
L'utilisation de bu ers permet une nette amélioration des performances en lecture et en écriture de fichiers.
Afin de pouvoir ajouter des fonctionnalités aux objets gérant les flux, Java utilise le pattern « decorator ».
Ce pattern permet d'encapsuler une fonctionnalité et de l'invoquer de façon récursive sur les objets étant composés de décorateurs.
Java et la réflexivité
La réflexivité, aussi appelée introspection, consiste à découvrir de façon dynamique des informations relatives à une classe ou à un objet. C'est notamment
utilisé au niveau de la machine virtuelle Java lors de l'exécution du programme. En gros, la machine virtuelle stocke les informations relatives à une classe
dans un objet.
La réflexivité n'est 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 à cette notion.
Ce chapitre va sûrement vous intéresser ! Alors, allons-y…
L'objet Class
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 : 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 permettant d'obtenir 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 :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Maintenant que vous savez récupérer un objet Class , nous allons voir ce dont il est capable. Nous n'allons examiner qu'une partie des fonctionnalités de l'objet
Class : je ne vais pas tout vous montrer, je pense que vous êtes dorénavant à même de chercher et de trouver tout seuls. Vous avez l'habitude de manipuler des
objets, à présent.
Notez que la classe Object n'a pas de superclasse. Normal, puisqu'elle se trouve au sommet de la hiérarchie. Donc si vous remplacez la classe String de
l'exemple précédent par la classe Object , vous devriez obtenir :
En plus de ça, l'objet Class permet de connaître la façon dont votre objet est constitué : interfaces, classe mère, variables…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
for(int i = 0; i < [Link]; i++)
[Link](faces[i]);
}
la figure suivante vous montre un morceau du résultat. Comme vous pouvez le constater, il y a beaucoup de méthodes dans la classe String .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Méthodes de la classe String
Vous pouvez constater que l'objet Method regorge lui aussi de méthodes intéressantes. Voici un code qui a iche la liste des méthodes, ainsi que celle de leurs
arguments respectifs :
Class[] p = m[i].getParameterTypes();
for(int j = 0; j < [Link]; j++)
[Link](p[j].getName());
[Link]("----------------------------------\n");
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Le résultat est visible sur la figure suivante. Il est intéressant de voir que vous obtenez toutes sortes d'informations sur les méthodes, leurs paramètres, les
exceptions levées, leur type de retour, etc.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
for(int i = 0; i < [Link]; i++)
[Link](m[i].getName());
}
[Link]("-----------------------------\n");
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous constatez que l'objet Class regorge de méthodes en tout genre !
Et si nous essayions 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 au diagramme en figure suivante.
Classe Paire
public Paire(){
this.valeur1 = null;
this.valeur2 = null;
[Link]("Instanciation !");
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public Paire(String val1, String val2){
this.valeur1 = val1;
this.valeur2 = val2;
[Link]("Instanciation avec des paramètres !");
}
Le but du jeu consiste à créer un objet Paire sans utiliser l'opérateur new .
Pour instancier un nouvel objet Paire , commençons par récupérer ses constructeurs. Ensuite, nous préparons un tableau contenant les données à insérer, puis
invoquons la méthode toString() .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static void main(String[] args) {
String nom = [Link]();
try {
//On crée un objet Class
Class cl = [Link](nom);
//Nouvelle instance de la classe Paire
Object o = [Link]();
} catch (SecurityException e) {
[Link]();
} catch (IllegalArgumentException e) {
[Link]();
} catch (ClassNotFoundException e) {
[Link]();
} catch (InstantiationException e) {
[Link]();
} catch (IllegalAccessException e) {
[Link]();
} catch (NoSuchMethodException e) {
[Link]();
} catch (InvocationTargetException e) {
[Link]();
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Et le résultat donne la figure suivante.
Instanciation dynamique
Nous pouvons maintenant appeler la méthode toString() du deuxième objet… Oh et puis soyons fous, appelons-la sur les deux :
[Link]("----------------------------------------");
[Link]("Méthode " + [Link]() + " sur o2: " +[Link](o2, null));
[Link]("Méthode " + [Link]() + " sur o: " +[Link](o, null));
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
} catch (SecurityException e) {
[Link]();
} catch (IllegalArgumentException e) {
[Link]();
} catch (ClassNotFoundException e) {
[Link]();
} catch (InstantiationException e) {
[Link]();
} catch (IllegalAccessException e) {
[Link]();
} catch (NoSuchMethodException e) {
[Link]();
} catch (InvocationTargetException e) {
[Link]();
}
}
Voilà : nous venons de créer deux instances d'une classe sans passer par l'opérateur new . Mieux encore, nous avons pu appeler une méthode de nos instances !
Je ne vais pas m'attarder sur ce sujet, mais gardez en tête que cette façon de faire, quoique très lourde, pourrait vous être utile.
Sachez que certains frameworks (ensemble d'objets o rant des fonctionnalités pour développer) utilisent la réflexivité afin d'instancier leurs objets (je pense
notamment à des frameworks basés sur des fichiers de configuration XML tels que Hibernate, Struts, Spring…). L'utilité de ceci vous semble-t-elle évidente ?
Même si vous ne savez pas les utiliser, peut-être avez-vous déjà entendu parler de ces frameworks Java.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Maintenant, vous n'allez pas utiliser ce genre de technique tous les jours. Cependant, il est possible que vous ayez besoin, pour une raison quelconque, de stocker
le nom d'une classe Java dans une base de données afin, justement, de pouvoir l'utiliser plus tard. Dans ce cas, lorsque votre base de données vous fournira le
nom de la classe en question, vous pourrez la manipuler dynamiquement.
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 tels que Method , Field et Constructor qui permettent de travailler avec vos di érents objets ainsi qu'avec ceux
présents dans Java.
Grâce à cet objet, vous pouvez créer des instances de vos classes Java sans utiliser new .
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 !
Le fonctionnement de base des IHM vous sera également présenté et vous apprendrez qu'en réalité, une fenêtre n'est qu'une multitude de composants
posés les uns sur les autres et que chacun possède un rôle qui lui est propre.
Mais trêve de bavardages inutiles, commençons tout de suite !
L'objet JFrame
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Avant de nous lancer à corps perdu dans cette partie, vous devez savoir de quoi nous allons nous servir. Dans ce cours, nous traiterons de [Link] et de
[Link] . Nous n'utiliserons pas de composants awt , nous travaillerons uniquement avec des composants swing ; en revanche, des objets issus du package
awt seront utilisés afin d'interagir et de communiquer avec les composants swing . Par exemple, un composant peut être représenté par un bouton, une zone
de texte, une case à cocher, etc.
Afin de mieux comprendre comment tout cela fonctionne, vous devez savoir que lorsque le langage Java a vu le jour, dans sa version 1.0, seul awt était utilisable
; swing n'existait pas, il est apparu dans la version 1.2 de Java (appelée aussi Java 2). Les composants awt sont considérés comme lourds (on dit aussi
HeavyWeight ) car ils sont fortement liés au système d'exploitation, c'est ce dernier qui les gère. Les composants swing , eux, sont comme dessinés dans un
conteneur, ils sont dit légers (on dit aussi LightWeight ) ; ils n'ont pas le même rendu à l'a ichage, car ce n'est plus le système d'exploitation qui les gère. Il
existe également d'autres di érences, comme le nombre de composants utilisables, la gestion des bordures...
Pour toutes ces raisons, il est très fortement recommandé de ne pas mélanger les composants swing et awt dans une même fenêtre ; cela pourrait occasionner
des conflits ! Si vous associez les deux, vous aurez de très grandes di icultés à développer une IHM stable et valide. En e et, swing et awt ont les mêmes
fondements mais di èrent dans leur utilisation.
Cette parenthèse fermée, nous pouvons entrer dans le vif du sujet. Je ne vous demande pas de créer un projet contenant une classe main , celui-ci doit être prêt
depuis des lustres ! Pour utiliser une fenêtre de type JFrame , vous devez l'instancier, comme ceci :
import [Link];
Lorsque vous exécutez ce code, vous n'obtenez rien, car par défaut, votre JFrame n'est pas visible. Vous devez donc lui dire « sois visible » de cette manière :
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ainsi, lorsque vous exécutez ce code, vous obtenez la figure suivante.
Première fenêtre
À toutes celles et ceux qui se disent que cette fenêtre est toute petite, je réponds : « 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.
Pour obtenir une fenêtre plus conséquente, il faudrait donc :
que notre programme s'arrête réellement lorsqu'on clique sur la croix rouge, car, pour ceux qui ne l'auraient pas remarqué, le processus Eclipse tourne
encore même après la fermeture de la fenêtre.
Pour chacun des éléments que je viens d'énumérer, il y a aura une méthode à appeler afin que notre JFrame sache à quoi s'en tenir. Voici d'ailleurs un code
répondant à toutes nos exigences :
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](400, 100);
//Nous demandons maintenant à notre objet de se positionner au centre
[Link](null);
//Termine le processus lorsqu'on clique sur la croix rouge
[Link](JFrame.EXIT_ON_CLOSE);
//Et enfin, la rendre visible
[Link](true);
}
}
Afin de ne pas avoir à redéfinir les attributs à chaque fois, je pense qu'il serait utile que nous possédions notre propre objet. Comme ça, nous aurons notre propre
classe !
Pour commencer, e açons tout le code que nous avons écrit dans notre méthode main . Créons ensuite une classe que nous allons appeler Fenetre et faisons-
la hériter de JFrame . Nous allons maintenant créer notre constructeur, dans lequel nous placerons nos instructions.
Cela nous donne :
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Ensuite, vous avez le choix : soit vous conservez votre classe contenant la méthode main et vous créez une instance de Fenetre , soit vous e acez cette classe
et vous placez votre méthode main dans votre classe Fenetre . Mais dans tous les cas, vous devez créer une instance de votre Fenetre . Personnellement, je
préfère placer ma méthode main dans une classe à part… Mais je ne vous oblige pas à faire comme moi ! Quel que soit l'emplacement de votre main , la ligne
de code suivante doit y figurer :
Exécutez votre nouveau code, et… vous obtenez 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êmes instructions à chaque fois. Ainsi, vous possédez une classe qui va se charger de l'a ichage de votre futur programme. Et voici une petite liste
de méthodes que vous serez susceptibles d'utiliser.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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 tandis que les
négatives la font sortir de l'écran par la gauche. La même règle s'applique aux valeurs de l'axe y, si ce n'est que les valeurs positives font descendre la fenêtre
depuis l'origine tandis que les négatives la font sortir par le haut de l'écran.
Je ne vais pas faire le tour de toutes les méthodes maintenant, car de toute façon, nous allons nous servir de bon nombre d'entre elles très
[Link], je suppose que vous aimeriez bien remplir un peu votre fenêtre. Je m'en doutais, mais avant il vous faut encore apprendre une
bricole. En e et, votre fenêtre, telle qu'elle apparaît, vous cache quelques petites choses !
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 superposées, comme le montre la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Structure d'une JFrame
la fenêtre ;
le RootPane (en vert), le conteneur principal qui contient les autres composants ;
le LayeredPane (en violet), qui forme juste un panneau composé du conteneur global et de la barre de menu ( MenuBar ) ;
le content pane (en rose) : c'est dans celui-ci que nous placerons nos composants ;
le GlassPane (en transparence), couche utilisée pour intercepter les actions de l'utilisateur avant qu'elles ne parviennent aux composants.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Pas de panique, nous allons nous servir uniquement du content pane. Pour le récupérer, il nous su it d'utiliser la méthode getContentPane() de la classe
JFrame . Cependant, nous allons utiliser un composant autre que le content pane : un JPanel dans lequel nous insérerons nos composants.
Il existe d'autres types de fenêtre : la JWindow , une JFrame sans bordure et non draggable (déplaçable), et la JDialog , une fenêtre non
redimensionnable. Nous n'en parlerons toutefois pas ici.
L'objet JPanel
Comme je vous l'ai dit, nous allons utiliser un JPanel , composant de type conteneur dont la vocation est d'accueillir d'autres objets de même type ou des
objets de type composant (boutons, cases à cocher…).
Voici le marche à suivre :
2. Instancier un JPanel puis lui spécifier une couleur de fond pour mieux le distinguer.
3. Avertir notre JFrame que ce sera notre JPanel qui constituera son content pane.
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Instanciation d'un objet JPanel
JPanel pan = new JPanel();
//Définition de sa couleur de fond
[Link]([Link]);
//On prévient notre JFrame que notre JPanel sera son content pane
[Link](pan);
[Link](true);
}
}
Premier JPanel
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 (des boutons, par exemple), nous allons nous amuser avec notre JPanel . Plus
particulièrement avec un objet dont le rôle est de dessiner et de peindre notre composant. Ça vous tente ? Alors, allons-y !
Nous allons commencer par 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é via
la méthode getGraphics() d'un composant swing ! Pour bien comprendre le fonctionnement de nos futurs conteneurs (ou composants), nous allons créer
une classe héritée de JPanel : appelons-la Panneau . Nous allons faire un petit tour d'horizon du fonctionnement de cette classe, dont voici le code :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
Cette méthode est celle que l'objet appelle pour se dessiner sur votre fenêtre ; si vous réduisez cette dernière et que vous l'a ichez de nouveau, c'est encore cette
méthode qui est appelée pour a icher votre composant. Idem si vous redimensionnez votre fenêtre… De plus, nous n'avons même pas besoin de redéfinir un
constructeur car cette méthode est appelée automatiquement !
C'est très pratique pour personnaliser des composants, car vous n'aurez jamais à l'appeler vous-mêmes : c'est automatique ! Tout ce que vous pouvez faire, c'est
forcer l'objet à se repeindre ; ce n'est toutefois pas cette méthode que vous invoquerez, mais 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
[Link](20, 20, 75, 75) , mais vous verrez à quoi elle sert lorsque vous exécuterez votre programme.
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](true);
}
}
Exécutez votre main , vous devriez obtenir la même chose qu'à la figure suivante.
Une fois votre fenêtre a iché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. L'intérêt de disposer d'une classe héritée d'un conteneur ou d'un composant, c'est que nous pouvons
redéfinir la façon dont est peint ce composant sur la fenêtre.
Après cette mise en bouche, explorons un peu plus les capacités de notre objet Graphics . Comme vous avez pu le voir, ce dernier permet, entre autres, de tracer
des ronds ; mais il possède tout un tas de méthodes plus pratiques et amusantes les unes que les autres… Nous ne les étudierons pas toutes, mais vous aurez
déjà de quoi faire.
Pour commencer, reprenons la méthode utilisée précédemment : [Link](20, 20, 75, 75) . Si nous devions traduire cette instruction en français, cela
donnerait : « Trace un rond plein en commençant à dessiner sur l'axe x à 20 pixels et sur l'axe y à 20 pixels, et fais en sorte qu'il occupe 75 pixels de large et 75
pixels de haut. »
Oui, mais si je veux que mon rond soit centré et qu'il le reste ?
C'est dans ce genre de cas qu'il est intéressant d'utiliser une classe héritée. Puisque nous sommes dans notre objet JPanel , nous avons accès à ses données
lorsque nous le dessinons.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
En e et, il existe des méthodes dans les objets composants qui retournent leur largeur ( getWidth() ) et leur hauteur ( getHeight() ). En revanche, réussir à
centrer un rond dans un JPanel en toutes circonstances 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 se trouve en ce moment. Vous pouvez constater que les coordonnées de départ correspondent au coin supérieur gauche du
carré qui entoure ce cercle, comme le montre la figure suivante.
Cela signifie que si nous voulons que notre cercle soit tout le temps centré, il faut que notre carré soit centré, donc que le centre de celui-ci corresponde au centre
de notre fenêtre ! La figure suivante est un schéma représentant ce que nous devons obtenir.
Coordonnées recherchées
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ainsi, le principe est d'utiliser la largeur et la hauteur de notre composant ainsi que la largeur et la hauteur du carré qui englobe notre rond ; c'est facile, jusqu'à
présent…
Maintenant, pour trouver où se situe le point depuis lequel doit commencer le dessin, il faut soustraire la moitié de la largeur du composant à la moitié de celle du
rond afin d'obtenir la valeur sur l'axe x, et faire de même (en soustrayant les hauteurs, cette fois) pour l'axe y. Afin que notre rond soit le plus optimisé possible,
nous allons donner comme taille à notre carré la moitié de la taille de notre fenêtre ; ce qui revient, au final, à diviser la largeur et la hauteur de cette dernière par
quatre. Voici le code correspondant :
import [Link];
import [Link];
Si vous testez à nouveau notre code, vous vous apercevez que notre rond est maintenant centré. Cependant, l'objet Graphics permet d'e ectuer plein d'autres
choses, comme peindre des ronds vides, par exemple. Sans rire ! Maintenant que vous avez vu comment fonctionne cet objet, nous allons pouvoir utiliser ses
méthodes.
La méthode drawOval()
Il s'agit de la méthode qui permet de dessiner un rond vide. Elle fonctionne exactement de la même manière que la méthode fillOval() . Voici un code
mettant en œuvre cette méthode :
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Si vous spécifiez une largeur di érente de la hauteur, ces méthodes dessineront une forme ovale.
La méthode drawRect()
Cette méthode permet de dessiner des rectangles vides. Bien sûr, son homologue fillRect() existe. Ces deux méthodes fonctionnent de la même manière que
les précédentes, voyez plutôt ce code :
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Rendu des méthodes drawRect() et fillRect()
La méthode drawRoundRect()
Il s'agit du même élément que précédemment, hormis le fait que le rectangle sera arrondi. L'arrondi est défini par la valeur des deux derniers paramètres.
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
La méthode drawLine()
Cette méthode permet de tracer des lignes droites. Il su it de lui spécifier les coordonnées de départ et d'arrivée de la ligne. Dans ce code, je trace les diagonales
du conteneur :
import [Link];
import [Link];
La méthode drawPolygon()
Grâce à cette méthode, vous pouvez dessiner des polygones de votre composition. Eh oui, c'est à vous de définir les coordonnées de tous les points qui les
forment ! Voici à quoi elle ressemble
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Le dernier paramètre est le nombre de points formant le polygone. Ainsi, vous n'aurez pas besoin d'indiquer deux fois le point d'origine pour boucler votre figure :
Java la fermera automatiquement en reliant le dernier point de votre tableau au premier. Cette méthode possède également son homologue pour dessiner des
polygones remplis : fillPolygon() .
import [Link];
import [Link];
int x2[] = {50, 60, 80, 90, 90, 80, 60, 50};
int y2[] = {60, 50, 50, 60, 80, 90, 90, 80};
[Link](x2, y2, 8);
}
}
Il existe également une méthode qui prend exactement les mêmes arguments mais qui, elle, trace plusieurs lignes : drawPolyline() .
Cette méthode va dessiner les lignes correspondant aux coordonnées définies dans les tableaux, sachant que lorsque son indice s'incrémente, la méthode prend
automatiquement les valeurs de l'indice précédent comme point d'origine. Cette méthode 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.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
La méthode drawString()
Voici la méthode permettant d'écrire du texte. Elle est très simple à utiliser : il su it de lui passer en paramètre la phrase à écrire et de lui spécifier à quelles
coordonnées commencer.
import [Link];
import [Link];
Vous pouvez aussi modifier la couleur (la modification s'appliquera également pour les autres méthodes) et la police d'écriture. Pour redéfinir la police d'écriture,
vous devez créer un objet Font . Le code suivant illustre la façon de procéder.
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
Font font = new Font("Courier", [Link], 20);
[Link](font);
[Link]([Link]);
[Link]("Tiens ! Le Site du Zéro !", 10, 20);
}
}
La méthode drawImage()
un objet Image ;
un objet ImageIO ;
un objet File .
Vous allez voir que l'utilisation de ces objets est très simple. Il su it de déclarer un objet de type Image et de l'initialiser en utilisant une méthode statique de
l'objet ImageIO qui, elle, prend un objet File en paramètre. Ça peut sembler compliqué, mais vous allez voir que ce n'est pas le cas… Notre image sera
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
stockée à la racine de notre projet, mais ce n'est pas une obligation. Dans ce cas, faites attention au chemin d'accès de votre image.
En ce qui concerne le dernier paramètre de la méthode drawImage , il s'agit de l'objet qui est censé observer l'image. Ici, nous allons utiliser notre objet
Panneau , donc this .
Cette méthode dessinera l'image avec ses propres dimensions. Si vous voulez qu'elle occupe l'intégralité de votre conteneur, utilisez le constructeur
suivant : drawImage(Image img, int x, int y, int width, int height, Observer obs) .
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Les résultats se trouvent aux deux figures suivantes (pour bien vous montrer la di érence, j'ai créé une fenêtre plus grande que l'image).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Conservation de la taille d'origine de l'image
L'objet Graphics2D
Ceci est une amélioration de l'objet Graphics , et vous allez vite comprendre pourquoi.
Pour utiliser cet objet, il nous su it en e et de caster l'objet Graphics en Graphics2D ( Graphics2D g2d = (Graphics2D) g ), et de ne surtout pas oublier
d'importer notre classe qui se trouve dans le package [Link] . L'une des possibilités qu'o re cet objet n'est autre que celle de peindre des objets avec des
dégradés de couleurs. Cette opération n'est pas du tout di icile à réaliser : il su it 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, mais juste deux ou trois afin que vous voyiez bien la di érence. Commençons par
notre objet GradientPaint ; voici comment l'initialiser (vous devez mettre à jour vos imports en ajoutant import [Link] ) :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Alors, que signifie tout cela ? Voici le détail du constructeur utilisé dans ce code :
Ensuite, pour utiliser ce dégradé dans une forme, il faut mettre à jour notre objet Graphics2D , comme ceci :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link].Graphics2D;
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les deux figures suivantes représentent les résultats obtenus, l'un avec le booléen à true , et l'autre à false .
Dégradé répété
Dégradé stoppé
Votre dégradé est oblique (rien ne m'échappe, à moi :-p). 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). Pour obtenir un dégradé vertical, il su it d'indiquer la valeur de la seconde
coordonnée x à 0, ce qui correspond à la figure suivante.
Dégradé horizontal
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Voici un petit cadeau :
import [Link];
import [Link];
import [Link];
import [Link].Graphics2D;
import [Link];
import [Link];
[Link](gp);
[Link](0, 0, 20, [Link]());
[Link](gp2);
[Link](20, 0, 20, [Link]());
[Link](gp3);
[Link](40, 0, 20, [Link]());
[Link](gp4);
[Link](60, 0, 20, [Link]());
[Link](gp5);
[Link](80, 0, 20, [Link]());
[Link](gp6);
[Link](100, 0, 40, [Link]());
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Maintenant que vous savez utiliser les dégradés avec des rectangles, vous savez les utiliser avec toutes les formes. Je vous laisse essayer cela tranquillement chez
vous.
Pour créer des fenêtres, Java fournit les composants swing (dans [Link] ) et awt (dans [Link] ).
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 à votre convenance.
Lorsque vous ajoutez un JPanel principal à votre fenêtre, n'oubliez pas d'indiquer à votre fenêtre qu'il constituera son content pane.
Pour redéfinir la façon dont l'objet est dessiné sur votre fenêtre, vous devez utiliser la méthode paintComponent() en créant une classe héritée.
C'est lui que vous allez utiliser pour dessiner dans votre conteneur.
Pour des dessins plus évolués, vous devez utiliser l'objet Graphics2D qui s'obtient en e ectuant un cast sur l'objet Graphics .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Je sens que vous êtes impatients de commencer alors… allons-y !
Création de l'animation
Voici un résumé de ce que nous avons déjà codé :
une classe héritée de JPanel avec laquelle nous faisons de jolis dessins. Un rond, en l'occurrence.
Jusqu'à présent, nous avons utilisé des valeurs fixes pour les coordonnées du rond, mais il va falloir dynamiser tout ça. Nous allons donc créer deux variables
privées de type int dans la classe Panneau : appelons-les posX et posY . Dans l'animation sur laquelle nous allons travailler, notre rond viendra de
l'extérieur de la fenêtre. Partons du principe que celui-ci a un diamètre de cinquante pixels : il faut donc que notre panneau peigne ce rond en dehors de sa zone
d'a ichage. Nous initialiserons donc nos deux variables d'instance à « -50 ». Voici le code de notre classe Panneau :
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public void setPosX(int posX) {
[Link] = posX;
}
Il ne nous reste plus qu'à faire en sorte que notre rond se déplace. Nous allons devoir trouver un moyen de changer ses coordonnées grâce à une boucle. Afin de
gérer tout cela, ajoutons une méthode privée dans notre classe Fenetre que nous appellerons en dernier lieu dans notre constructeur. Voici donc ce à quoi
ressemble notre classe Fenetre :
import [Link];
import [Link];
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link](pan);
[Link](true);
go();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
for(int i = -50; i < [Link](); i++){
int x = [Link](), y = [Link]();
x++;
y++;
[Link](x);
[Link](y);
[Link]();
try {
[Link](10);
} catch (InterruptedException e) {
[Link]();
}
}
}
}
Vous vous demandez sûrement l'utilité des deux instructions à la fin de la méthode go() . La première de ces deux nouvelles instructions est [Link]() .
Elle demande à votre composant, ici un JPanel , de se redessiner.
La toute première fois, dans le constructeur de notre classe Fenetre , votre composant avait invoqué la méthode paintComponent() et avait dessiné un rond
aux coordonnées que vous lui aviez spécifiées. La méthode repaint() ne fait rien d'autre qu'appeler à nouveau la méthode paintComponent() ; mais
puisque nous avons changé les coordonnées du rond par le biais des accesseurs, la position de celui-ci sera modifiée à chaque tour de boucle.
La deuxième instruction, [Link]() , est un moyen de suspendre votre code. Elle met en attente votre programme pendant un laps de temps défini dans
la méthode sleep() exprimé en millièmes de seconde (plus le temps d'attente est court, plus l'animation est 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. Vous verrez plus tard qu'il est possible de diviser certaines tâches en
plusieurs processus afin de ne pas perdre du temps et des performances. Pour le moment, sachez que vous pouvez e ectuer des pauses dans vos programmes
grâce à cette instruction :
try{
[Link](1000); //Ici, une pause d'une seconde
}catch(InterruptedException e) {
[Link]();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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 la lumière est faite sur cette a aire, exécutez ce code, vous obtenez la figure suivante.
Bien sûr, cette image est le résultat final : vous devez avoir vu votre rond bouger. Sauf qu'il a laissé une traînée derrière lui… L'explication de ce phénomène est
simple : vous avez demandé à votre objet Panneau de se redessiner, mais il a également a iché les précédents passages de votre rond ! Pour résoudre ce
problème, il faut e acer ces derniers avant de redessiner le rond.
Comment ? Dessinez un rectangle de n'importe quelle couleur occupant toute la surface disponible avant de peindre votre rond. Voici le nouveau code de la
classe Panneau :
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class Panneau extends JPanel {
private int posX = -50;
private int posY = -50;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Capture de l'animation à trois moments di érents
Cela vous plairait-il que votre animation se poursuive tant que vous ne fermez pas la fenêtre ? Oui ? Alors, continuons.
Améliorations
Voici l'un des moments délicats que j'attendais. Si vous vous rappelez bien ce que je vous ai expliqué sur le fonctionnement des boucles, vous vous souvenez de
mon avertissement à propos des boucles infinies. Eh bien, ce que nous allons faire ici, c'est justement utiliser une boucle infinie.
Il existe plusieurs manières de réaliser une boucle infinie : vous avez le choix entre une boucle for , while ou do… while . Regardez ces déclarations :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
Nous allons donc remplacer notre boucle finie par une boucle infinie dans la méthode go() de l'objet Fenetre . Cela donne :
Si vous avez exécuté cette nouvelle version, vous vous êtes rendu compte qu'il reste un problème à régler… En e et, notre rond ne se replace pas à son point de
départ une fois qu'il a atteint l'autre côté de la fenêtre !
Si vous ajoutez une instruction [Link]() dans la méthode paintComponent() inscrivant les coordonnées du rond, vous verrez que
celles-ci ne cessent de croître.
Le premier objectif est bien atteint, mais il nous reste à résoudre ce dernier problème. Pour cela, il faut réinitialiser les coordonnées du rond lorsqu'elles
atteignent le bout de notre composant. Voici donc notre méthode go() revue et corrigée :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private void go(){
for(;;){
int x = [Link](), y = [Link]();
x++;
y++;
[Link](x);
[Link](y);
[Link]();
try {
[Link](10);
} catch (InterruptedException e) {
[Link]();
}
//Si nos coordonnées arrivent au bord de notre composant
//On réinitialise
if(x == [Link]() || y == [Link]()){
[Link](-50);
[Link](-50);
}
}
}
Ce code fonctionne parfaitement (en tout cas, comme nous l'avons prévu), mais avant de passer au chapitre suivant, nous pouvons encore l'améliorer. Nous
allons maintenant rendre notre rond capable de détecter les bords de notre Panneau et de ricocher sur ces derniers !
Jusqu'à présent, nous n'attachions aucune importance au bord que notre rond dépassait. Cela est terminé ! Dorénavant, nous séparerons le dépassement des
coordonnées posX et posY de notre Panneau .
Pour les instructions qui vont suivre, gardez en mémoire que les coordonnées du rond correspondent en réalité aux coordonnées du coin supérieur gauche
du carré entourant le rond.
si la valeur de la coordonnée x du rond est inférieure à la largeur du composant et que le rond avance, on continue d'avancer ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
sinon, on recule.
Dans ce code, j'utilise deux variables de type int pour éviter de rappeler les méthodes getPosX() et getPosY() .
//Si la coordonnée x est supérieure à la taille du Panneau moins la taille du rond, on recule
if(x > [Link]()-50)
backX = true;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
backY = true;
//Sinon, on décrémente
else
[Link](--x);
Exécutez l'application : le rond ricoche contre les bords du Panneau . Vous pouvez même étirer la fenêtre ou la réduire, ça marchera toujours ! On commence à
faire des choses sympa, non ?
Voici le code complet du projet si vous le souhaitez :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Classe Panneau
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public void setPosY(int posY) {
[Link] = posY;
}
}
Classe Fenetre
import [Link];
import [Link];
public Fenetre() {
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link](pan);
[Link](true);
go();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
// Le booléen pour savoir si l'on recule ou non sur l'axe x
boolean backX = false;
// Le booléen pour savoir si l'on recule ou non sur l'axe y
boolean backY = false;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
// On redessine notre Panneau
[Link]();
// Comme on dit : la pause s'impose ! Ici, trois millièmes de seconde
try {
[Link](3);
} catch (InterruptedException e) {
[Link]();
}
}
}
}
Cette méthode prend en paramètre un entier qui correspond à une valeur temporelle exprimée en millièmes de seconde.
Vous pouvez utiliser des boucles infinies pour créer des animations.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
vous employez ce genre de composant… Et c'est encore pire lorsqu'il y en a plusieurs !
Avant de commencer, nous devrons apprendre à positionner des composants dans une fenêtre. Il nous faut en e et gérer la façon dont le contenu est a iché
dans une fenêtre.
une classe contenant une méthode main que nous appellerons Test ;
une classe héritée de JFrame (contenant la totalité du code que l'on a déjà écrit, hormis la méthode go() ), nous la nommerons Fenetre .
Dans la classe Fenetre , nous allons créer une variable d'instance de type JPanel et une autre de type JButton . Faisons de JPanel le content pane de
notre Fenetre , puis définissons le libellé (on parle aussi d'étiquette) de notre bouton et mettons-le sur ce qui nous sert de content pane (en l'occurrence,
JPanel ).
Il ne nous reste plus qu'à ajouter ce bouton sur notre content pane grâce à la méthode add() de l'objet JPanel . Voici donc notre code :
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private JButton bouton = new JButton("Mon bouton");
public Fenetre(){
[Link]("Animation");
[Link](300, 150);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
//Ajout du bouton à notre content pane
[Link](bouton);
[Link](pan);
[Link](true);
}
}
Je ne sais pas si vous avez remarqué, mais le bouton est centré sur votre conteneur ! Cela vous semble normal ? Ça l'est, car par défaut, JPanel gère la mise en
page. En fait, il existe en Java des objets qui servent à agencer vos composants, et JPanel en instancie un par défaut.
Pour vous le prouver, je vais vous faire travailler sur le content pane de votre JFrame . Vous constaterez que pour obtenir la même chose que précédemment,
nous allons être obligés d'utiliser un de ces fameux objets d'agencement.
Tout d'abord, pour utiliser le content pane d'une JFrame , il faut appeler la méthode getContentPane() : nous ajouterons nos composants au content pane
qu'elle retourne. Voici donc le nouveau code :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
public Fenetre(){
[Link]("Bouton");
[Link](300, 150);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
//On ajoute le bouton au content pane de la JFrame
[Link]().add(bouton);
[Link](true);
}
}
Votre bouton est énorme ! En fait, il occupe toute la place disponible, parce que le content pane de votre JFrame ne possède pas d'objet d'agencement. Faisons
donc un petit tour d'horizon de ces objets et voyons comment ils fonctionnent.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Positionner son composant : les layout managers
Vous allez voir qu'il existe plusieurs sortes de layout managers, plus ou moins simples à utiliser, dont le rôle est de gérer la position des éléments sur la fenêtre.
Tous ces layout managers se trouvent dans le package [Link] .
L'objet BorderLayout
Le premier objet que nous aborderons est le BorderLayout . Il 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 devez utiliser les valeurs NORTH , SOUTH , EAST , WEST ou encore
CENTER . Mais puisqu'un aperçu vaut mieux qu'un exposé sur le sujet, voici un exemple à la figure suivante mettant en œuvre un BorderLayout .
Exemple de BorderLayout
Cette fenêtre est composée de cinq JButton positionnés aux cinq endroits di érents que propose un BorderLayout . Voici le code de cette fenêtre :
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On définit le layout à utiliser sur le content pane
[Link](new BorderLayout());
//On ajoute le bouton au content pane de la JFrame
//Au centre
[Link]().add(new JButton("CENTER"), [Link]);
//Au nord
[Link]().add(new JButton("NORTH"), [Link]);
//Au sud
[Link]().add(new JButton("SOUTH"), [Link]);
//À l'ouest
[Link]().add(new JButton("WEST"), [Link]);
//À l'est
[Link]().add(new JButton("EAST"), [Link]);
[Link](true);
}
}
Ce n'est pas très di icile à 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 en deuxième paramètre un attribut static de la classe BorderLayout (dont la liste est citée plus
haut).
Utiliser l'objet BorderLayout soumet vos composants à certaines contraintes. Pour une position NORTH ou SOUTH , la hauteur de votre composant sera
proportionnelle à la fenêtre, mais il occupera toute la largeur ; tandis qu'avec WEST et EAST , ce sera la largeur qui sera proportionnelle alors que toute la
hauteur sera occupée ! Et bien entendu, avec CENTER , tout l'espace est utilisé.
Vous devez savoir que CENTER est aussi le layout par défaut du content pane de la fenêtre, d'où la taille du bouton lorsque vous l'avez ajouté pour la
première fois.
L'objet GridLayout
Celui-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 à partir de 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 trois lignes et de deux colonnes, nous obtenons le résultat
visible sur la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Exemple de rendu avec un GridLayout
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Ce code n'est pas bien di érent du précédent : nous utilisons simplement un autre layout manager et n'avons pas besoin de définir le positionnement lors de
l'ajout du composant avec la méthode add() .
Sachez également que vous pouvez définir le nombre de lignes et de colonnes en utilisant ces méthodes :
Vous pouvez aussi ajouter de l'espace entre les colonnes et les lignes.
L'objet BoxLayout
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Grâce à lui, vous pourrez ranger vos composants à la suite soit sur une ligne, soit sur une colonne. Le mieux, c'est encore un exemple de rendu (voir figure
suivante) avec un code.
Exemple de BoxLayout
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link]("Box Layout");
[Link](300, 120);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](new BoxLayout(b2, BoxLayout.LINE_AXIS));
[Link](new JButton("Bouton 2"));
[Link](new JButton("Bouton 3"));
[Link]().add(b4);
[Link](true);
}
}
Ce code est simple : on crée trois JPanel contenant chacun un certain nombre de JButton rangés en ligne grâce à l'attribut LINE_AXIS . Ces trois conteneurs
créés, nous les rangeons dans un quatrième où, cette fois, nous les agençons dans une colonne grâce à l'attribut PAGE_AXIS . Rien de compliqué, vous en
conviendrez, mais vous devez savoir qu'il existe un moyen encore plus simple d'utiliser ce layout : via l'objet Box . Ce dernier n'est rien d'autre qu'un conteneur
paramétré avec un BoxLayout . Voici un code a ichant la même chose que le précédent :
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public Fenetre(){
[Link]("Box Layout");
[Link](300, 120);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]().add(b4);
[Link](true);
}
}
L'objet CardLayout
Vous allez à présent pouvoir gérer vos conteneurs comme un tas de cartes (les uns sur les autres), et basculer d'un contenu à l'autre en deux temps, trois clics. Le
principe est d'assigner des conteneurs au layout en leur donnant un nom afin de les retrouver plus facilement, permettant de passer de l'un à l'autre sans e ort.
La figure suivante est un schéma représentant ce mode de fonctionnement.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Schéma du CardLayout
Je vous propose un code utilisant ce layout. Vous remarquerez que j'ai utilisé des boutons afin de passer d'un conteneur à un autre et n'y comprendrez peut-être
pas tout, mais ne vous inquiétez pas, nous allons apprendre à réaliser tout cela avant la fin de ce chapitre. Pour le moment, ne vous attardez donc pas trop sur les
actions : concentrez-vous sur le layout en lui-même.
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link]("CardLayout");
[Link](300, 120);
[Link](JFrame.EXIT_ON_CLOSE);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](null);
[Link](bouton);
[Link](bouton2);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On définit le layout
[Link](cl);
//On ajoute les cartes à la pile avec un nom pour les retrouver
[Link](card1, listContent[0]);
[Link](card2, listContent[1]);
[Link](card3, listContent[2]);
[Link]().add(boutonPane, [Link]);
[Link]().add(content, [Link]);
[Link](true);
}
}
La figure suivante correspond aux résultats de ce code à chaque clic sur les boutons.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Schéma du CardLayout
L'objet GridBagLayout
Cet objet est certainement le plus di icile à utiliser et à comprendre (ce qui l'a beaucoup desservi auprès des développeurs Java). Pour faire simple, ce layout se
présente sous la forme d'une grille à la façon d'un tableau Excel : vous devez positionner vos composants en vous servant des coordonnées des cellules (qui sont
définies lorsque vous spécifiez leur nombre). Vous devez aussi définir les marges et la façon dont vos composants se répliquent dans les cellules... Vous voyez que
c'est plutôt dense comme gestion du positionnement. Je tiens aussi à vous prévenir que je n'entrerai pas trop dans les détails de ce layout afin de ne pas trop
compliquer les choses.
La figure suivante représente la façon dont nous allons positionner nos composants.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Positionnement avec le GridBagLayout
Imaginez que le nombre de colonnes et de lignes ne soit pas limité comme il l'est sur le schéma (c'est un exemple et j'ai dû limiter sa taille, mais le principe est là).
Vous paramétreriez le composant avec des coordonnées de cellules, en précisant si celui-ci doit occuper une ou plusieurs d'entre elles. Afin d'obtenir un rendu
correct, vous devriez indiquer au layout manager lorsqu'une ligne se termine, ce qui se fait en spécifiant qu'un composant est le dernier élément d'une ligne, et
vous devriez en plus spécifier au composant débutant la ligne qu'il doit suivre le dernier composant de la précédente.
Je me doute que c'est assez flou et confus, je vous propose donc de regarder la figure suivante, qui est un exemple de ce que nous allons obtenir.
Exemple de GridBagLayout
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Tous les éléments que vous voyez sont des conteneurs positionnés suivant une matrice, comme expliqué ci-dessus. Afin que vous vous en rendiez compte,
regardez comment le tout est rangé sur la figure suivante.
Composition du GridBagLayout
Vous pouvez voir que nous avons fait en sorte d'obtenir un tableau de quatre colonnes sur trois lignes. Nous avons positionné quatre éléments sur la première
ligne, spécifié que le quatrième élément terminait celle-ci, puis nous avons placé un autre composant au début de la deuxième ligne d'une hauteur de deux cases,
en informant le gestionnaire que celui-ci suivait directement la fin de la première ligne. Nous ajoutons un composant de trois cases de long terminant la deuxième
ligne, pour passer ensuite à un composant de deux cases de long puis à un dernier achevant la dernière ligne.
Lorsque des composants se trouvent sur plusieurs cases, vous devez spécifier la façon dont ils s'étalent : horizontalement ou verticalement.
Le moment est venu de vous fournir le code de cet exemple, mais je vous préviens, ça pique un peu les yeux :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link]("GridBagLayout");
[Link](300, 160);
[Link](JFrame.EXIT_ON_CLOSE);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](null);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](new GridBagLayout());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link] = 1;
//Celle-ci indique que la cellule se réplique de façon horizontale
[Link] = [Link];
[Link] = [Link];
[Link](cell6, gbc);
//---------------------------------------------
[Link] = 1;
[Link] = 2;
[Link] = 2;
[Link](cell7, gbc);
//---------------------------------------------
[Link] = 3;
[Link] = [Link];
[Link](cell8, gbc);
//---------------------------------------------
//On ajoute le conteneur
[Link](content);
[Link](true);
}
}
Vous pouvez vous rendre compte que c'est via l'objet GridBagConstraints que tout se joue. Vous pouvez utiliser ses di érents arguments afin de positionner
vos composants, en voici une liste :
weightx : si la grille est plus large que l'espace demandé, l'espace est redistribué proportionnellement aux valeurs de weightx des di érentes colonnes.
weighty : si la grille est plus haute que l'espace demandé, l'espace est redistribué proportionnellement aux valeurs de weighty des di érentes lignes.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
anchor : ancrage du composant dans la cellule, c'est-à-dire son alignement dans la cellule (en bas à droite, en haut à gauche…). Voici les di érentes valeurs
utilisables :
fill : remplissage si la cellule est plus grande que le composant. Valeurs possibles : NONE , HORIZONTAL , VERTICAL et BOTH .
insets : espace autour du composant. S'ajoute aux espacements définis par les propriétés ipadx et ipady ci-dessous.
Dans mon exemple, je ne vous ai pas parlé de tous les attributs existants, mais si vous avez besoin d'un complément d'information, n'hésitez pas à consulter le
site d'Oracle.
L'objet FlowLayout
Celui-ci est certainement le plus facile à utiliser ! Il se contente de centrer les composants dans le conteneur. Regardez plutôt la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Exemple de FlowLayout
On dirait bien que nous venons de trouver le layout manager défini par défaut dans les objets JPanel . Lorsque vous insérez plusieurs composants dans ce
gestionnaire, il passe à la ligne suivante dès que la place est trop étroite. Voyez l'exemple de la figure suivante.
Il faut que vous sachiez que les IHM ne sont en fait qu'une imbrication de composants positionnés grâce à des layout managers. Vous allez tout de suite voir de
quoi je veux parler : nous allons maintenant utiliser notre conteneur personnalisé avec un bouton. Vous pouvez donc revenir dans le projet contenant notre
animation créée au cours des chapitres précédents. Le but est d'insérer notre animation au centre de notre fenêtre et un bouton en bas de celle-ci, comme le
montre la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Bouton et animation dans la même fenêtre
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JButton bouton = new JButton("mon bouton");
private JPanel container = new JPanel();
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]([Link]);
[Link](new BorderLayout());
[Link](pan, [Link]);
[Link](bouton, [Link]);
[Link](container);
[Link](true);
go();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](--x);
//Idem pour l'axe Y
if(!backY)
[Link](++y);
else
[Link](--y);
Pour ajouter un bouton dans une fenêtre, vous devez utiliser la méthode add() de son content pane.
Il existe des objets permettant de positionner les composants sur un content pane ou un conteneur : les layout managers.
Le layout manager par défaut du content pane d'un objet JFrame est le BorderLayout .
Outre le FlowLayout et le BorderLayout , il existe le GridLayout , le CardLayout , le BoxLayout et le GridBagLayout . La liste n'est pas exhaustive.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Interagir avec des boutons
Nous avons vu dans le chapitre précédent les di érentes façons de positionner des boutons et, par extension, des composants (car oui, ce que nous venons
d'apprendre pourra être réutilisé avec tous les autres composants que nous verrons par la suite).
Maintenant que vous savez positionner des composants, il est grand temps de leur indiquer ce qu'ils doivent faire. C'est ce que je vous propose d'aborder
dans ce chapitre. Mais avant cela, nous allons voir comment personnaliser un bouton. Toujours prêts ?
Bouton personnalisé
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
import [Link].Graphics2D;
import [Link];
J'ai aussi créé un bouton personnalisé avec une image de fond, comme le montre la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Bouton avec une image de fond
J'ai appliqué l'image (bien sûr, ladite image se trouve à la racine de mon projet !) sur l'intégralité du fond, comme je l'ai montré lorsque nous nous amusions avec
notre Panneau . Voici le code de cette classe Bouton :
import [Link];
import [Link];
import [Link];
import [Link].Graphics2D;
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private Image img;
Rien de compliqué jusque-là… C'est à partir de maintenant que les choses deviennent intéressantes !
Et si je vous proposais de changer l'aspect de votre objet lorsque vous cliquez dessus avec votre souris et lorsque vous relâchez le clic ? Il existe des interfaces à
implémenter qui permettent de gérer toutes sortes d'événements dans votre IHM. Le principe est un peu déroutant au premier abord, mais il est assez simple
lorsqu'on a un peu pratiqué. N'attendons plus et voyons cela de plus près !
Avant de nous lancer dans l'implémentation, vous pouvez voir le résultat que nous allons obtenir sur les deux figures suivantes.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Apparence du bouton au survol de la souris
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Il va tout de même falloir passer par un peu de théorie avant d'arriver à ce résultat. Pour détecter les événements qui surviennent sur 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, nous le verrons à la fin de ce chapitre.
Vous vous en doutez, nous devrons implémenter l'interface MouseListener dans notre classe Bouton . Nous devrons aussi préciser à notre classe qu'elle devra
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, ce qui
signifie que dès que notre objet observable (notre bouton) obtiendra des informations concernant les actions e ectuées par la souris, il indiquera à l'objet qui
l'observe (c'est-à-dire à lui-même) ce qu'il doit e ectuer.
Cela est réalisable 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 supertype : ici, notre classe implémente l'interface MouseListener , nous pouvons
donc utiliser cet objet comme référence de cette interface.
Voici à présent notre classe Bouton :
import [Link];
import [Link];
import [Link];
import [Link].Graphics2D;
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
//Grâce à cette instruction, notre objet va s'écouter
//Dès qu'un événement de la souris sera intercepté, il en sera averti
[Link](this);
}
C'est en redéfinissant ces di érentes méthodes présentes dans l'interface MouseListener que nous allons gérer les di érentes images à dessiner dans notre
objet.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Rappelez-vous en outre que même si vous n'utilisez pas toutes les méthodes d'une interface, vous devez malgré tout insérer le squelette des méthodes non
utilisées (avec les accolades), cela étant également valable pour les classes abstraites.
Dans notre cas, la méthode repaint() est appelée de façon implicite : lorsqu'un événement est déclenché, notre objet se redessine automatiquement !
Comme lorsque vous redimensionniez votre fenêtre dans les premiers chapitres.
Nous n'avons alors plus qu'à modifier notre image en fonction de la méthode invoquée. Notre objet comportera les caractéristiques suivantes :
Pour ce faire, je vous propose de télécharger les fichiers PNG dont je me suis servi (rien ne vous empêche de les créer vous-mêmes).
Télécharger les images
Je vous rappelle que dans le code qui suit, les images sont placées à la racine du projet.
import [Link];
import [Link];
import [Link];
import [Link].Graphics2D;
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private String name;
private Image img;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Et voilà le travail ! Si vous avez enregistré mes images, elles ne possèdent probablement pas le même nom que dans mon code : vous devez alors modifier le code
en fonction de celui que vous leur avez attribué ! D'accord, ça va de soi… mais on ne sait jamais.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous possédez dorénavant un bouton personnalisé qui réagit au passage de votre souris. Je sais qu'il y aura des « p'tits malins » qui cliqueront sur le bouton et
relâcheront le clic en dehors du bouton : dans ce cas, le fond du bouton deviendra orange, puisque c'est ce qui doit être e ectué vu la méthode
mouseReleased() . Afin de pallier ce problème, nous allons vérifier que lorsque le clic est relâché, la souris se trouve toujours sur le bouton.
Nous avons implémenté l'interface MouseListener ; il reste cependant un 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 !
Cet objet nous permet d'obtenir beaucoup d'informations sur les événements. Nous ne détaillerons pas tout ici, mais nous verrons certains côtés pratiques de ce
type d'objet tout au long de cette partie. Dans notre cas, nous pouvons récupérer les coordonnées x et y du curseur de la souris par rapport au Bouton grâce aux
méthodes getX() et getY() . Cela signifie que si nous relâchons le clic en dehors de la zone où se trouve notre objet, la valeur retournée par la méthode
getY() sera négative.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous verrez dans les chapitres qui suivent qu'il existe plusieurs interfaces pour les di érentes actions possibles sur une IHM. Sachez qu'il existe aussi une
convention pour ces interfaces : leur nom commence par le type de l'action, suivi du mot Listener . Nous avons étudié ici les actions de la souris, voyez
le nom de l'interface : MouseListener .
Nous possédons à présent un bouton réactif, mais qui n'e ectue rien pour le moment. Je vous propose de combler cette lacune.
Afin de gérer les di érentes actions à e ectuer selon le bouton sur lequel on clique, nous allons utiliser l'interface ActionListener .
Nous n'allons pas implémenter cette interface dans notre classe Bouton mais dans notre classe Fenetre , le but étant de faire en sorte que lorsque l'on clique
sur le bouton, il se passe quelque chose dans notre application : changer un état, une variable, e ectuer une incrémentation… Enfin, n'importe quelle action !
Comme je vous l'ai expliqué, lorsque nous appliquons un addMouseListener() , nous informons l'objet observé qu'un autre objet doit être tenu au courant de
l'événement. Ici, nous voulons que ce soit notre application (notre Fenetre ) qui écoute notre Bouton , le but étant de pouvoir lancer ou arrêter l'animation
dans le Panneau .
Avant d'en arriver là, nous allons faire plus simple : nous nous pencherons dans un premier temps sur l'implémentation de l'interface ActionListener . Afin de
vous montrer toute la puissance de cette interface, nous utiliserons un nouvel objet issu du package [Link] : le JLabel . Cet objet se comporte comme
un libellé : il est spécialisé dans l'a ichage de texte ou d'image. Il est donc idéal pour notre premier exemple !
On l'instancie et l'initialise plus ou moins de la même manière que le JButton :
Créez une variable d'instance de type JLabel (appelez-la label ) et initialisez-la avec le texte qui vous plaît ; ajoutez-la ensuite à votre content pane en
position [Link] .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Le résultat se trouve en figure suivante.
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](new BorderLayout());
[Link](pan, [Link]);
[Link](bouton, [Link]);
[Link](label, [Link]);
[Link](container);
[Link](true);
go();
}
//Le reste ne change pas
}
Vous pouvez voir que le texte de cet objet est aligné par défaut en haut à gauche. Il est possible de modifier quelques paramètres tels que :
l'alignement du texte ;
la police à utiliser ;
la couleur du texte ;
d'autres paramètres.
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
[Link](pan, [Link]);
[Link](bouton, [Link]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Font police = new Font("Tahoma", [Link], 16);
//On l'applique au JLabel
[Link](police);
//Changement de la couleur du texte
[Link]([Link]);
//On modifie l'alignement du texte grâce aux attributs statiques
//de la classe JLabel
[Link]([Link]);
[Link](label, [Link]);
[Link](container);
[Link](true);
go();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Maintenant que notre libellé se présente exactement sous la forme que nous voulons, nous pouvons implémenter l'interface ActionListener . Vous
remarquerez que cette interface ne contient qu'une seule méthode !
public Fenetre(){
//Ce morceau de code ne change pas
}
}
}
Nous allons maintenant informer notre objet Bouton que notre objet Fenetre l'écoute. Vous l'avez deviné : ajoutons notre Fenetre à la liste des objets qui
écoutent notre Bouton grâce à la méthode addActionListener(ActionListener obj) présente dans la classe JButton , donc utilisable avec la variable
bouton . Ajoutons cette instruction dans le constructeur en passant this en paramètre (puisque c'est notre Fenetre qui écoute le Bouton ).
Une fois l'opération e ectuée, nous pouvons modifier le texte du JLabel avec la méthode actionPerformed() . Nous allons compter le nombre de fois que
l'on a cliqué sur le bouton : ajoutons une variable d'instance de type int dans notre class et appelons-la compteur , puis dans la méthode
actionPerformed() , incrémentons ce compteur et a ichons son contenu dans notre libellé.
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
[Link](pan, [Link]);
[Link](bouton, [Link]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](true);
go();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Et nous ne faisons que commencer… Eh oui, nous allons maintenant ajouter un deuxième bouton à notre Fenetre , à côté du premier (vous êtes libres d'utiliser
la classe personnalisée ou un simple JButton ). Pour ma part, j'utiliserai des boutons normaux ; en e et, dans notre classe personnalisée, la façon dont le libellé
est écrit dans notre bouton n'est pas assez souple et l'a ichage peut donc être décevant (dans certains cas, le libellé peut ne pas être centré)…
Bref, nous possédons à présent deux boutons écoutés par notre objet Fenetre .
Vous devez créer un deuxième JPanel qui contiendra nos deux boutons, puis l'insérer dans le content pane en position [Link] . Si vous
tentez de positionner deux composants au même endroit grâce à un BorderLayout ,seul le dernier composant ajouté apparaîtra : en e et, le composant
occupe toute la place disponible dans un BorderLayout !
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
[Link](pan, [Link]);
[Link](this);
[Link](this);
//…
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Un deuxième bouton dans la fenêtre
À présent, le problème est le suivant : comment e ectuer deux actions di érentes dans la méthode actionPerformed() ?
En e et, si nous laissons la méthode actionPerformed() telle quelle, les deux boutons exécutent la même action lorsqu'on les clique. Essayez, vous verrez le
résultat.
Il existe un moyen de connaître l'élément ayant déclenché l'événement : il faut se servir de l'objet passé en paramètre dans la méthode actionPerformed() .
Nous pouvons exploiter 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 si le résultat correspond à la figure suivante.
if([Link]() == bouton2)
[Link]("Vous avez cliqué sur le bouton 2");
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Détection de la source de l'événement
Notre code fonctionne à merveille ! Cependant, cette approche n'est pas très orientée objet : si notre IHM contient une multitude de boutons, la méthode
actionPerformed() sera très chargée. Nous pourrions créer deux objets à part, chacun écoutant un bouton, dont le rôle serait de réagir de façon appropriée
pour chaque bouton ; mais si nous avions besoin de modifier des données spécifiques à la classe contenant nos boutons, il faudrait ruser afin de parvenir à faire
communiquer nos objets… Pas terrible non plus.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public MaClasseExterne(){
//…
}
class MaClassInterne{
public MaClassInterne(){
//…
}
}
}
Grâce à cela, nous pourrons concevoir une classe spécialisée dans l'écoute des composants et qui e ectuera un travail bien déterminé. Dans notre exemple, nous
créerons deux classes internes implémentant chacune l'interface ActionListener et redéfinissant la méthode actionPerformed() :
Une fois ces opérations e ectuées, il ne nous reste plus qu'à indiquer à chaque bouton « qui l'écoute » grâce à la méthode addActionListener() .
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
[Link](pan, [Link]);
//Ce sont maintenant nos classes internes qui écoutent nos boutons
[Link](new BoutonListener());
[Link](new Bouton2Listener());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private void go(){
//Cette méthode ne change pas
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Utilisation de deux actions sur deux boutons
Vous pouvez constater que nos classes internes ont même accès aux attributs déclarés private dans notre classe Fenetre .
Dorénavant, nous n'avons plus à nous soucier du bouton qui a déclenché l'événement, car nous disposons de deux classes écoutant chacune un bouton. Nous
pouvons sou ler un peu : une grosse épine vient de nous être retirée du pied.
Vous pouvez aussi faire écouter votre bouton par plusieurs classes. Il vous su it d'ajouter ces classes supplémentaires à l'aide d' addActionListener() .
Eh oui, faites le test : créez une troisième classe interne et attribuez-lui le nom que vous voulez (personnellement, je l'ai appelée Bouton3Listener ).
Implémentez-y l'interface ActionListener et contentez-vous d'e ectuer un simple [Link]() dans la méthode actionPerformed() .
N'oubliez pas de l'ajouter à la liste des classes qui écoutent votre bouton (n'importe lequel des deux ; j'ai pour ma part choisi le premier).
//Les imports…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Les variables d'instance…
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
[Link](pan, [Link]);
[Link](new Bouton2Listener());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les classes internes sont vraiment des classes à part entière. Elles peuvent également hériter d'une superclasse. De ce fait, c'est presque comme si nous nous
trouvions dans le cas d'un héritage multiple (ce n'en est pas un, même si cela y ressemble). Ce code est donc valide :
public MaClasseExterne(){
//...
}
Vous voyez bien que ce genre de classes peut s'avérer très utile.
Bon, nous avons réglé le problème d'implémentation : nous possédons deux boutons qui sont écoutés. Il ne nous reste plus qu'à lancer et arrêter notre animation
à l'aide de ces boutons. Mais auparavant, nous allons étudier une autre manière d'implémenter des écouteurs et, par extension, des classes devant redéfinir les
méthodes d'une classe abstraite ou d'une interface.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
JButton bouton = new JButton("Contenu suivant");
//Définition de l'action sur le bouton
[Link](new ActionListener(){
public void actionPerformed(ActionEvent event){
//Action !
}
});
L'une des particularités de cette méthode, c'est que l'écouteur n'écoutera que ce composant. Vous pouvez vérifier qu'il n'y se trouve aucune déclaration de classe
et que nous instancions une interface par l'instruction new ActionListener() . Nous devons seulement redéfinir la méthode, que vous connaissez bien
maintenant, dans un bloc d'instructions ; d'où les accolades après l'instanciation, comme le montre la figure suivante.
C'est simple : procéder de cette manière revient à créer une classe fille sans être obligé de créer cette classe de façon explicite. L'héritage se produit
automatiquement. En fait, le code ci-dessus revient à e ectuer ceci :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public void actionPerformed(ActionEvent event){
//Action !
}
}
}
Seulement, la classe créée n'a pas de nom, l'héritage s'e ectue de façon implicite ! Nous bénéficions donc de tous les avantages de la classe mère en ne
redéfinissant que la méthode qui nous intéresse.
Sachez aussi que les classes anonymes peuvent être utilisées pour implémenter des classes abstraites. Je vous conseille d'e ectuer de nouveaux tests en utilisant
notre exemple du pattern strategy ; mais cette fois, plutôt que de créer des classes, créez des classes anonymes.
Les classes anonymes sont soumises aux mêmes règles que les classes « normales » :
Cependant, ces classes possèdent des restrictions à cause de leur rôle et de leur raison d'être :
elles sont automatiquement déclarées final : on ne peut dériver de cette classe, l'héritage est donc impossible !
au lancement, le bouton Go ne sera pas cliquable alors que le bouton Stop oui ;
si l'animation est interrompue, le bouton Stop ne sera plus cliquable, mais le bouton Go le sera.
Ne vous inquiétez pas, c'est très simple à réaliser. Il existe une méthode gérant ces changements d'état :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
JButton bouton = new JButton("bouton");
[Link](false); //Le bouton n'est plus cliquable
[Link](true); //Le bouton est de nouveau cliquable
Ces objets permettent de réaliser pas mal de choses ; soyez curieux et testez-en les méthodes. Allez donc faire un tour sur le site d'Oracle : fouillez, fouinez…
L'une de ces méthodes, qui s'avère souvent utile et est utilisable avec tous ces objets (ainsi qu'avec les objets que nous verrons par la suite), est la méthode de
gestion de dimension. Il ne s'agit pas de la méthode setSize() , mais de la méthode setPreferredSize() . Elle prend en paramètre un objet Dimension ,
qui, lui, prend deux entiers comme arguments.
Voici un exemple :
Afin de bien gérer notre animation, nous devons améliorer notre méthode go() . Sortons donc de cette méthode les deux entiers dont nous nous servions afin
de recalculer les coordonnées de notre rond. La boucle infinie doit dorénavant pouvoir être interrompue ! Pour réussir cela, nous allons déclarer un booléen qui
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
changera d'état selon le bouton sur lequel on cliquera ; nous l'utiliserons comme paramètre de notre boucle.
Voyez ci-dessous le code de notre classe Fenetre .
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](pan, [Link]);
[Link](new BoutonListener());
[Link](false);
[Link](new Bouton2Listener());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
else [Link](--y);
[Link]();
try {
[Link](3);
} catch (InterruptedException e) {
[Link]();
}
}
}
l'animation se lance ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
l'animation s'arrête lorsque l'on clique sur le bouton Stop ;
Comme je l'ai expliqué dans le chapitre traitant des conteneurs, un thread est lancé au démarrage de notre application : c'est le processus principal du
programme. Au démarrage, l'animation est donc lancée dans le même thread que notre objet Fenetre . Lorsque nous lui demandons de s'arrêter, aucun
problème : les ressources mémoire sont libérées, on sort de la boucle infinie et l'application continue à fonctionner.
Mais lorsque nous redemandons à l'animation de se lancer, l'instruction se trouvant dans la méthode actionPerformed() appelle la méthode go() et, étant
donné que nous nous trouvons à l'intérieur d'une boucle infinie, nous restons dans la méthode go() et ne sortons plus de la méthode actionPerformed() .
Explication de ce phénomène
Java gère les appels aux méthodes grâce à ce que l'on appelle vulgairement la pile.
Pour expliquer cela, prenons un exemple tout bête ; regardez cet objet :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
methode3();
[Link]("Fin méthode 2");
}
Si vous instanciez cet objet, vous obtenez dans la console la figure suivante.
Je suppose que vous avez remarqué avec stupéfaction que l'ordre des instructions est un peu bizarre. Voici ce qu'il se passe :
celle-ci utilise la méthode 3 : une fois qu'elle a terminé, la JVM retourne dans la méthode 2 ;
lorsqu'elle a fini de s'exécuter, on remonte à la fin de la méthode 1, jusqu'à la dernière instruction appelante : le constructeur.
Lors de tous les appels, on dit que la JVM empile les invocations sur la pile. Une fois que la dernière méthode empilée a terminé de s'exécuter, la JVM la
dépile.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
La figure suivante présente un schéma résumant la situation.
Dans notre programme, imaginez que la méthode actionPerformed() soit représentée par la méthode 2, et que notre méthode go() soit représentée par la
méthode 3. Lorsque nous entrons dans la méthode 3, nous entrons dans une boucle infinie… Conséquence directe : nous ne ressortons jamais de cette méthode
et la JVM ne dépile plus !
Afin de pallier ce problème, nous allons utiliser un nouveau thread. Grâce à cela, la méthode go() se trouvera dans une pile à part.
Attends : on arrive pourtant à arrêter l'animation alors qu'elle se trouve dans une boucle infinie. Pourquoi ?
Tout simplement parce que nous ne demandons d'e ectuer qu'une simple initialisation de variable dans la gestion de notre événement ! Si vous créez une
deuxième méthode comprenant une boucle infinie et que vous l'invoquez lors du clic sur le bouton Stop , vous aurez exactement le même problème.
Je ne vais pas m'éterniser là-dessus, nous verrons cela dans un prochain chapitre. À présent, je pense qu'il est de bon ton de vous parler du mécanisme d'écoute
d'événements, le fameux pattern observer.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Être à l'écoute de ses objets : le design pattern Observer
Le design pattern Observer est utilisé pour gérer les événements de vos IHM. C'est une technique de programmation. La connaître n'est pas absolument
indispensable, mais cela vous aide à mieux comprendre le fonctionnement de Swing et AWT . C'est par ce biais que vos composants e ectueront quelque chose
lorsque vous les cliquerez ou les survolerez.
Je vous propose de découvrir son fonctionnement à l'aide d'une situation problématique.
Posons le problème
Sachant que vous êtes des développeurs Java chevronnés, un de vos amis proches vous demande si vous êtes en mesure de l'aider à réaliser une horloge digitale
en Java. Il a en outre la gentillesse de vous fournir les classes à utiliser pour la création de son horloge. Votre ami a l'air de s'y connaître, car ce qu'il vous a fourni
est bien structuré.
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
//On initialise la JFrame
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link](false);
[Link](200, 80);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On initialise l'horloge
[Link] = new Horloge();
//On initialise le JLabel
Font police = new Font("DS-digital", Font.TYPE1_FONT, 30);
[Link](police);
[Link]([Link]);
//On ajoute le JLabel à la JFrame
[Link]().add([Link], [Link]);
}
package [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link] = //Les heures
[Link](Calendar.HOUR_OF_DAY) + " : "
+
( //Les minutes
[Link]([Link]) < 10
? "0" + [Link]([Link])
: [Link]([Link])
)
+ " : "
+
( //Les secondes
([Link]([Link])< 10)
? "0"+[Link]([Link])
: [Link]([Link])
);
try {
[Link](1000);
} catch (InterruptedException e) {
[Link]();
}
}
}
}
Si vous ne disposez pas de la police d'écriture que j'ai utilisée, utilisez-en une autre : Arial ou Courrier , par exemple.
Le problème auquel votre ami est confronté est simple : il est impossible de faire communiquer l'horloge avec la fenêtre.
Je ne vois pas où est le problème : il n'a qu'à passer son instance de JLabel dans son objet Horloge , et le tour est joué !
En réalité, votre ami, dans son infinie sagesse, souhaite - je le cite - que l'horloge ne dépende pas de son interface graphique, juste au cas où il devrait passer
d'une IHM swing à une IHM awt .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Il est vrai que si l'on passe l'objet d'a ichage dans l'horloge, dans le cas où l'on change le type de l'IHM, toutes les classes doivent être modifiées ; ce n'est pas
génial. En fait, lorsque vous procédez de la sorte, on dit que vous couplez des objets : vous rendez un ou plusieurs objets dépendants d'un ou de plusieurs autres
objets (entendez par là que vous ne pourrez plus utiliser les objets couplés indépendamment des objets auxquels ils sont attachés).
Le couplage entre objets est l'un des problèmes principaux relatifs à la réutilisation des objets. Dans notre cas, si vous utilisez l'objet Horloge dans une autre
application, vous serez confrontés à plusieurs problèmes étant donné que cet objet ne s'a iche que dans un JLabel .
C'est là que le pattern observer entre en jeu : il fait communiquer des objets entre eux sans qu'ils se connaissent réellement ! Vous devez être curieux de voir
comment il fonctionne, je vous propose donc de l'étudier sans plus tarder.
Ce sont deux points cruciaux, mais un autre élément, que vous ne connaissez pas encore, va vous plaire : tout se fait automatiquement !
Comment les choses vont-elles alors se passer ? Réfléchissons à ce que nous voulons que notre horloge digitale e ectue : elle doit pouvoir avertir l'objet servant à
a icher l'heure lorsqu'il doit rafraîchir son a ichage. Puisque les horloges du monde entier se mettent à jour toutes les secondes, il n'y a aucune raison pour que
la nôtre ne fasse pas de même.
Ce qui est merveilleux avec ce pattern, c'est que notre horloge ne se contentera pas d'avertir un seul objet que sa valeur a changé : elle pourra en e et mettre
plusieurs observateurs au courant !
En fait, pour faire une analogie, interprétez la relation entre les objets implémentant le pattern observer comme un éditeur de journal et ses clients (voir figure
suivante).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Livreur de journaux
Grâce à ce schéma, vous pouvez sentir que notre objet défini comme observable pourra être surveillé par plusieurs objets : il s'agit d'une relation dite de un à
plusieurs vers l'objet Observateur . Avant de vous expliquer plus en détail le fonctionnement de ce pattern, jetez un œil au diagramme de classes de notre
application en figure suivante.
Ce diagramme indique que ce ne sont pas les instances d' Horloge ou de JLabel que nous allons utiliser, mais des implémentations d'interfaces.
En e et, vous savez que les classes implémentant une interface peuvent être définies par le type de l'interface. Dans notre cas, la classe Fenetre implémentera
l'interface Observateur : nous pourrons la voir comme une classe du type Observateur . Vous avez sans doute remarqué que la deuxième interface - celle
dédiée à l'objet Horloge - possède trois méthodes :
une permettant d'ajouter des observateurs (nous allons donc gérer une collection d'observateurs) ;
Grâce à cela, nos objets ne sont plus liés par leurs types, mais par leurs interfaces ! L'interface qui apportera les méthodes de mise à jour, d'ajout d'observateurs,
etc. travaillera donc avec des objets de type Observateur .
Ainsi, le couplage ne s'e ectue plus directement, il s'opère par le biais de ces interfaces. Ici, il faut que nos deux interfaces soient couplées pour que le système
fonctionne. De même que, lorsque je vous ai présenté le pattern decorator, nos classes étaient très fortement couplées puisqu'elles devaient travailler ensemble :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
nous devions alors faire en sorte de ne pas les séparer.
Voici comment fonctionnera l'application :
notre objet Horloge , implémentant l'interface Observable , préviendra les objets spécifiés de ses changements ;
à partir de là, notre objet Horloge fera le reste : à chaque changement, nous appellerons la méthode mettant tous les observateurs à jour.
Le code source de ces interfaces se trouve ci-dessous (notez que j'ai créé un package [Link] ).
[Link]
package [Link];
[Link]
package [Link];
Voici maintenant le code de nos deux classes, travaillant ensemble mais n'étant que faiblement couplées.
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
);
//On avertit les observateurs que l'heure a été mise à jour
[Link]();
try {
[Link](1000);
} catch (InterruptedException e) {
[Link]();
}
}
}
[Link]
package [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
//On initialise la JFrame
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link](false);
[Link](200, 80);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](true);
[Link]();
}
Exécutez ce code, vous verrez que tout fonctionne à merveille. Vous venez de permettre à deux objets de communiquer en n'utilisant presque aucun couplage :
félicitations !
Vous pouvez voir que lorsque l'heure change, la méthode updateObservateur() est invoquée. Celle-ci parcourt la collection d'objets Observateur et appelle
sa méthode update(String hour) . La méthode étant redéfinie dans notre classe Fenetre afin de mettre JLabel à jour, l'heure s'a iche !
J'ai mentionné que ce pattern est utilisé dans la gestion événementielle d'interfaces graphiques. Vous avez en outre remarqué que leurs syntaxes sont identiques.
En revanche, je vous ai caché quelque chose : il existe des classes Java permettant d'utiliser le pattern observer sans avoir à coder les interfaces.
Celles-ci fonctionnent de manière quasiment identique à notre façon de procéder, seuls quelques détails di èrent. Personnellement, je préfère de loin utiliser un
pattern observer « fait maison ».
Pourquoi cela ? Tout simplement parce que l'objet que l'on souhaite observer doit hériter de la classe Observable . Par conséquent, il ne pourra plus hériter
d'une autre classe étant donné que Java ne gère pas l'héritage multiple. La figure suivante présente la hiérarchie de classes du pattern observer présent dans
Java.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Diagramme de classes du pattern observer de Java
Vous remarquez qu'il fonctionne presque de la même manière que celui que nous avons développé. Il y a toutefois une di érence dans la méthode
update(Observable obs, Object obj) : sa signature a changé.
Cette méthode prend ainsi deux paramètres :
un objet Observable ;
un Object représentant une donnée supplémentaire que vous souhaitez lui fournir.
Vous connaissez le fonctionnement de ce pattern, il vous est donc facile de comprendre le schéma. Je vous invite cependant à e ectuer vos propres recherches
sur son implémentation dans Java : vous verrez qu'il existe des subtilités (rien de méchant, cela dit).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
import [Link];
import [Link].Graphics2D;
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]([Link]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous pouvez interagir avec un composant grâce à votre souris en implémentant l'interface MouseListener .
Lorsque vous implémentez une interface < … >Listener , vous indiquez à 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 ce que la classe doit observer grâce aux méthodes de type add< … >Listener(< … >Listener) .
Une classe interne est une classe se trouvant à l'intérieur d'une classe.
Une telle classe a accès à toutes les données et méthodes de sa classe externe.
La JVM traite les méthodes appelées en utilisant une pile qui définit leur ordre d'exécution.
Une méthode est empilée à son invocation, mais n'est dépilée que lorsque toutes ses instructions ont fini de s'exécuter.
Le pattern observer permet d'utiliser des objets faiblement couplés. Grâce à ce pattern, les objets restent indépendants.
TP : une calculatrice
Ah ! Ça faisait longtemps… Un petit TP ! Dans celui-ci, nous allons - enfin, vous allez - pouvoir réviser tout ce qui a été vu au cours de cette partie :
les fenêtres ;
les conteneurs ;
les boutons ;
les interactions ;
Élaboration
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Nous allons tout de suite voir ce dont notre calculatrice devra être capable :
Faire des calculs à la chaîne, par exemple : 1 + 2 + ... ; lorsqu'on clique à nouveau sur un opérateur, il faut a icher le résultat du calcul précédent.
Conception
Vous devriez obtenir à peu de choses près la figure suivante.
Notre calculatrice
Voyons maintenant ce dont nous avons besoin pour parvenir à nos fins :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
autant de conteneurs que nécessaire ;
un booléen pour savoir si nous devons e acer ce qui figure à l'écran et écrire un nouveau nombre ;
nous allons utiliser une variable de type double pour nos calculs ;
Pour alléger le nombre de classes internes, vous pouvez en créer une qui se chargera d'écrire ce qui doit être a iché à l'écran. Utilisez la méthode getSource()
pour savoir sur quel bouton on a cliqué.
Je ne vais pas tout vous dire, il faut que vous cherchiez par vous-mêmes : la réflexion est très importante ! En revanche, vous devez savoir que la correction que je
vous fournis n'est pas la correction. Il y a plusieurs solutions possibles. Je vous propose seulement l'une d'elles.
Allez, au boulot !
Correction
Vous avez bien réfléchi ? Vous vous êtes brûlé quelques neurones ? Vous avez mérité votre correction ! Regardez bien comment tout interagit, et vous
comprendrez comment fonctionne ce code.
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
public Calculatrice(){
[Link](240, 260);
[Link]("Calculette");
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link](false);
//On initialise le conteneur avec tous les composants
initComposant();
//On ajoute le conteneur
[Link](container);
[Link](true);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
ecran = new JLabel("0");
[Link](police);
//On aligne les informations à droite dans le JLabel
[Link]([Link]);
[Link](new Dimension(220, 20));
JPanel operateur = new JPanel();
[Link](new Dimension(55, 225));
JPanel chiffre = new JPanel();
[Link](new Dimension(165, 225));
JPanel panEcran = new JPanel();
[Link](new Dimension(220, 30));
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](tab_button[i]);
break;
case 14 :
tab_button[i].addActionListener(new MoinsListener());
tab_button[i].setPreferredSize(dim2);
[Link](tab_button[i]);
break;
case 15 :
tab_button[i].addActionListener(new MultiListener());
tab_button[i].setPreferredSize(dim2);
[Link](tab_button[i]);
break;
case 16 :
tab_button[i].addActionListener(new DivListener());
tab_button[i].setPreferredSize(dim2);
[Link](tab_button[i]);
break;
default :
//Par défaut, ce sont les premiers éléments du tableau
//donc des chiffres, on affecte alors le bon listener
[Link](tab_button[i]);
tab_button[i].addActionListener(new ChiffreListener());
break;
}
}
[Link](ecran);
[Link]([Link]([Link]));
[Link](panEcran, [Link]);
[Link](chiffre, [Link]);
[Link](operateur, [Link]);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private void calcul(){
if([Link]("+")){
chiffre1 = chiffre1 +
[Link]([Link]()).doubleValue();
[Link]([Link](chiffre1));
}
if([Link]("-")){
chiffre1 = chiffre1 -
[Link]([Link]()).doubleValue();
[Link]([Link](chiffre1));
}
if([Link]("*")){
chiffre1 = chiffre1 *
[Link]([Link]()).doubleValue();
[Link]([Link](chiffre1));
}
if([Link]("/")){
try{
chiffre1 = chiffre1 /
[Link]([Link]()).doubleValue();
[Link]([Link](chiffre1));
} catch(ArithmeticException e) {
[Link]("0");
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
if(update){
update = false;
}
else{
if(![Link]().equals("0"))
str = [Link]() + str;
}
[Link](str);
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Je vais vous donner une petite astuce afin de créer un .jar exécutable en Java.
C'est exact pour peu que vous ayez ajouté les exécutables de votre JRE (présents dans le répertoire bin ) dans votre variable d'environnement PATH ! Si
ce n'est pas le cas, refaites un tour dans le premier chapitre du livre, section « Compilation en ligne de commande », et remplacez le répertoire du JDK par
celui du JRE (si vous n'avez pas téléchargé le JDK ; sinon, allez récupérer ce dernier).
La création d'un .jar est un jeu d'enfant. Commencez par e ectuer un clic droit sur votre projet et choisissez l'option Export , comme le montre la figure
suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Exporter son projet
Vous voici dans la gestion des exports. Eclipse vous demande quel type d'export vous souhaitez réaliser, comme à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Type d'export à choisir
Comme l'illustre la figure précédent, sélectionnez JAR File puis cliquez sur Next . Vous voici maintenant dans la section qui vous demande les fichiers que
vous souhaitez inclure dans votre archive, comme à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Choix des fichiers à inclure
Dans le premier cadre, sélectionnez tous les fichiers qui composeront votre exécutable .jar .
Dans le second cadre, indiquez à Eclipse l'endroit où créer l'archive et le nom vous souhaitez lui donner.
La page suivante n'est pas très pertinente ; je la mets cependant en figure suivante afin de ne perdre personne.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Choix du niveau d'erreurs tolérable
Cliquez sur Next : vous arrivez sur la page qui vous demande de spécifier l'emplacement de la méthode main dans votre programme (figure suivante).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Choix du point de départ du programme
Cliquez sur Browse… pour a icher un pop-up listant les fichiers des programmes contenant une méthode main . Ici, nous n'en avons qu'une (voir figure
suivante). Souvenez-vous qu'il est possible que plusieurs méthodes main soient déclarées, mais une seule sera exécutée !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Notre méthode main
Sélectionnez le point de départ de votre application et validez. La figure suivante correspond à ce que vous devriez obtenir.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Récapitulatif d'export
Vous pouvez maintenant cliquer sur Finish et voir s'a icher un message ressemblant à celui de la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Message lors de l'export
Ce type de message n'est pas alarmant : il vous signale qu'il existe des éléments qu'Eclipse ne juge pas très clairs. Ils n'empêcheront toutefois pas votre
application de fonctionner, contrairement à un message d'erreur que vous repérerez facilement : il est en rouge.
Une fois cette étape validée, vous pouvez voir avec satisfaction qu'un fichier .jar a bien été généré dans le dossier spécifié, comme à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Nous en étions restés à notre animation qui bloque, et je vous avais dit que la solution était d'utiliser un deuxième Thread . Dans ce chapitre, nous allons
voir comment créer une (ou plusieurs) nouvelle(s) pile(s) de fonctions grâce à ces fameux threads. Il existe une classe Thread dans Java permettant leur
gestion. Vous allez voir qu'il existe deux façons de créer un nouveau thread.
Non, vous ne rêvez pas : il s'agit bien de notre méthode main , le thread principal de notre application !
Voyez un thread comme une machine bien huilée capable d'e ectuer les tâches que vous lui spécifiez. Une fois instancié, un thread attend son lancement. Dès
que c'est fait, il invoque sa méthode run() qui va lui permettre de connaître les tâches qu'il a à e ectuer.
Nous allons maintenant apprendre à créer un nouveau thread. Je l'avais mentionné dans l'introduction, il existe deux manières de faire :
créer une implémentation de l'interface Runnable et instancier un objet Thread avec l'implémentation de cette interface.
Comme je vous le disais, nous allons opter pour la première solution. Tout ce que nous avons à faire, c'est redéfinir la méthode run() de notre objet afin qu'il
sache ce qu'il doit faire. Puisque nous allons en utiliser plusieurs, autant pouvoir les di érencier : nous allons leur donner des noms.
Créons donc une classe gérant tout cela qui contient un constructeur comprenant un String en paramètre pour spécifier le nom du thread. Cette classe doit
également comprendre une méthode getName() afin de retourner ce nom. La classe Thread se trouvant dans le package [Link] , aucune instruction
import n'est nécessaire. En voici le code :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class TestThread extends Thread {
public TestThread(String name){
super(name);
}
public void run(){
for(int i = 0; i < 10; i++)
[Link]([Link]());
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Essai de plusieurs Thread
Vous pouvez voir que l'ordre d'exécution est souvent aléatoire, car Java utilise un ordonnanceur. Vous devez savoir que si vous utilisez plusieurs threads dans une
application, ceux-ci ne s'exécutent pas toujours en même temps ! En fait, l'ordonnanceur gère les threads de façon aléatoire : il va en faire tourner un pendant un
certain temps, puis un autre, puis revenir au premier, etc., jusqu'à ce qu'ils soient terminés. Lorsque l'ordonnanceur passe d'un thread à un autre, le thread
interrompu est mis en sommeil tandis que l'autre est en éveil.
Notez qu'avec les processeurs multi-coeurs aujourd'hui, il est désormais possible d'exécuter deux tâches exactement en même temps. Tout dépend donc
de votre ordinateur.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
TERMINATED : lorsque le thread a e ectué toutes ses tâches ; on dit aussi qu'il est « mort ». Vous ne pouvez alors plus le relancer par la méthode start() .
TIMED_WAITING : lorsque le thread est en pause (quand vous utilisez la méthode sleep() , par exemple).
BLOCKED : lorsque l'ordonnanceur place un thread en sommeil pour en utiliser un autre, il lui impose cet état.
Un thread est considéré comme terminé lorsque la méthode run() est ôtée de sa pile d'exécution. En e et, une nouvelle pile d'exécution contient à sa base la
méthode run() de notre thread. Une fois celle-ci dépilée, notre nouvelle pile est détruite !
En fait, le thread principal crée un second thread qui se lance et construit une pile dont la base est sa méthode run() ; celle-ci appelle une méthode, l'empile,
e ectue toutes les opérations demandées, et une fois qu'elle a terminé, elle dépile cette dernière. La méthode run() prend fin, la pile est alors détruite.
Nous allons modifier notre classe TestThread afin d'a icher les états de nos threads que nous pouvons récupérer grâce à la méthode getState() .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("statut " + [Link]() + " = " +[Link]());
if(t != null)
[Link]("statut de " + [Link]() + " pendant le thread " + [Link]() +" = " +[Link]());
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Test avec plusieurs threads simultanés
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Dans notre classe TestThread , nous avons ajouté quelques instructions d'a ichage afin de visualiser l'état courant de nos objets. Mais nous avons aussi ajouté
un constructeur supplémentaire prenant un Thread en paramètre afin d'obtenir l'état de notre premier thread lors de l'exécution du second.
Dans le jeu d'essais, vous pouvez voir les di érents statuts qu'ont pris les threads. Ainsi, le premier est dans l'état BLOCKED lorsque le second est en cours de
traitement, ce qui justifie ce que je vous disais : les threads ne s'exécutent pas en même temps !
Vous pouvez voir aussi que les opérations e ectuées par nos threads sont en fait codées dans la méthode run() . Reprenez l'image que j'ai montrée
précédemment : « un thread est une machine bien huilée capable d'e ectuer les tâches que vous lui spécifiez ». Faire hériter un objet de Thread permet de créer
un nouveau thread très facilement. Vous pouvez cependant procéder di éremment : redéfinir uniquement ce que doit e ectuer le nouveau thread grâce à
l'interface Runnable . 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 !
Trêve de bavardages : codons notre implémentation de Runnable . Vous ne devriez avoir aucun problème à y parvenir, sachant qu'il n'y a que la méthode
run() à redéfinir.
Afin d'illustrer cela, nous allons utiliser un exemple que j'ai trouvé intéressant lorsque j'ai appris à me servir des threads : nous allons créer un objet
CompteEnBanque contenant une somme d'argent par défaut (disons 100), une méthode pour retirer de l'argent ( retraitArgent() ) et une méthode
retournant le solde ( getSolde() ). Cependant, avant de retirer de l'argent, nous vérifierons que nous ne sommes pas à découvert… Notre thread va e ectuer
autant d'opérations que nous le souhaitons. La figure suivante représente le diagramme de classes résumant la situation.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Thread et compte en banque
Je résume :
dans notre cas, les objets Thread contiendront une implémentation de Runnable : RunImpl ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](2);
[Link]("Retrait effectué");
}
}
}
}
[Link]
return [Link];
}
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Rien d'extraordinaire ici, une simple boucle aurait fait la même chose. Ajoutons un nom à notre implémentation et créons un deuxième thread utilisant un
deuxième compte. Il faut penser à modifier l'implémentation afin que nous puissions connaître le thread qui travaille :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public void run() {
for(int i = 0; i < 50; i++){
if([Link]() > 0){
[Link](2);
[Link]("Retrait effectué par " + [Link]);
}
}
}
}
Jusqu'ici, rien de perturbant : nous avons utilisé deux instances distinctes de RunImpl utilisant elles-mêmes deux instances distinctes de CompteEnBanque .
Mais que se passerait-il si nous utilisions la même instance de CompteEnBanque pour deux threads di érents ? Testez plusieurs fois le code que voici :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Retrait multithreadé
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous pouvez voir des incohérences monumentales ! J'imagine que vous pensiez comme moi que le compte aurait été débité par pas de deux jusqu'à la fin sans
obtenir d'aberrations de ce genre, puisque nous utilisons le même objet… Eh bien, non ! Pourquoi ? Tout simplement parce que l'ordonnanceur de Java place les
threads en sommeil quand il le désire, et lorsque le thread qui était en sommeil se réveille, il reprend son travail là où il l'avait laissé !
Voyons comment résoudre ce problème.
Il vous su it d'ajouter dans la déclaration de la méthode le mot clé synchronized , grâce auquel la méthode est inaccessible à un thread si elle est déjà utilisée
par un autre thread. Ainsi, les threads cherchant à utiliser des méthodes déjà prises en charge par un autre thread sont placés dans une « liste d'attente ».
Je récapitule une nouvelle fois, en me servant d'un exemple simple. 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 :
je commence par demander une baguette : la boulangère me la pose sur le comptoir et commence à calculer le montant ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
c'est là que le thread B (vous) cherche aussi à utiliser la méthode M ; cependant, elle est déjà occupée par un thread (moi) ;
l'action revient sur moi (thread A) ; au moment de payer, je dois chercher de la monnaie dans ma poche ;
l'action revient sur le thread B (vous)… mais la méthode M n'est toujours pas libérée du thread A, vous êtes donc remis en attente ;
on revient sur le thread A qui arrive enfin à payer et à quitter la boulangerie : la méthode M est maintenant libérée ;
et ainsi de suite.
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
//Le constructeur n'a pas changé
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](true);
}
}
Voilà, vous avez enfin le contrôle sur votre animation ! Nous allons à présent pouvoir l'agrémenter un peu dans les chapitres suivants.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
la machine qui exécutera la tâche devra posséder un processeur à plusieurs cœurs (2, 4 ou plus) ;
s'assurer qu'il y a un réel gain de performance ! Dans certains cas, découper une tâche rend le traitement plus long.
En guise d'exemple, je vous propose de coder une recherche de fichiers (simplifiée au maximum pour ne pas surcharger le code). Voici les classes que nous allons
utiliser, pour le moment sans la gestion Fork/Join :
[Link]
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
public FolderScanner(){ }
public FolderScanner(Path p, String f){
path = p;
filter = f;
}
/**
* Méthode qui se charge de scanner les dossiers de façon récursive
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
* @throws ScanException
*/
public long sequentialScan() throws ScanException{
//Si le chemin n'est pas valide, on lève une exception
if(path == null || [Link](""))
throw new ScanException("Chemin à scanner non valide (vide ou null) !");
[Link]("Scan du dossier : " + path + " à la recherche des fichiers portant l'extension " + [Link]);
return result;
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
public class Main {
try {
Long start = [Link]();
Long resultat = [Link]();
Long end = [Link]();
[Link]("Il y a " + resultat + " fichier(s) portant l'extension " + filtre);
[Link]("Temps de traitement : " + (end - start));
} catch (ScanException e) {
[Link]();
}
}
}
Lorsque je lance ce code le temps de traitement est vraiment long (j'ai beaucoup de dossiers dans mes documents :-° ), comme le montre la figure suivante.
Nous pouvons voir qu'il est possible de découper le scan de chaque dossier dans une sous-tâche, et c'est exactement ce que nous allons faire. Pour ce faire, nous
devons faire hériter notre classe FolderScanner d'une des classes permettant ce découpage. La plateforme Java 7 nous met à disposition deux classes qui
héritent de la classe abstraite ForkJoinTask<V> :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
RecursiveAction : classe permettant de découper une tâche ne renvoyant aucune valeur particulière. Elle hérite de ForkJoinTask<Void> ;
RecursiveTask<V> : identique à la classe précédente mais retourne une valeur, de type <V> , en fin de traitement. C'est cette classe que nous allons
utiliser pour pouvoir nous retourner le nombre de fichiers trouvés.
Nous allons devoir utiliser, en plus de l'objet de découpage, un objet qui aura pour rôle de superviser l'exécution des tâches et sous-tâches afin de pouvoir
fusionner les threads en fin de traitement : ForkJoinPool .
Avant de vous présenter le code complet, voici comment ça fonctionne. Les objets qui permettent le découpage en sous-tâches fournissent trois méthodes qui
permettent cette gestion :
compute() : méthode abstraite à redéfinir dans l'objet héritant afin de définir le traitement à e ectuer ;
fork() : méthode qui crée un nouveau thread dans le pool de thread ( ForkJoinPool ) ;
Ces classes nécessitent que vous redéfinissiez la méthode compute() afin de définir ce qu'il y a à faire. La figure suivante est un schéma représentant la façon
dont les choses se passent.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Concrètement, avec notre exemple, voici ce qu'il va se passer :
notre objet qui sert à scanner le contenu va vérifier le contenu pour voir s'il n'y a pas de sous-dossiers ;
pour chaque sous-dossier, nous allons créer une nouvelle tâche et la lancer ;
nous allons compter le nombre de fichiers qui correspond à nos critères dans le dossier en cours de scan ;
nous allons récupérer le nombre de fichiers trouvés par les exécutions en tâche de fond ;
Pour que vous compreniez bien, voici une partie de mon dossier Mes Documents :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Voici comment va s'exécuter le code
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class FolderScanner extends RecursiveTask<Long>{
public FolderScanner(){ }
public FolderScanner(Path p, String f){
path = p;
filter = f;
}
/**
* Notre méthode de scan en mode mono thread
* @throws ScanException
*/
public long sequentialScan() throws ScanException{
//Si le chemin n'est pas valide, on lève une exception
if(path == null || [Link](""))
throw new ScanException("Chemin à scanner non valide (vide ou null) !");
[Link]("Scan du dossier : " + path + " à la recherche des fichiers portant l'extension " + [Link]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
} catch (IOException e) { [Link](); }
return result;
}
/**
* Méthode que nous allons utiliser pour les traitements
* en mode parallèle.
* @throws ScanException
*/
public long parallelScan() throws ScanException{
//List d'objet qui contiendra les sous-tâches créées et lancées
List<FolderScanner> list = new ArrayList<>();
[Link]("Scan du dossier : " + path + " a la recherche des fichiers portant l'extension " + [Link]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
if([Link]([Link]())){
//Nous créons donc un nouvel objet FolderScanner
//Qui se chargera de scanner ce dossier
FolderScanner f = new FolderScanner([Link](), [Link]);
//Nous l'ajoutons à la liste des tâches en cours pour récupérer le résultat plus tard
[Link](f);
//C'est cette instruction qui lance l'action en tâche de fond
[Link]();
}
}
} catch (IOException e) {
[Link]();
}
//On compte maintenant les fichiers, correspondant au filtre, présents dans ce dossier
try(DirectoryStream<Path> listing = [Link](path, [Link])){
for(Path nom : listing){
result++;
}
} catch (IOException e) {
[Link]();
}
/**
* Méthode qui défini l'action à faire
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
* dans notre cas, nous lan çons le scan en mode parallèles
*/
protected Long compute() {
long resultat = 0;
try {
resultat = [Link]();
} catch (ScanException e) {
[Link]();
}
return resultat;
}
import [Link];
import [Link];
import [Link];
//Création de notre tâche principale qui se charge de découper son travail en sous-tâches
FolderScanner fs = new FolderScanner(chemin, filtre);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Long start = [Link]();
Pour vous donner un ordre d'idée, le scan en mode mono thread de mon dossier Mes Documents prend en moyenne 2 minutes alors que le temps moyen en
mode Fork/Join est d'environ… 10 secondes ! Pas mal, hein ?
La figure suivante représente l'utilisation de mes processeurs.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous constaterez que l'utilisation de ce mode est très gourmand en ressource processeurs. Il est donc à utiliser avec parcimonie.
Dans cet exemple nous avons créé dynamiquement autant de threads que nécessaires pour traiter nos tâches. Vous n'aurez peut-être pas besoin de faire ceci
pour des problèmes où seulement 2 ou 3 sous-tâches su isent, surtout si vous le savez à l'avance. L'idée maîtresse revient à définir un seuil au delà duquel le
traitement se fera en mode Fork/join, sinon, il se fera dans un seul thread (je vous rappelle qu'il se peut que ce mode de fonctionnement soit plus lent et
consommateur qu'en mode normal). Voici comment procéder dans ce genre de cas :
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
CalculSuite calcul2 = new CalculSuite(debut + milieu, fin);
resultat = [Link]() + [Link]();
}
return resultat;
}
Vous pouvez voir que ce code fonctionne très bien même si son intérêt n'est que pédagogique.
La classe Thread et l'interface Runnable se trouvent dans le package [Link] , aucun import spécifique n'est donc nécessaire pour leur utilisation.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les opérations que vous souhaitez e ectuer dans une autre pile d'exécution sont à placer dans la méthode run() , qu'il s'agisse d'une classe héritant de
Thread ou d'une implémentation de Runnable .
Pour protéger l'intégrité des données accessibles à plusieurs threads, utilisez le mot clé synchronized dans la déclaration de vos méthodes.
Un thread est déclaré mort lorsqu'il a dépilé la méthode run() de sa pile d'exécution.
Les threads peuvent présenter plusieurs états : NEW , RUNNABLE , BLOCKED , WAITING , TIMED_WAITING et TERMINATED .
Dans cet exemple, nous aurons bien sûr besoin d'une liste, faites-en une. Cependant, vous ne manquerez pas de constater que notre objet est ridiculement petit.
Vous connaissez le remède : il su it de lui spécifier une taille !
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JComboBox combo = new JComboBox();
private JLabel label = new JLabel("Une ComboBox");
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
[Link](new Dimension(100, 20));
En revanche, cette liste est vide ! Pour résoudre ce problème, il su it d'utiliser la méthode addItem(Object obj) .
Sachez que lorsque l'objet a iche les éléments ajoutés, il appelle leur méthode toString() . Dans cet exemple, nous avons utilisé des objets String ,
mais essayez avec un autre objet et vous constaterez le résultat…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class Fenetre extends JFrame {
//Les variables d'instance restent inchangées
public Fenetre(){
//…
[Link](new Dimension(100, 20));
[Link]("Option 1");
[Link]("Option 2");
[Link]("Option 3");
[Link]("Option 4");
//…
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Pour initialiser une JComboBox , vous pouvez utiliser le constructeur prenant un tableau d'objets en paramètre afin de renseigner tous les éléments d'un coup.
Ceci est donc équivalent au code précédent :
String[] tab = {"Option 1", "Option 2", "Option 3", "Option 4"};
combo = new JComboBox(tab);
Vous pouvez assigner un choix par défaut avec la méthode setSelectedIndex(int index) . Vous avez aussi la possibilité de changer la couleur du texte, la
couleur de fond ou la police, exactement comme avec un JLabel .
Depuis Java 7, l'objet JComboBox peut être paramétré avec un type générique, comme ceci : JComboBox<String> combo = new JComboBox<String>(); ce
qui permet de mieux gérer le contenu de nos listes et ainsi mieux récupérer les valeurs de ces dernières.
Un autre objet dont nous ne parlerons pas accepte aussi un type paramétré, l'objet JList<E> . Celui-ci étant très proche de l'objet JComboBox<E> , nous
n'en parlerons pas ici mais maintenant vous savez qu'il existe.
Maintenant que nous savons comment fonctionne cet objet, nous allons apprendre à communiquer avec lui.
L'interface ItemListener
Cette interface possède une méthode à redéfinir. Celle-ci est appelée lorsqu'un élément a changé d'état. Puisqu'un exemple est toujours plus éloquent, voici un
code implémentant cette interface :
public Fenetre(){
//Le début ne change pas
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Ajout du listener
[Link](new ItemState());
[Link](new Dimension(100, 20));
[Link]([Link]);
Dans mon exemple, j'ai cliqué sur Option 2 , puis Option 3 , puis Option 4 , ce qui correspond à la figure suivante.
Vous voyez que lorsque nous cliquons sur une autre option, notre objet commence par modifier l'état de l'option précédente (l'état passe en DESELECTED ) avant
de changer celui de l'option choisie (celle-ci passe à l'état SELECTED ). Nous pouvons donc suivre très facilement l'état de nos éléments grâce à cette interface ;
cependant, pour plus de simplicité, nous utiliserons l'interface ActionListener afin de récupérer l'option sélectionnée.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Les autres imports
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
ActionListener et JComboBox
Vous constatez qu'en utilisant cette méthode, nous pouvons récupérer l'option sur laquelle l'action a été e ectuée. L'appel de la méthode getSelectedItem()
retourne la valeur de l'option sélectionnée ; une fois récupérée, nous pouvons travailler avec notre liste !
Maintenant que nous savons comment récupérer les informations dans une liste, je vous invite à continuer notre animation.
Notre méthode paintComponent() doit pouvoir dessiner la forme demandée ; ainsi, trois cas de figure se profilent :
soit nous intégrons les instructions if dans cette méthode et l'objet Graphics dessinera en fonction de la variable ;
soit nous développons une méthode privée appelée dans la méthode paintComponent() et qui dessinera la forme demandée ;
soit nous utilisons le pattern strategy afin d'encapsuler la façon dont nous dessinerons nos formes dans notre animation.
Le pattern strategy est de loin la meilleure solution, mais afin de ne pas alourdir nos exemples, nous travaillerons « à l'ancienne ».
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 dans la méthode paintComponent() de sorte que cette dernière puisse l'utiliser ; c'est donc dans cette méthode que nous placerons nos
conditions.
Je vous propose les formes suivantes :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
le rond, forme par défaut ;
le carré ;
le triangle ;
Cela signifie que notre liste contiendra ces quatre choix et que le rond figurera en premier lieu. Nous créerons aussi une implémentation d' ActionListener
dans une classe interne pour gérer les actions de notre liste. Je l'ai appelée FormeListener (c'est fou ce que je suis original).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Di érents rendus de l'application
Essayez de réaliser ces formes vous-mêmes : il n'y a là rien de compliqué, je vous assure ! Bon, l'étoile est peut-être un peu plus complexe que les autres, mais ce
n'est pas insurmontable.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Classe Panneau
import [Link];
import [Link];
import [Link];
import [Link];
import [Link].Graphics2D;
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
if([Link]("TRIANGLE")){
//Calcul des sommets
//Le sommet 1 se situe à la moitié du côté supérieur du carré
int s1X = posX + 25;
int s1Y = posY;
//Le sommet 2 se situe en bas à droite
int s2X = posX + 50;
int s2Y = posY + 50;
//Le sommet 3 se situe 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};
//Nous utilisons la méthode fillPolygon()
[Link](ptsX, ptsY, 3);
}
if([Link]("ETOILE")){
//Pour l'étoile, on se 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;
[Link](s1X, s1Y, s2X, s2Y);
int s3X = posX;
int s3Y = posY + 17;
[Link](s2X, s2Y, s3X, s3Y);
int s4X = posX + 50;
int s4Y = posY + 17;
[Link](s3X, s3Y, s4X, s4Y);
int s5X = posX;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
int s5Y = posY + 50;
[Link](s4X, s4Y, s5X, s5Y);
[Link](s5X, s5Y, s1X, s1Y);
}
}
Classe Fenetre
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
[Link](pan, [Link]);
[Link](new BoutonListener());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](new Bouton2Listener());
[Link](false);
JPanel south = new JPanel();
[Link](bouton);
[Link](bouton2);
[Link](south, [Link]);
[Link]("ROND");
[Link]("CARRE");
[Link]("TRIANGLE");
[Link]("ETOILE");
[Link](new FormeListener());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]();
try {
[Link](3);
} catch (InterruptedException e) {
[Link]();
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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)
[Link]([Link]().toString());
}
}
}
Et voilà le travail ! Vous avez vu : il n'y avait rien de sorcier. En fait, étant donné que vous avez l'habitude d'utiliser des objets graphiques et des implémentations
d'interfaces, les choses vont maintenant s'accélérer, car le principe est le même pour tous les objets graphiques de base.
Nous pouvons également cocher la case par défaut en appelant la méthode setSelected(Boolean bool) à laquelle nous passons true . Cet objet possède,
comme tous les autres, une multitude de méthodes nous simplifiant la vie ; je vous invite aussi à fouiner un peu…
Nous créerons directement une implémentation de l'interface ActionListener , vous connaissez bien la démarche. Contrôlons également que notre objet est
coché à l'aide de la méthode isSelected() qui retourne un booléen. Voici un code mettant tout cela en œuvre :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
JPanel top = new JPanel();
[Link](new StateListener());
[Link](new StateListener());
[Link](check1);
[Link](check2);
[Link](top, [Link]);
[Link](container);
[Link](true);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Nos cases à cocher
Ici, je me suis amusé à cocher et décocher mes cases. Il n'y a rien de bien di icile, ça devient routinier, non ?
En ce qui concerne la taille, si on la réduit ou l'augmente d'une unité à chaque rafraîchissement, l'e et de morphing sera ultra rapide. Donc, pour ralentir l'e et,
nous utiliserons une méthode retournant 1 ou 0 selon le nombre de rafraîchissements. Cela implique que nous aurons besoin d'une variable pour les dénombrer.
Nous e ectuerons une augmentation ou une réduction toutes les dix fois.
Pour bien séparer les deux cas de figure, nous insérerons 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 la case, le morphing s'activera, et il se désactivera une fois décochée. La classe Panneau devra donc disposer d'un mutateur pour le
booléen de morphing.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Souvenez-vous que nous gérons la collision avec les bords dans notre classe Fenetre . Cependant, en « mode morphing », la taille de notre forme n'est plus
constante : il faudra gérer ce nouveau cas de figure dans notre méthode go() . Notre classe Panneau devra posséder un accesseur permettant de retourner la
taille actuelle de la forme.
Vous avez désormais toutes les clés en main pour réussir cette animation.
La figure suivante donne un aperçu de ce que vous devriez obtenir (je n'ai représenté que le rond et le triangle, mais ça fonctionne avec toutes les formes).
Morphing
Fichier [Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link].Graphics2D;
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Un booléen pour le mode morphing
//Un autre pour savoir si la taille doit être réduite
private boolean morph = false, reduce = false;
private String forme = "ROND";
//Le compteur de rafraîchissements
private int increment = 0;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
int[] ptsX = {s1X, s2X, s3X};
int[] ptsY = {s1Y, s2Y, s3Y};
[Link](ptsX, ptsY, 3);
}
if([Link]("ETOILE")){
int s1X = posX + 50/2;
int s1Y = posY;
int s2X = posX + 50;
int s2Y = posY + 50;
[Link](s1X, s1Y, s2X, s2Y);
int s3X = posX;
int s3Y = posY + 50/3;
[Link](s2X, s2Y, s3X, s3Y);
int s4X = posX + 50;
int s4Y = posY + 50/3;
[Link](s3X, s3Y, s4X, s4Y);
int s5X = posX;
int s5Y = posY + 50;
[Link](s4X, s4Y, s5X, s5Y);
[Link](s5X, s5Y, s1X, s1Y);
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
drawSize = drawSize + getUsedSize();
if([Link]("ROND")){
[Link](posX, posY, drawSize, drawSize);
}
if([Link]("CARRE")){
[Link](posX, posY, drawSize, drawSize);
}
if([Link]("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};
[Link](ptsX, ptsY, 3);
}
if([Link]("ETOILE")){
int s1X = posX + drawSize/2;
int s1Y = posY;
int s2X = posX + drawSize;
int s2Y = posY + drawSize;
[Link](s1X, s1Y, s2X, s2Y);
int s3X = posX;
int s3Y = posY + drawSize/3;
[Link](s2X, s2Y, s3X, s3Y);
int s4X = posX + drawSize;
int s4Y = posY + drawSize/3;
[Link](s3X, s3Y, s4X, s4Y);
int s5X = posX;
int s5Y = posY + drawSize;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](s4X, s4Y, s5X, s5Y);
[Link](s5X, s5Y, s1X, s1Y);
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public int getPosX() {
return posX;
}
Fichier [Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
[Link](pan, [Link]);
[Link](new BoutonListener());
[Link](new Bouton2Listener());
[Link](false);
JPanel south = new JPanel();
[Link](bouton);
[Link](bouton2);
[Link](south, [Link]);
[Link]("ROND");
[Link]("CARRE");
[Link]("TRIANGLE");
[Link]("ETOILE");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](new FormeListener());
[Link](new MorphListener());
if(!backX) [Link](++x);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
else [Link](--x);
if(!backY) [Link](++y);
else [Link](--y);
[Link]();
try {
[Link](3);
} catch (InterruptedException e) {
[Link]();
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Alors, qu'en pensez-vous ? J'aime bien, moi… Vous voyez, l'utilisation des JCheckBox est très simple. Je vous propose maintenant d'étudier un de ses cousins !
Le voici, le cousin éloigné… Le principe est de proposer au moins deux choix, mais de ne permettre d'en sélectionner qu'un à la fois. L'instanciation se fait de la
même manière que pour un JCheckBox ; d'ailleurs, nous utiliserons l'exemple du début de ce chapitre en remplaçant les cases à cocher par des boutons radio.
Voici le code correspondant :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
JPanel top = new JPanel();
[Link](new StateListener());
[Link](new StateListener());
[Link](jr1);
[Link](jr2);
[Link](top, [Link]);
[Link](container);
[Link](true);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Test avec un groupe de boutons
Vous pouvez voir que cet objet s'utilise de la même manière que le précédent. Le problème, ici, c'est que nous pouvons sélectionner les deux options (alors que ce
n'est normalement pas possible). Pour qu'un seul bouton radio soit sélectionné à la fois, nous devons définir un groupe de boutons à l'aide de ButtonGroup .
Nous y ajouterons nos boutons radio, et seule une option pourra alors être sélectionnée.
public Fenetre(){
//Les autres instructions
[Link](true);
[Link](new StateListener());
[Link](new StateListener());
//On ajoute les boutons au groupe
[Link](jr1);
[Link](jr2);
[Link](jr1);
[Link](jr2);
[Link](top, [Link]);
[Link](container);
[Link](true);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Les imports habituels
import [Link];
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
JPanel top = new JPanel();
Font police = new Font("Arial", [Link], 14);
[Link](police);
[Link](new Dimension(150, 30));
[Link]([Link]);
[Link](label);
[Link](jtf);
[Link](top, [Link]);
[Link](container);
[Link](true);
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Exemple de champ de texte
Nous pouvons initialiser le contenu avec la méthode setText(String str) ou le récupérer grâce à la méthode getText() .
Il existe un objet très ressemblant à celui-ci, en un peu plus éto é. En fait, cet objet permet de créer un JTextField formaté pour recevoir un certain type de
données saisies (date, pourcentage etc.). Voyons cela tout de suite.
Grâce à ce type d'objet, nous pourrons éviter beaucoup de contrôles et de casts sur le contenu de nos 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 texte qu'il contenait importait peu, mais un
jour, vous aurez sans doute besoin d'une zone de texte qui n'accepte qu'un certain type de données. Avec l'objet JFormattedTextField , nous nous en
approchons (mais vous verrez que vous pourrez faire encore mieux). Cet objet retourne une valeur uniquement si celle-ci correspond à ce que vous avez autorisé.
Je m'explique : si vous voulez que votre zone de texte contienne par exemple des entiers et rien d'autre, c'est possible ! En revanche, ce contrôle ne s'e ectue que
lorsque vous quittez le champ en question. Vous pouvez ainsi saisir des lettres dans un objet n'acceptant que des entiers, mais la méthode getText() ne
renverra alors rien, car le contenu sera e acé, les données ne correspondent pas aux attentes de l'objet. Voici un code et deux exemples, ainsi que leur rendu
(figure suivante).
public Fenetre(){
[Link]("Animation");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
JPanel top = new JPanel();
Font police = new Font("Arial", [Link], 14);
[Link](police);
[Link](new Dimension(150, 30));
[Link]([Link]);
[Link](new Dimension(150, 30));
[Link](new BoutonListener());
[Link](label);
[Link](jtf);
[Link](jtf2);
[Link](b);
[Link](top);
[Link](true);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Exemple valide à gauche et invalide à droite
Vous voyez qu'en plus, notre objet met automatiquement la saisie en forme lorsqu'elle est valide : il espace les nombres tous les trois chi res afin d'en faciliter la
lecture.
Voici ce que vous pouvez utiliser dans ce genre de champ :
NumberFormat avec :
getIntegerInstance()
getPercentInstance()
getNumberInstance()
DateFormat avec
getTimeInstance()
getDateInstance()
MessageFormat
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Sans entrer dans les détails, vous pouvez aussi utiliser un objet MaskFormatter qui permet d'attribuer un format de longueur fixe à votre zone de texte. C'est
très pratique lorsque vous souhaitez introduire un numéro de téléphone, un numéro de sécurité sociale etc.
Vous devez définir ce format avec un paramètre lors de l'instanciation du masque à l'aide de métacaractères. Ceux-ci indiquent à votre objet MaskFormatter ce
que le contenu de votre zone de texte contiendra. Voici la liste de ces métacaractères :
# : indique un chi re ;
L'instanciation d'un tel objet peut lever une ParseException . Vous devez donc l'entourer d'un bloc try{…}catch(ParseException e){…} .
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){[Link]();}
Vous voyez qu'il n'y a là rien de compliqué. Je vous invite à essayer cela dans le code précédent, vous constaterez qu'avec le métacaractère utilisé dans notre
objet MaskFormatter , nous sommes obligés de saisir des chi res. La figure suivante montre le résultat après avoir cliqué sur le bouton.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Essai avec un MaskFormatter
Je ne sais pas pour le numéro de téléphone américain, mais le numéro français est loin d'être un numéro de téléphone valide. Nous voici confrontés à un
problème qui nous hantera tant que nous programmerons : l'intégrité de nos données !
Comme le montre l'exemple précédent, nous pouvons suggérer à l'utilisateur ce qu'il doit renseigner comme données dans les champs, mais nous ne devons pas
lui faire aveuglément confiance ! C'est simple : on part du principe de ne jamais faire confiance à l'utilisateur.
Nous sommes donc obligés d'e ectuer une multitude de contrôles supplémentaires. Pour ce faire, nous pouvons :
dans le cas où nous n'utilisons pas de MaskFormatter , vérifier en plus que les saisies sont numériques ;
etc.
En gros, nous devons vérifier l'intégrité de nos données (dans le cas qui nous intéresse, l'intégrité de nos chaînes de caractères) pendant ou après la saisie. Je ne
vous cache pas que cela prendra une grande part de votre temps lorsque vous coderez vos propres logiciels, mais c'est le métier qui veut ça.
Avant de terminer ce chapitre (assez conséquent, je l'avoue), je vous propose de voir comment nous pouvons récupérer les événements du clavier. Nous avons
appris à interagir avec la souris, mais pas avec le clavier.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.
l'interface ItemListener qui écoute les événements sur une liste déroulante.
Voici à présent l'interface KeyListener . Comme l'indique le titre, elle nous permet d'intercepter les événements clavier lorsque l'on :
Vous savez ce qu'il vous reste à faire : créer une implémentation de cette interface dans votre projet. Créez une classe interne qui l'implémente et utilisez l'astuce
d'Eclipse pour générer les méthodes nécessaires.
Vous constatez qu'il y en a trois :
keyReleased ( KeyEvent event), appelée lorsqu'on relâche une touche (c'est à ce moment que le composant se voit a ecter la valeur de la touche) ;
keyTyped ( KeyEvent event), appelée entre les deux méthodes citées ci-dessus.
Comme vous vous en doutez, l'objet KeyEvent nous permettra d'obtenir des informations sur les touches qui ont été utilisées. Parmi celles-ci, nous utiliserons :
Nous pouvons aussi déterminer lorsque certaines touches de contrôle ont été utilisées ( SHIFT , CTRL …), connaître le composant à l'origine de l'événement,
etc. Nous n'en parlerons pas ici, mais ce genre d'information est facile à trouver sur Internet.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Pour des raisons de simplicité, nous n'utiliserons pas un JFormattedTextField mais un JTextField sans MaskFormatter . Ainsi, nous n'aurons pas à nous
préoccuper des tirets de notre champ.
Pour commencer, nous allons examiner l'ordre dans lequel se déroulent les événements clavier ; il est vrai que ceux-ci se produisent si rapidement que nous
n'avons pas le temps de les voir défiler. J'ai donc ajouté une pause à la fin de chaque méthode de l'implémentation afin de mieux observer l'ordre d'exécution.
Voici le code source que nous allons utiliser (il est presque identique aux précédents, rassurez-vous) :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link]("Animation");
[Link](300, 150);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
jtf = new JTextField();
JPanel top = new JPanel();
[Link](label);
[Link](jtf);
[Link](b);
[Link](top);
[Link](true);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public void keyTyped(KeyEvent event) {
[Link]("Code touche tapée : " + [Link]() + " - caractère touche tapée : " + [Link]());
pause();
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Premier test de l'interface KeyListener
Vous pouvez maintenant vous rendre compte de l'ordre dans lequel les événements du clavier sont gérés : en premier, lorsqu'on presse la touche, en deuxième,
lorsqu'elle est tapée, et enfin, lorsqu'elle est relâchée.
Dans le cas qui nous intéresse, nous souhaitons que lorsque l'utilisateur saisit un caractère interdit, celui-ci soit automatiquement retiré de la zone de saisie. Pour
cela, nous procéderons à un traitement spécifique dans la méthode keyReleased(KeyEvent event) .
Si vous avez e ectué beaucoup de tests de touches, vous avez dû remarquer que les codes des touches correspondant aux chi res du pavé numérique sont
compris entre 96 et 105.
À partir de là, c'est simple : il nous su it de supprimer le caractère tapé de la zone de saisie si son code n'est pas compris dans cet intervalle. Toutefois, un
problème se pose avec cette méthode : ceux qui possèdent un ordinateur portable sans pavé numérique ne pourront rien saisir alors qu'il est possible d'obtenir
des chi res en appuyant sur MAJ + & , é , ' , ( ou - .
Ce souci nous amène à opter pour une autre solution : nous créerons une méthode dont le type de retour sera un booléen nous indiquant si la saisie est
numérique ou non. Comment ? Tout simplement en exécutant un [Link](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 alors
false ( true dans le cas contraire).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
La méthode parseInt() prend un String en paramètre. La méthode getKeyChar() , elle, renvoie un char . Il faudra donc penser à faire la
conversion.
Vous vous apercevez que les lettres simples sont désormais interdites à la saisie : mission accomplie ! Cependant, les caractères spéciaux comme « ô », « ï », etc.
ne sont pas pris en charge par cette méthode. Par conséquent, leur saisie reste possible.
Vous pouvez ajouter des éléments dans une liste avec la méthode addItem(Object obj) .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
L'interface ItemListener permet de gérer les états de vos éléments.
La méthode getSelectedItem() retourne une variable de type Object : pensez donc à e ectuer un cast, ou à utiliser la méthode toString() si les
éléments sont des chaînes de caractères.
Les objets JCheckBox , JRadioButton et ButtonGroup sont présents dans le package [Link] .
Vous pouvez déterminer si l'un de ces composants est sélectionné grâce à la méthode isSelected() . Cette méthode retourne true si l'objet est
sélectionné, false dans le cas contraire.
Vous pouvez restreindre le nombre de choix à un parmi plusieurs réponses en utilisant la classe ButtonGroup .
Vous pouvez ajouter des boutons à un groupe de boutons grâce à la méthode add(AbstractButton button) .
Afin de contrôler les événements clavier, l'utilisation d'une implémentation de l'interface KeyListener est nécessaire.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
a icher une information (message d'erreur, d'avertissement…) ;
etc.
Vous pouvez voir qu'elles peuvent servir à beaucoup de choses. Il faut toutefois les utiliser avec parcimonie : il est assez pénible pour l'utilisateur 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.
La figure suivante vous montre à quoi ressemblent des boîtes de dialogues « informatives ».
Ces boîtes ne sont pas destinées à participer à de quelconques opérations : elles a ichent juste un message à l'attention de l'utilisateur. Voici le code utilisé pour
obtenir ces boîtes :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Boîte du message d'erreur
jop3 = new JOptionPane();
[Link](null, "Message d'erreur", "Erreur", JOptionPane.ERROR_MESSAGE);
Ces trois boîtes ne s'a ichent pas en même temps, tout simplement parce qu'en Java (mais aussi dans les autres langages), les boîtes de dialogue sont dites
modales. Cela signifie que lorsqu'une boîte fait son apparition, celle-ci bloque toute interaction avec un autre composant, et ceci tant que l'utilisateur n'a pas mis
fin au dialogue !
Maintenant, voyons de plus près comment construire un tel objet. Ici, nous avons utilisé la
méthode showMessageDialog(Component parentComponent, String message, String title, int messageType); où :
Component parentComponent : correspond au composant parent ; ici, il n'y en a aucun, nous mettons donc null .
int messageType : 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 variait entre nos 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.
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
jop3 = new JOptionPane();
img = new ImageIcon("images/[Link]");
[Link](null, "Message d'erreur", "Erreur", JOptionPane.ERROR_MESSAGE, img);
}
}
Ces 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 à
faire vos tests. Vous remarquerez aussi l'emploi de l'objet ImageIcon , qui lit le fichier image à l'emplacement spécifié dans son constructeur. La figure suivante
représente le résultat obtenu.
Ce type de boîte est très utile pour signaler à l'utilisateur qu'une opération s'est terminée ou qu'une erreur est survenue. L'exemple le plus simple qui me vient en
tête est le cas d'une division par zéro : on peut utiliser une boîte de dialogue dans le bloc catch .
Voici les types de boîtes que vous pouvez a icher (ces types restent valables pour tout ce qui suit) :
JOptionPane.ERROR_MESSAGE
JOptionPane.INFORMATION_MESSAGE
JOptionPane.PLAIN_MESSAGE
JOptionPane.QUESTION_MESSAGE
JOptionPane.WARNING_MESSAGE
Je pense que vous voyez désormais l'utilité de telles boîtes de dialogue. Nous allons donc poursuivre avec les boîtes de confirmation.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les boîtes de confirmation
Comme leur nom l'indique, ces dernières permettent de valider, d'invalider ou d'annuler une décision. Nous utiliserons toujours l'objet JOptionPane , mais ce
sera cette fois avec la méthode showConfirmDialog() , une méthode qui retourne un entier correspondant à l'option que vous aurez choisie dans cette boîte :
Yes ;
No ;
Cancel.
Comme exemple, nous pouvons prendre notre animation dans sa version la plus récente. Nous pourrions utiliser une boîte de confirmation lorsque nous cliquons
sur l'un des boutons contrôlant l'animation ( Go ou Stop ).
public Fenetre(){
//Rien de changé ici
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private void go(){
//Cette méthode n'a pas changé non plus
}
if(option == JOptionPane.OK_OPTION){
animated = true;
t = new Thread(new PlayAnimation());
[Link]();
[Link](false);
[Link](true);
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
if(option != JOptionPane.NO_OPTION &&
option != JOptionPane.CANCEL_OPTION &&
option != JOptionPane.CLOSED_OPTION){
animated = false;
[Link](true);
[Link](false);
}
}
}
//…
JOptionPane jop = new JOptionPane();
int option = [Link](null, "Voulez-vous lancer l'animation ?", "Lancement de l'animation", JOptionPane.YES_NO_OPT
ION, JOptionPane.QUESTION_MESSAGE);
if(option == JOptionPane.OK_OPTION){
animated = true;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
t = new Thread(new PlayAnimation());
[Link]();
[Link](false);
[Link](true);
}
//…
//…
JOptionPane jop = new JOptionPane();
int option = [Link](null, "Voulez-vous arrêter l'animation ?", "Arrêt de l'animation", JOptionPane.YES_NO_CANCEL
_OPTION, JOptionPane.QUESTION_MESSAGE);
en revanche, plutôt que d'a icher directement la boîte, nous a ectons le résultat que renvoie la méthode showConfirmDialog() à 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 dans cette boîte, vous pouvez a ecter une valeur de type int :
correspondant à l'entier JOptionPane.CANCEL_OPTION pour la boîte apparaissant lors du clic sur « Stop », qui vaut 2 ;
correspondant à l'entier JOptionPane.CLOSED_OPTION pour la même boîte que ci-dessus et qui vaut -1.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
En e ectuant un test sur la valeur de notre entier, nous pouvons en déduire le bouton sur lequel on a cliqué et agir en conséquence ! La figure suivante représente
deux copies d'écran du résultat obtenu.
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
N_MESSAGE);
[Link](null, "Votre nom est " + nom, "Identité", JOptionPane.INFORMATION_MESSAGE);
}
}
Rien d'extraordinaire… Maintenant, voyons comment on intègre une liste dans une boîte de ce genre. Vous allez voir, c'est simplissime !
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Liste dans une boîte de dialogue
ensuite, vous devez passer un tableau de String afin de remplir la combo (l'objet JComboBox ) de la boîte ;
Cette méthode retourne un objet de type Object , comme si vous récupériez la valeur directement dans la combo ! Du coup, n'oubliez pas de 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 supplémentaire et que le type de retour n'est pas un objet mais un entier.
Ce type de boîte propose un choix de boutons correspondant aux éléments passés en paramètres (tableau de String ) au lieu d'une combo ; elle prend aussi
une valeur par défaut, mais retourne l'indice de l'élément dans la liste au lieu de l'élément lui-même.
Je pense que vous vous y connaissez assez pour comprendre le code suivant :
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
int rang = [Link](null,
"Veuillez indiquer votre sexe !",
"Gendarmerie nationale !",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
sexe,
sexe[2]);
[Link](null, "Votre sexe est " + sexe[rang], "Etat civil", JOptionPane.INFORMATION_MESSAGE);
}
}
Boîte multi-boutons
Voilà, vous en avez terminé avec les boîtes de saisie. Cependant, vous avez dû vous demander s'il n'était pas possible d'ajouter des composants à ces boîtes. C'est
vrai : vous pourriez avoir besoin de plus de renseignements, sait-on jamais… Je vous propose donc de voir comment créer vos propres boîtes de dialogue !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link]("Ma JFrame");
[Link](300, 100);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]().setLayout(new FlowLayout());
[Link]().add(bouton);
[Link](new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
ZDialog zd = new ZDialog(null, "Coucou les ZérOs", true);
}
});
[Link](true);
}
Je pense que vous avez deviné le rôle des paramètres du constructeur, mais je vais tout de même les expliciter :
boolean modal correspond à la modalité ; true : boîte modale, false : boîte non modale.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Rien de compliqué… Il est donc temps d'ajouter des composants à notre objet. Par contre, vous conviendrez que si nous prenons la peine de construire un tel
composant, nous attendons plus qu'une simple réponse à une question ouverte (oui/non), une chaîne de caractères ou encore un choix dans une liste… Nous en
voulons bien plus ! Plusieurs saisies, avec plusieurs listes en même temps !
Vous avez vu que nous devrons récupérer les informations choisies dans certains cas, mais pas dans tous : nous allons donc devoir déterminer ces di érents cas,
ainsi que les choses à faire.
Partons du fait que notre boîte comprendra un bouton OK et un bouton Annuler : dans le cas où l'utilisateur clique sur OK , on récupère les informations, si
l'utilisateur clique sur Annuler , on ne récupère rien. Et il faudra aussi tenir compte de la modalité de notre boîte : la méthode setVisible(false); met fin au
dialogue ! Ceci signifie également que le dialogue s'entame au moment où l'instruction setVisible(true); est exécutée. C'est pourquoi nous allons sortir
cette instruction du constructeur de l'objet et la mettre dans une méthode à part.
Maintenant, il faut que l'on puisse indiquer à notre boîte de renvoyer les informations ou non. C'est pour cela que nous allons utiliser un booléen - appelons-le
sendData - initialisé à false , mais qui passera à true si on clique sur OK .
Comment récupérer les informations saisies dans notre boîte depuis notre fenêtre, vu que nous voulons obtenir plusieurs informations ?
C'est vrai qu'on ne peut retourner qu'une valeur à la fois. Mais il peut y avoir plusieurs solutions à ce problème :
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 :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
faire des accesseurs dans votre ZDialog ;
créer un objet dont le rôle est de collecter les informations dans votre boîte et de retourner cet objet ;
etc.
Nous allons opter pour un objet qui collectera les informations et que nous retournerons à la fin de la méthode showZDialog() . Avant de nous lancer dans sa
création, nous devons savoir ce que nous allons mettre dans notre boîte… J'ai choisi de vous faire programmer une boîte permettant de spécifier les
caractéristiques d'un personnage de jeu vidéo :
Pour ce qui est du placement des composants, l'objet JDialog se comporte exactement comme un objet JFrame ( BorderLayout par défaut, ajout
d'un composant au conteneur…).
Nous pouvons donc créer notre objet contenant les informations de notre boîte de dialogue, je l'ai appelé ZDialogInfo .
public ZDialogInfo(){}
public ZDialogInfo(String nom, String sexe, String age, String cheveux, String taille){
[Link] = nom;
[Link] = sexe;
[Link] = age;
[Link] = cheveux;
[Link] = taille;
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public String toString(){
String str;
if([Link] != null && [Link] != null && [Link] != null && [Link] != null && [Link] != null){
str = "Description de l'objet InfoZDialog";
str += "Nom : " + [Link] + "\n";
str += "Sexe : " + [Link] + "\n";
str += "Age : " + [Link] + "\n";
str += "Cheveux : " + [Link] + "\n";
str += "Taille : " + [Link] + "\n";
}
else{
str = "Aucune information !";
}
return str;
}
}
L'avantage avec cette méthode, c'est que nous n'avons pas à nous soucier d'une éventuelle annulation de la saisie : l'objet d'information renverra toujours
quelque chose.
Voici le code source de notre boîte perso :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](new BorderLayout());
[Link](icon);
//Le nom
JPanel panNom = new JPanel();
[Link]([Link]);
[Link](new Dimension(220, 60));
nom = new JTextField();
[Link](new Dimension(100, 25));
[Link]([Link]("Nom du personnage"));
nomLabel = new JLabel("Saisir un nom :");
[Link](nomLabel);
[Link](nom);
//Le sexe
JPanel panSexe = new JPanel();
[Link]([Link]);
[Link](new Dimension(220, 60));
[Link]([Link]("Sexe du personnage"));
sexe = new JComboBox();
[Link]("Masculin");
[Link]("Féminin");
[Link]("Indéterminé");
sexeLabel = new JLabel("Sexe : ");
[Link](sexeLabel);
[Link](sexe);
//L'âge
JPanel panAge = new JPanel();
[Link]([Link]);
[Link]([Link]("Age du personnage"));
[Link](new Dimension(440, 60));
tranche1 = new JRadioButton("15 - 25 ans");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](true);
tranche2 = new JRadioButton("26 - 35 ans");
tranche3 = new JRadioButton("36 - 50 ans");
tranche4 = new JRadioButton("+ de 50 ans");
ButtonGroup bg = new ButtonGroup();
[Link](tranche1);
[Link](tranche2);
[Link](tranche3);
[Link](tranche4);
[Link](tranche1);
[Link](tranche2);
[Link](tranche3);
[Link](tranche4);
//La taille
JPanel panTaille = new JPanel();
[Link]([Link]);
[Link](new Dimension(220, 60));
[Link]([Link]("Taille du personnage"));
tailleLabel = new JLabel("Taille : ");
taille2Label = new JLabel(" cm");
taille = new JTextField("180");
[Link](new Dimension(90, 25));
[Link](tailleLabel);
[Link](taille);
[Link](taille2Label);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("Blond");
[Link]("Brun");
[Link]("Roux");
[Link]("Blanc");
cheveuxLabel = new JLabel("Cheveux");
[Link](cheveuxLabel);
[Link](cheveux);
[Link](new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
zInfo = new ZDialogInfo([Link](), (String)[Link](), getAge(), (String)[Link]() ,get
Taille());
setVisible(false);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public String getTaille(){
return ([Link]().equals("")) ? "180" : [Link]();
}
});
[Link](okBouton);
[Link](cancelBouton);
[Link]().add(panIcon, [Link]);
[Link]().add(content, [Link]);
[Link]().add(control, [Link]);
}
}
J'ai ajouté une image, mais vous n'y êtes nullement obligés ! Voici le code source permettant de tester cette boîte :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public Fenetre(){
[Link]("Ma JFrame");
[Link](300, 100);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]().setLayout(new FlowLayout());
[Link]().add(bouton);
[Link](new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
ZDialog zd = new ZDialog(null, "Coucou les ZérOs", true);
ZDialogInfo zInfo = [Link]();
JOptionPane jop = new JOptionPane();
[Link](null, [Link](), "Informations personnage", JOptionPane.INFORMATION_MESSAGE);
}
});
[Link](true);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Di érentes copies d'écran de test
Voilà : nous venons de voir comment utiliser des boîtes de dialogue. En route pour l'utilisation des menus, à présent !
Les menus
Faire son premier menu
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous vous rappelez que j'ai mentionné qu'une MenuBar fait partie de la composition de l'objet JFrame . Le moment est venu pour vous d'utiliser un composant
de ce genre. Néanmoins, celui-ci appartient au package [Link] . Dans ce chapitre nous utiliserons son homologue, l'objet JMenuBar , issu dans le package
[Link] . Pour travailler avec des menus, nous aurons besoin :
Afin de permettre des interactions avec nos futurs menus, nous allons devoir implémenter l'interface ActionListener que vous connaissez déjà bien. Ces
implémentations serviront à écouter les objets JMenuItem : ce sont ces objets qui déclencheront l'une ou l'autre opération. Les JMenu , eux, se comportent
automatiquement : 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 !
Je vous propose d'enlever tous les composants (boutons, combos, etc.) de notre animation et de gérer tout cela par le biais d'un menu.
Avant de nous lancer dans cette tâche, voici une application de tout cela, histoire de vous familiariser avec les concepts et leur syntaxe.
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private JMenuItem item4 = new JMenuItem("Arrêter");
public ZFenetre(){
[Link](400, 200);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
this.test1_2.add(jrmi1);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
this.test1_2.add(jrmi2);
L'action attachée au JMenutItem Fermer permet de quitter l'application. Ce que donne le code est a iché à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Premier menu
Vous voyez qu'il n'y a rien de di icile dans l'élaboration d'un menu. Je vous propose donc d'en créer un pour notre animation. Allons-y petit à petit : nous ne
gérerons les événements que par la suite. Pour le moment, nous allons avoir besoin :
d'un menu Animation pour lancer, interrompre (par défaut à setEnabled(false) ) ou quitter l'animation ;
d'un menu Forme afin de sélectionner le type de forme utiliser (sous-menu + une radio par forme) et de permettre d'activer le mode morphing (case à
cocher) ;
d'un menu À propos avec un joli « ? » qui va ouvrir une boîte de dialogue.
N'e acez surtout pas les implémentations pour les événements : retirez seulement les composants qui les utilisent. Ensuite, créez votre menu !
Voici un code qui ne devrait pas trop di érer de ce que vous avez écrit :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private ButtonGroup bg = new ButtonGroup();
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
[Link](pan, [Link]);
[Link](container);
[Link]();
[Link](true);
}
//Menu forme
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](carre);
[Link](triangle);
[Link](rond);
[Link](etoile);
[Link](rond);
[Link](carre);
[Link](triangle);
[Link](etoile);
[Link](true);
[Link](typeForme);
[Link](morph);
//Menu À propos
[Link](aProposItem);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
JOptionPane jop = new JOptionPane();
int option = [Link](null,
"Voulez-vous lancer l'animation ?",
"Lancement de l'animation",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if(option == JOptionPane.OK_OPTION){
[Link](false);
[Link](true);
animated = true;
t = new Thread(new PlayAnimation());
[Link]();
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Notre menu et son animation
Il ne reste plus qu'à faire communiquer nos menus et notre animation ! Pour cela, rien de plus simple, il su it d'indiquer à nos MenuItem qu'on les écoute. 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 de ItemListener ), nous utiliserons donc la première méthode.
Afin que l'application fonctionne bien, j'ai apporté deux modifications mineures dans la classe Panneau . J'ai ajouté une instruction dans une condition :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Les imports
public Fenetre(){
//Le constructeur est inchangé
}
[Link]();
[Link](new ActionListener(){
public void actionPerformed(ActionEvent event){
[Link](0);
}
});
[Link](quitter);
//Menu Forme
[Link](carre);
[Link](triangle);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](rond);
[Link](etoile);
[Link](rond);
[Link](carre);
[Link](triangle);
[Link](etoile);
[Link](true);
[Link](typeForme);
//Menu À propos
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](null, mess, "À propos", JOptionPane.INFORMATION_MESSAGE, img);
}
});
[Link](aProposItem);
/**
* Écouteur du menu Quitter
* @author CHerby
*/
class StopAnimationListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//Idem
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
class PlayAnimation implements Runnable{
public void run() {
go();
}
}
/**
* Écoute les menus Forme
* @author CHerby
*/
class FormeListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
[Link](((JRadioButtonMenuItem)[Link]()).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([Link]()) [Link](true);
//Sinon rien !
else [Link](false);
}
}
}
Comme je l'ai indiqué dans le dialogue du menu À propos , je crois qu'il est temps d'ajouter des raccourcis clavier à notre application ! Vous êtes prêts ?
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
À nouveau, il est très simple d'insérer des raccourcis clavier. Pour ajouter un « accélérateur » (raccourcis clavier des éléments de menu) sur un JMenu , nous
appellerons la méthode setAccelerator(); et pour ajouter un mnémonique (raccourcis permettant de simuler le clic sur un point de menu) sur un
JMenuItem , nous nous servirons de la méthode setMnemonic(); .
Attribuons le mnémonique « A » au 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 su it d'invoquer la méthode setMnemonic(char mnemonic); sur le JMenu que vous désirez.
[Link]('F');
[Link](forme);
[Link]('P');
[Link](aPropos);
//Ajout de la barre de menus sur la fenêtre
[Link](menuBar);
}
Nous avons à présent les lettres correspondant au mnémonique soulignées dans nos menus. Et il y a mieux : si vous tapez ALT + <la lettre> , le menu
correspondant se déroule ! La figure suivante correspond à ce que j'obtiens.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Sachez que vous pouvez aussi mettre des mnémoniques sur les objets JMenuItem . Je dois également vous dire qu'il existe une autre façon d'ajouter un
mnémonique sur un JMenu (mais c'est uniquement valable avec un JMenu ) : en passant le mnémonique en deuxième paramètre du constructeur de l'objet,
comme ceci :
Oui, je sais, c'est simple, très simple, même. Pour ajouter des accélérateurs, c'est quasiment pareil, si ce n'est que nous devrons utiliser un nouvel objet :
KeyStroke . Cet objet permet de déterminer la touche utilisée ou à utiliser. 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 à notre JMenuItem Lancer en utilisant la
méthode getKeyStroke(char caracter); de l'objet KeyStroke .
Ajoutez cette ligne de code au début de la méthode initMenu() (vous aurez besoin des packages [Link] et
[Link] ) :
Testez votre application, un petit « c » est apparu à côté du menu Lancer . La figure suivante illustre le phénomène.
Appuyez sur la touche c de votre clavier : celle-ci a le même e et qu'un clic sur le menu « Lancer » !
Si vous mettez le caractère « C », vous serez obligés d'appuyer simultanément sur SHIFT + c ou d'activer la touche MAJ !
Si le principe est bon, dites-vous aussi que maintenant, presser la touche c lancera systématiquement 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 .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Pour cela, nous allons utiliser une méthode getKeyStroke() un peu di érente : elle ne prendra pas le caractère de notre touche en argument, mais son code
ainsi qu'une ou plusieurs touche(s) composant la combinaison. Pour obtenir le code d'une touche, nous utiliserons l'objet KeyEvent , un objet qui stocke tous
les codes des 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 + SHIFT + A pour le menu Arrêter :
[Link]([Link](KeyEvent.VK_L, KeyEvent.CTRL_MASK));
[Link](lancer);
//Ajout du listener pour arrêter l'animation
[Link](new StopAnimationListener());
[Link](false);
[Link]([Link](KeyEvent.VK_A, KeyEvent.CTRL_DOWN_MASK + KeyEvent.SHIFT_DOWN_MASK));
[Link](arreter);
J'imagine que vous êtes perturbés par 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é d'entre eux sont sous la forme VK_ < le caractère ou le nom de la touche >. Lisez-le ainsi : Value of Key
< nom de la touche >.
À part certaines touches de contrôle comme CTRL , ALT , SHIFT , etc. vous pouvez facilement retrouver le code d'une touche grâce à cet objet !
Ensuite, vous avez dû remarquer qu'en tapant KeyEvent.CTRL_DOWN_MASK , Eclipse vous a proposé quasiment la même chose (figure suivante).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Versions di érentes
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 ! Vous voilà donc
avec un menu comprenant des mnémoniques et des accélérateurs. Il est maintenant temps de voir comment créer un menu contextuel !
Le menu contextuel ne doit s'a icher que dans la zone où l'animation s'exécute, pas dans le menu !
Il ne doit s'a icher que lorsqu'on fait un clic droit, et rien d'autre !
Nous allons mettre dans notre menu contextuel les actions « Lancer l'animation », « Arrêter l'animation » ainsi que deux nouveautés :
Avant d'implémenter les deux nouvelles fonctionnalités, nous allons travailler sur les deux premières.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Lorsque nous lancerons l'animation, nous devrons mettre les deux menus Lancer l'animation dans l'état setEnabled(false); et les deux menus
Arrêter l'animation dans l'état setEnabled(true); (et pour l'arrêter, il faudra faire l'inverse).
Comme je vous l'ai dit plus haut, nous allons utiliser le même objet qui écoute pour les deux menus. Il nous faudra créer une véritable instance de ces objets et
signaler à l'application que ces objets écoutent non seulement le menu du haut, mais aussi le menu contextuel.
Nous avons parfaitement le droit de le faire : plusieurs objets peuvent écouter un même composant et plusieurs composants peuvent avoir le même objet qui les
écoute ! Vous êtes presque prêts à créer votre menu contextuel, il ne vous manque que ces informations :
comment lui spécifier qu'il doit le faire uniquement suite à un clic droit.
Le déclenchement de l'a ichage du pop-up doit se faire lors d'un clic de souris. Vous connaissez une interface qui gère ce type d'événement : l'interface
MouseListener . Nous allons donc indiquer à notre panneau qu'un objet du type de cette interface va l'écouter !
Tout comme dans le chapitre sur les zones de saisie, il existe une classe qui contient toutes les méthodes de ladite interface : la classe MouseAdapter .
Vous pouvez implémenter celle-ci afin de ne redéfinir que la méthode dont vous avez besoin ! C'est cette solution que nous allons utiliser.
Si vous préférez, vous pouvez utiliser l'événement mouseClicked , mais je pensais plutôt à mouseReleased() , pour une raison simple à laquelle vous n'avez
peut-être pas pensé : si ces deux événements sont quasiment identiques, dans un certain cas, seul l'événement mouseClicked() sera appelé. Il s'agit du cas où
vous cliquez sur une zone, déplacez votre souris en dehors de la zone tout en maintenant le clic et relâchez le bouton de la souris. C'est pour cette raison que je
préfère utiliser la méthode mouseReleased() . Ensuite, pour préciser où a icher le menu contextuel, nous allons utiliser la méthode
show(Component invoker, int x, int y); de la classe JPopupMenu :
Component invoker : désigne l'objet invoquant le menu contextuel, dans notre cas, l'instance de Panneau .
Souvenez-vous que vous pouvez déterminer les coordonnées de la souris grâce à l'objet passé en paramètre de la méthode
mouseReleased(MouseEvent event) .
Je suis sûr que vous savez comment vous y prendre pour indiquer au menu contextuel de s'a icher et qu'il ne vous manque plus qu'à détecter le clic droit. C'est là
que l'objet MouseEvent va vous sauver la mise ! En e et, il possède une méthode isPopupTrigger() qui renvoie vrai s'il s'agit d'un clic droit. Vous avez
toutes les cartes en main pour élaborer votre menu contextuel (rappelez-vous que nous ne gérons pas encore les nouvelles fonctionnalités).
Je vous laisse quelques instants de réflexion…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous avez fini ? Nous pouvons comparer nos codes ? Je vous invite à consulter le code ci-dessous (il ne vous montre que les nouveautés).
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On initialise le menu stop
[Link](false);
//On affecte les écouteurs
[Link](stopAnimation);
[Link](startAnimation);
[Link](rouge);
[Link](bleu);
[Link](vert);
[Link](launch);
[Link](stop);
[Link](couleur);
[Link](background);
//La méthode qui va afficher le menu
[Link](pan, [Link](), [Link]());
}
}
});
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](pan, [Link]);
[Link](container);
[Link]();
[Link](true);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
int option = [Link](null,
"Voulez-vous lancer l'animation ?",
"Lancement de l'animation",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if(option == JOptionPane.OK_OPTION){
[Link](false);
[Link](true);
animated = true;
t = new Thread(new PlayAnimation());
[Link]();
}
}
}
/**
* Écouteur du menu Quitter
* @author CHerby
*/
class StopAnimationListener implements ActionListener{
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
JOptionPane.QUESTION_MESSAGE);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Menu contextuel
Rien de di icile ici, voici donc les codes sources de nos deux classes.
[Link]
import [Link];
//Les autres imports
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public void paintComponent(Graphics g){
//Affectation de la couleur de fond
[Link](couleurFond);
[Link](0, 0, [Link](), [Link]());
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Nos imports habituels
public Fenetre(){
[Link]("Animation");
[Link](300, 300);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]([Link]);
[Link](new BorderLayout());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](bgColor);
[Link](bgColor);
[Link](bgColor);
[Link](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
[Link](new MouseAdapter(){
public void mouseReleased(MouseEvent event){
//Seulement s'il s'agit d'un clic droit
if([Link]()){
[Link](blancBack);
[Link](rougeBack);
[Link](bleuBack);
[Link](vertBack);
[Link](blanc);
[Link](rouge);
[Link](bleu);
[Link](vert);
[Link](launch);
[Link](stop);
[Link](couleur);
[Link](background);
[Link](pan, [Link]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](container);
[Link]();
[Link](true);
}
if([Link]() == vertBack)
[Link]([Link]);
else if ([Link]() == bleuBack)
[Link]([Link]);
else if([Link]() == rougeBack)
[Link]([Link]);
else
[Link]([Link]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Changement de couleur via le menu contextuel
Vous conviendrez que les menus et les menus contextuels peuvent s'avérer vraiment utiles et ergonomiques ! En plus, ils sont relativement simples à
implémenter (et à utiliser). Cependant, vous avez sans doute remarqué qu'il y a beaucoup de clics superflus, que ce soit pour utiliser un menu ou menu
contextuel : il faut au moins un clic pour a icher leur contenu (sauf dans le cas de l'accélérateur).
Pour contrer ce genre de chose, il existe un concept très puissant : la barre d'outils !
Pour faire simple, la barre d'outils sert à e ectuer des actions disponibles dans le menu, mais sans devoir fouiller dans celui-ci ou mémoriser le raccourci clavier
(accélérateur) qui y est lié. Elle permet donc des actions rapides.
Elle est généralement composée d'une multitude de boutons, une image apposée sur chacun d'entre eux symbolisant l'opération qu'il peut e ectuer.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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, à
une di érence près : celui-ci prend des boutons ( JButton ) en arguments, et il n'y a pas d'endroit spécifique où incorporer votre barre d'outils (il faudra
l'expliciter lors de sa création).
Tout d'abord, il nous faut des images à mettre sur nos boutons… J'en ai fait de toutes simples (figure suivante), mais libre à vous d'en choisir d'autres.
Au niveau des 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 on l'a
fait pour les deux actions du menu contextuel. Concernant les boutons pour les formes, c'est un peu plus délicat. Les autres composants qui éditaient la forme de
notre animation étaient des boutons radios. Or, ici, nous avons des boutons standard. Outre le fait qu'il va falloir une instance précise de la classe
FormeListener , nous aurons à modifier un peu son comportement…
Il nous faut savoir si l'action vient d'un bouton radio du menu ou d'un bouton de la barre d'outils : c'est l'objet ActionEvent qui nous permettra d'accéder à
cette information. Nous n'allons pas tester tous les boutons radio un par un, pour ces composants, le système utilisé jusque-là était très bien. Non, nous allons
simplement vérifier si celui qui a déclenché l'action est un JRadioButtonMenuItem , et si c'est le cas, nous testerons les boutons.
Rappelez-vous le chapitre sur la réflexivité ! La méthode getSource() nous retourne un objet, il est donc possible de connaître la classe de celui-ci avec la
méthode getClass() et par conséquent d'en obtenir le nom grâce à la méthode getName() .
Il va falloir qu'on pense à mettre à jour le bouton radio sélectionné dans le menu. Et là, pour votre plus grand bonheur, je connais une astuce qui marche pas mal
du tout : lors du clic sur un bouton de la barre d'outils, il su it de déclencher l'événement sur le bouton radio correspondant ! 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 ! Ainsi,
plutôt que de gérer la même façon de faire à deux endroits, nous allons rediriger l'action e ectuée sur un composant vers un autre.
Vous avez toutes les cartes en main pour réaliser votre barre d'outils. N'oubliez pas que vous devez spécifier sa position sur le conteneur principal ! Bon. Faites des
tests, comparez, codez, e acez… au final, vous devriez avoir quelque chose comme ceci :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
//Nos imports habituels
public Fenetre(){
//La seule nouveauté est la méthode ci-dessous
[Link]();
[Link](true);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](play);
[Link](cancel);
[Link]();
[Link](fListener);
[Link](fondBouton);
[Link](square);
[Link](fondBouton);
[Link](fListener);
[Link](tri);
[Link](fondBouton);
[Link](fListener);
[Link](star);
[Link](toolBar, [Link]);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public void actionPerformed(ActionEvent arg0) {
//Toujours la même boîte de dialogue…
if(option == JOptionPane.OK_OPTION){
[Link](false);
[Link](true);
[Link](false);
[Link](true);
animated = true;
t = new Thread(new PlayAnimation());
[Link]();
}
}
}
/**
* Écouteur du menu Quitter
* @author CHerby
*/
class StopAnimationListener implements ActionListener{
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On remplace nos boutons par nos MenuItem
[Link](true);
[Link](false);
[Link](true);
[Link](false);
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
}
}
Elle n'est pas jolie, votre IHM, maintenant ? Vous avez bien travaillé, surtout qu'à présent, je vous explique peut-être les grandes lignes, mais je vous force à aussi
réfléchir par vous-mêmes ! Eh oui, vous avez appris à penser en orienté objet et connaissez les points principaux de la programmation événementielle.
Maintenant, il vous reste simplement à acquérir des détails techniques spécifiques (par exemple, la manière d'utiliser certains objets).
Pour ceux qui l'auraient remarqué, la barre d'outils est déplaçable ! Si vous cliquez sur la zone mise en évidence à la figure suivante, vous pourrez la repositionner.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Zone de déplacement
Il su it de maintenir le clic et de faire glisser votre souris vers la droite, la gauche ou encore le bas. Vous verrez alors un carré se déplacer et, lorsque vous
relâcherez le bouton, votre barre aura changé de place, comme le montre la figure suivante.
Elles sont fortes ces barres d'outils, tout de même ! En plus de tout ça, vous pouvez utiliser autre chose qu'un composant sur une barre d'outils...
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
une action pour la couleur du fond en rouge ;
etc.
Cela peut être très lourd à faire, mais je laisse votre bon sens déterminer s'il est pertinent d'utiliser cette méthode ou non !
//…
//…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
}
Vous pouvez voir que cela peut être très pratique. Désormais, si vous ajoutez une action sur une barre d'outils, celle-ci crée automatiquement un bouton
correspondant ! Utiliser les actions abstraites plutôt que des implémentations de telle ou telle interface est un choix qui vous revient. Nous pouvons d'ailleurs très
bien appliquer ce principe au code de notre animation, mais vous constaterez qu'il s'alourdira, nous éviterons donc de le faire… Mais comme je vous le disais,
c'est une question de choix et de conception.
Les boîtes de dialogue s'utilisent, à l'exception des boîtes personnalisées, avec l'objet JOptionPane .
La méthode showConfirmDialog() permet d'a icher 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() a iche une boîte attendant une saisie clavier 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() a iche une boîte attendant que l'utilisateur e ectue un clic sur une option.
Celle-ci retourne l'indice de l'élément sur lequel vous avez cliqué ou un indice négatif dans 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 setVisible(true) est invoquée et se termine lorsque la méthode setVisible(false)
est appelée.
L'objet servant à insérer une barre de menus 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.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
L'ajout d'accélérateurs permet de déclencher des actions, le plus souvent par des combinaisons de touches.
Afin de récupérer les codes des touches du clavier, vous devrez utiliser un objet KeyStroke ainsi qu'un objet KeyEvent .
Un menu contextuel fonctionne comme un menu normal, à la di érence qu'il s'agit d'un objet JPopupMenu . Vous devez toutefois spécifier le composant sur
lequel doit s'a icher le menu contextuel.
TP : l'ardoise magique
Nous voilà partis pour un nouveau TP. Les objectifs de celui-ci sont :
etc.
un carré ;
un rond.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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 :
Points de menu
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
L'auteur s'exprime
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 et de l'agrandissement… Vous allez devoir gérer ce cas de figure, sans quoi votre zone de dessin
s'e acera à 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) dans votre classe dérivée de JPanel ! J'en ai presque trop dit…
Prérequis
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Afin de faire les tracés, il va falloir détecter le mouvement de la souris. Je ne vous en ai pas encore parlé auparavant, mais vous avez l'habitude d'utiliser des
interfaces de gestion d'événements, maintenant…
Afin de détecter les mouvements de la souris, vous allez devoir utiliser l'interface MouseMotionListener ; celle-ci contient deux méthodes :
mouseDragged(MouseEvent e) , qui fonctionne comme mouseMoved , sauf que vous devrez avoir cliqué sur le composant et maintenir ce clic enfoncé
pendant le mouvement (exactement ce dont vous avez besoin).
Voilà : vous allez devoir créer une implémentation de cette interface pour réussir à dessiner sur votre conteneur !
Ne vous précipitez pas, réfléchissez bien à ce dont vous avez besoin, comment utiliser vos implémentations, etc. Un code bien réfléchi est un code rapidement
opérationnel !
Correction
Je vous propose une des corrections possibles.
Vous constaterez que c'est un code assez simple. Cet exercice n'a rien de di icile et a surtout le mérite de vous faire travailler un peu tout ce que vous avez vu
jusqu'ici…
[Link]
//Couleur du point
private Color color = [Link];
//Taille
private int size = 10;
//Position sur l'axe X : initialisé au dehors du conteneur
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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";
//ACCESSEURS
public Color getColor() {
return color;
}
public void setColor(Color color) {
[Link] = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
[Link] = size;
}
public int getX() {
return x;
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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) {
[Link] = type;
}
}
[Link]
//Couleur du pointeur
private Color pointerColor = [Link];
//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
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private int pointerSize = 15;
//Collection de points !
private ArrayList<Point> points = new ArrayList<Point>();
public DrawPanel(){
[Link](new MouseAdapter(){
public void mousePressed(MouseEvent e){
[Link](new Point([Link]() - (pointerSize / 2), [Link]() - (pointerSize / 2), pointerSize, pointerColor, pointerTyp
e));
repaint();
}
});
[Link](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é
[Link](new Point([Link]() - (pointerSize / 2), [Link]() - (pointerSize / 2), pointerSize, pointerColor, pointerTyp
e));
repaint();
}
[Link]([Link]);
[Link](0, 0, [Link](), [Link]());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Si on doit effacer, on ne passe pas dans le else => pas de dessin
if([Link]){
[Link] = false;
}
else{
//On parcourt notre collection de points
for(Point p : [Link])
{
//On récupère la couleur
[Link]([Link]());
//Efface le contenu
public void erase(){
[Link] = true;
[Link] = new ArrayList<Point>();
repaint();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Définit la forme du pointeur
public void setPointerType(String str){
[Link] = str;
}
}
[Link]
//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");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
green = new JButton(new ImageIcon("images/[Link]")),
blue = new JButton(new ImageIcon("images/[Link]"));
//LES ÉCOUTEURS
private FormeListener fListener = new FormeListener();
private CouleurListener cListener = new CouleurListener();
public Fenetre(){
[Link](700, 500);
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
//Initialise le menu
private void initMenu(){
[Link](new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
[Link]();
}
});
[Link]([Link](KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK));
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
[Link](0);
}
});
[Link]([Link](KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK));
[Link](nouveau);
[Link]();
[Link](quitter);
[Link]('F');
[Link](fListener);
[Link](fListener);
[Link](rond);
[Link](carre);
[Link](cListener);
[Link](cListener);
[Link](cListener);
[Link](rouge);
[Link](vert);
[Link](bleu);
[Link]('E');
[Link](forme);
[Link]();
[Link](couleur);
[Link](fichier);
[Link](edition);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](menuBar);
}
[Link](square);
[Link](circle);
[Link]();
[Link](red);
[Link](blue);
[Link](green);
[Link]().add(toolBar, [Link]);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
else [Link]("CIRCLE");
}
}
}
Améliorations possibles
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Voici ce que vous pouvez faire afin de rendre cette application plus attractive :
utiliser les énumérations (ou encore le pattern strategy) pour gérer les di érentes fonctionnalités ;
etc.
Il y a plusieurs objets qui peuvent vous aider à mieux gérer le contenu de vos IHM ; ceux qui seront abordés ici vont, je pense, vous rendre un sacré service…
Toutefois, laissez-moi vous mettre en garde : ici, nous n'aborderons pas les objets dans le détail, nous ne ferons même qu'en survoler certains. Le fait est
que vous êtes dorénavant à même d'approfondir tel ou tel sujet en Java.
Autres conteneurs
L'objet JSplitPane
Avant de vous faire un laïus (un petit, je vous rassure), regardez la figure suivante. Elle représente des fenêtres avec un JSplitPane .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Exemple de JSplitPane avec déplacement du splitter
Cette figure représente l'intérieur d'un objet JFrame . La barre au milieu est un objet déplaçable qui permet d'agrandir une zone tout en rétrécissant celle d'à
côté. Ici, dans la première image, la barre est vers la gauche. La deuxième image est prise pendant que je déplace la barre centrale et enfin, la troisième
correspond au résultat lorsque j'ai relâché le bouton de ma souris ! Vous pouvez constater que le conteneur de gauche est devenu plus grand, au détriment de
celui de droite…
Je vous rassure tout de suite, ce composant est très simple d'utilisation. En fait, les composants abordés dans ce chapitre n'ont rien de compliqué. Je ne vais pas
vous faire mariner plus longtemps : l'objet utilisé ici est un JSplitPane . Voici le code source que j'ai utilisé pour avoir le résultat ci-dessus :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link](null);
[Link]("Gérer vos conteneur");
[Link](JFrame.EXIT_ON_CLOSE);
[Link](200, 200);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On crée deux conteneurs de couleurs différentes
JPanel pan = new JPanel();
[Link]([Link]);
Vous avez sans doute repéré l'attribut JSplitPane.HORIZONTAL_SPLIT dans le constructeur de l'objet : il sert à spécifier le type de séparation utilisé. Eh oui, il
en existe d'autres ! Vous pouvez obtenir une séparation verticale en utilisant l'attribut JSplitPane.VERTICAL_SPLIT , comme à la figure suivante.
Split vertical
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Autre point, les deux autres paramètres ne sont pas nécessairement des JPanel. Ici, j'ai utilisé des JPanel, mais vous pouvez en fait utiliser n'importe quelle classe
dérivant de JComponent (conteneur, bouton, case à cocher…) : elle n'est pas belle, la vie ?
Je ne vous avais donc pas menti : cet objet est vraiment très simple d'utilisation, mais je ne vais pas vous laisser tout de suite. Vous ne l'avez peut-être pas
remarqué mais ces objets ne peuvent pas faire disparaître entièrement les côtés. Dans notre cas, la fenêtre est petite, mais vous aurez peut-être l'occasion d'avoir
une grande IHM et d'agrandir ou de rétrécir fréquemment vos contenus.
L'objet JSplitPane dispose d'une méthode qui permet de rendre la barre de séparation « intelligente »… enfin presque. Ladite méthode ajoute deux petits
boutons sur votre barre et, lorsque vous cliquerez dessus, fera rétrécir le côté vers lequel pointe la flèche dans le bouton. L'illustration de mes propos se trouve à
la figure suivante.
Flèches de positionnement
Pour avoir ces deux boutons en plus sur votre barre, il vous su it d'invoquer la méthode [Link](true); (mon objet s'appelle
toujours split ) et le tour est joué ! Amusez-vous à cliquer sur ces boutons et vous verrez à quoi ils servent.
Avant de vous laisser fouiner un peu à propos de cet objet, vous devez savoir que vous pouvez définir une taille de séparateur grâce à la méthode
[Link](int size) ; la figure suivante vous montre ce que j'ai obtenu avec une taille de 35 pixels.
Agrandissement du splitter
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous pouvez également définir où doit s'a icher la barre de séparation. Ceci se fait grâce à la méthode setDividerLocation(int location); ou
setDividerLocation(double location); .
Avant de vous montrer un exemple de code utilisant cette méthode, vous avez dû comprendre que, vu que cet objet peut accepter en paramètres des sous-classes
de JComponent , il pouvait aussi accepter des JSplitPane ! La figure suivante vous montre ce que j'ai pu obtenir.
Multiple splitter
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link](null);
[Link]("Gérer vos conteneur");
[Link](JFrame.EXIT_ON_CLOSE);
[Link](200, 200);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On crée deux conteneurs de couleurs différentes
JPanel pan = new JPanel();
[Link]([Link]);
JPanel pan2 = new JPanel();
[Link]([Link]);
JPanel pan3 = new JPanel();
[Link]([Link]);
JPanel pan4 = new JPanel();
[Link]([Link]);
//On construit enfin notre séparateur
split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, pan, pan4);
//On place le premier séparateur
[Link](80);
split2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, pan3, pan2);
//On place le deuxième séparateur
[Link](100);
//On passe les deux précédents JSplitPane à celui-ci
split3 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, split, split2);
//On place le troisième séparateur
[Link](80);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Je pense que vous en savez assez pour utiliser cet objet comme il convient. Nous allons à présent voir un autre objet bien pratique. Il permet d'ajouter un scroll
(barre de défilement) à côté de vos conteneurs afin de pouvoir dépasser les limites de ceux-ci.
L'objet JScrollPane
Afin que vous puissiez mieux juger l'utilité de l'objet que nous allons utiliser ici, nous allons voir un nouvel objet de texte : le JTextArea . Cet objet est très simple
: c'est une forme de JTextField , mais en plus grand ! Nous pouvons directement écrire dans ce composant, celui-ci ne retourne pas directement à la ligne si
vous atteignez le bord droit de la fenêtre.
Pour vérifier si les lettres tapées au clavier sont bien dans notre objet, vous pouvez récupérer le texte saisi grâce à la méthode getText() . Voici un code
d'exemple :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link](null);
[Link]("Gérer vos conteneur");
[Link](JFrame.EXIT_ON_CLOSE);
[Link](200, 200);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
});
//On ajoute l'objet au content pane de notre fenêtre
[Link]().add(textPane, [Link]);
[Link]().add(bouton, [Link]);
[Link](true);
}
Le code est simple et clair, je vous laisse le tester chez vous ! Cependant, les plus curieux d'entre vous l'auront remarqué : si vous écrivez trop de lignes, vous
dépassez la limite imposée par le bas de votre fenêtre… Le texte est bien écrit mais vous ne le voyez pas… Exactement comme pour le bord droit. Pour ce genre
de problème, il existe ce qu'on appelle des « scrolls ». Ce sont de petit ascenseurs positionnés sur le côté et / ou sur le bas de votre fenêtre et qui vous permettent
de dépasser les limites imposées par ladite fenêtre, comme le montre la figure suivante !
Exemple de JScrollPane
Vous voyez le petit ascenseur à droite et en bas de la fenêtre ? Avec ça, finis les problèmes de taille de vos conteneurs ! Voici le code que j'ai utilisé pour obtenir ce
résultat :
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public Fenetre(){
[Link](null);
[Link]("Gérer vos conteneur");
[Link](JFrame.EXIT_ON_CLOSE);
[Link](200, 200);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
L'objet utilisé afin d'avoir un ascenseur s'appelle donc un JScrollPane . Désormais, vous pourrez écrire aussi loin que vous le voulez, vers le bas et vers la droite
! Les ascenseurs apparaissent automatiquement lorsque vous dépassez les limites autorisées. De plus, vous pouvez redéfinir leurs comportements grâce aux
méthodes :
[Link](int policy) , qui permet de définir le comportement du scroll en bas de votre fenêtre ;
[Link](int policy) , qui permet de définir le comportement du scroll à droite de votre fenêtre.
Le paramètre de ces méthodes est un entier défini dans la classe JScrollPane , il peut prendre les valeurs suivantes :
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED : le scroll vertical n'est visible que s'il est nécessaire, donc s'il y a dépassement de la taille en hauteur ;
JScrollPane.VERTICAL_SCROLLBAR_NEVER : le scroll vertical n'est jamais visible, même si vous dépassez ; en revanche, le conteneur s'allonge tout de
même ;
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS : le scroll vertical est toujours visible, même si vous ne dépassez pas.
Les mêmes entiers existent pour le scroll horizontal, mais vous devrez alors remplacer VERTICAL par HORIZONTAL ! Vous devez tout de même savoir que cet
objet en utilise un autre : un JScrollBar . Les deux barres de défilement sont deux instances de cet objet…
Nous avons vu comment séparer un conteneur, comment agrandir un conteneur, nous allons maintenant voir comment ajouter dynamiquement des conteneurs !
L'objet JTabbedPane
Dans ce chapitre, vous allez apprendre à créer plusieurs « pages » dans votre IHM… Jusqu'à maintenant, vous ne pouviez pas avoir plusieurs contenus dans votre
fenêtre, à moins de leur faire partager l'espace disponible.
Il existe une solution toute simple qui consiste à créer des onglets et, croyez-moi, c'est aussi très simple à faire. L'objet à utiliser est un JTabbedPane . Afin d'avoir
un exemple plus ludique, j'ai constitué une classe héritée de JPanel afin de créer des onglets ayant une couleur de fond di érente… Cette classe ne devrait plus
vous poser de problèmes :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
import [Link];
import [Link];
import [Link];
public Panneau(){}
public Panneau(Color color){
[Link] = color;
[Link] = "Contenu du panneau N°" + (++COUNT);
}
public void paintComponent(Graphics g){
[Link]([Link]);
[Link](0, 0, [Link](), [Link]());
[Link]([Link]);
[Link](new Font("Arial", [Link], 15));
[Link]([Link], 10, 20);
}
}
J'ai utilisé cet objet afin de créer un tableau de Panneau . Chaque instance est ensuite ajoutée à mon objet gérant les onglets via sa méthode
add(String title, JComponent comp) .
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private JTabbedPane onglet;
public Fenetre(){
[Link](null);
[Link]("Gérer vos conteneurs");
[Link](JFrame.EXIT_ON_CLOSE);
[Link](400, 200);
}
//On passe ensuite les onglets au content pane
[Link]().add(onglet);
[Link](true);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Plusieurs onglets
Vous constatez que l'utilisation de cet objet est très simple, là aussi… Je vais tout de même vous présenter quelques méthodes bien utiles. Par exemple, vous
pouvez ajouter une image en guise d'icône à côté du titre de l'onglet. Ce qui pourrait nous donner la figure suivante.
Vous avez également la possibilité de changer l'emplacement des en-têtes d'onglets en spécifiant cet emplacement dans le constructeur, comme ceci :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Affiche les onglets en bas de la fenêtre
JTabbedPane onglet = new JTabbedPane([Link]);
Vous pouvez aussi utiliser la méthode setTabPlacement([Link]); qui a le même e et : ici, la barre d'exploration des onglets sera située en bas
du conteneur. Vous avez aussi la possibilité d'ajouter ou de retirer des onglets. Pour ajouter, vous avez deviné comment procéder ! Pour retirer un onglet, nous
allons utiliser la méthode remove(int index) . Cette méthode parle d'elle-même, elle va retirer l'onglet ayant pour index le paramètre passé.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//CTRL + SHIFT + O pour générer les imports nécessaires
public Fenetre(){
[Link](null);
[Link]("Gérer vos conteneurs");
[Link](JFrame.EXIT_ON_CLOSE);
[Link](400, 200);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Ajout du bouton pour retirer l'onglet sélectionné
JButton delete = new JButton("Effacer l'onglet");
[Link](new ActionListener(){
public void actionPerformed(ActionEvent e){
//On récupère l'index de l'onglet sélectionné
int selected = [Link]();
//S'il n'y a plus d'onglet, la méthode ci-dessus retourne -1
if(selected > -1)[Link](selected);
}
});
[Link]().add(pan, [Link]);
[Link](true);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Beaucoup, beaucoup d'onglets…
Ces deux objets sont très souvent associés et permettent de réaliser des applications multifenêtres, comme à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private JDesktopPane desktop = new JDesktopPane();
private static int xy = 10;
public Bureau(){
[Link](400, 300);
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
JButton ajouter = new JButton("Ajouter une fenêtre interne");
[Link](new ActionListener(){
public void actionPerformed(ActionEvent event){
++nbreFenetre;
xy += 2;
[Link](new MiniFenetre(nbreFenetre), nbreFenetre);
}
});
[Link]().add(desktop, [Link]);
[Link]().add(ajouter, [Link]);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](true);
}
}
L'objet JWindow
Pour faire simple, c'est une JFrame , mais sans les contours permettant de réduire, fermer ou agrandir la fenêtre ! Il est souvent utilisé pour faire des splash
screens (ce qui s'a iche au lancement d'Eclipse, par exemple…).
public Window(){
setSize(220, 165);
setLocationRelativeTo(null);
JPanel pan = new JPanel();
JLabel img = new JLabel(new ImageIcon("planè[Link]"));
[Link]([Link]);
[Link]([Link]);
[Link]([Link]([Link]));
[Link](img);
getContentPane().add(pan);
}
}
Le JEditorPane
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Voici un objet sympathique mais quelque peu limité par la façon dont il gère son contenu HTML (voir figure suivante) ! Il permet de réaliser des textes riches (avec
une mise en page). Il y a aussi le JTextPane qui vous permet très facilement de faire un mini-éditeur de texte (enfin, tout est relatif…).
public Fenetre(){
[Link](600, 400);
[Link]("Conteneur éditable");
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
editorPane = new JEditorPane();
[Link](" <HTML><HEAD></HEAD><BODY></BODY></HTML> ");
[Link]().add(onglet, [Link]);
[Link](true);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
Dans cet exemple, on édite le code HTML dans l'onglet d'édition et, au changement d'onglet, on génère un fichier temporaire avec l'extension .html . Ce fichier
est stocké dans un répertoire nommé « tmp » à la racine de notre projet.
Le JSlider
Ce composant vous permet d'utiliser un système de mesure pour une application : redimensionner une image, choisir le tempo d'un morceau de musique,
l'opacité d'une couleur, etc. La figure suivante montre à quoi il ressemble.
Un JSlider
Le code source :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
JSlider slide = new JSlider();
[Link](100);
[Link](0);
[Link](30);
[Link](true);
[Link](true);
[Link](10);
[Link](20);
[Link](new ChangeListener(){
public void stateChanged(ChangeEvent event){
[Link]("Valeur actuelle : " + ((JSlider)[Link]()).getValue());
}
});
[Link]().add(slide, [Link]);
[Link]().add(label, [Link]);
}
public static void main(String[] args){
Slide slide = new Slide();
[Link](true);
}
}
La JProgressBar
Elle vous permet de réaliser une barre de progression pour des traitements longs. La figure suivante en est un exemple.
Une JProgressBar
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//CTRL + SHIFT + O pour générer les imports nécessaires
public class Progress extends JFrame{
private Thread t;
private JProgressBar bar;
private JButton launch ;
public Progress(){
[Link](300, 80);
[Link]("*** JProgressBar ***");
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link]().add(bar, [Link]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public void run(){
[Link](false);
La modification des valeurs de cet objet doit se faire dans un thread, sinon vous aurez une barre vide, un temps d'attente puis la barre remplie, mais sans que les
valeurs aient défilé en temps réel !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//CTRL + SHIFT + O pour générer les imports nécessaires
public class BorderDemo extends JFrame{
public BorderDemo(){
[Link]("Les bordures font la fête !");
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
[Link](550, 200);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
for(int i = 0; i < [Link]; i++){
JLabel lib = new JLabel(list[i]);
[Link](new Dimension(150, 50));
[Link](listBorder[i]);
[Link]([Link]);
[Link]([Link]);
[Link](lib);
}
[Link]().add(pan);
}
Exemples de bordures
L'objet JSplitPane vous permet de scinder un conteneur en deux parties via un splitter déplaçable.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
L'objet JScrollPane vous permet d'avoir un conteneur ou un objet contenant du texte de s'étirer selon son contenu, en hauteur comme en largeur.
L'objet JTabbedPane vous permet d'obtenir une interface composée d'autant d'onglets que vous le désirez et gérable de façon dynamique.
Les onglets peuvent être disposés aux quatre coins d'une fenêtre.
Les objets JDesktopPane combinés à des objets JInternalFrame vous permettent de créer une application multifenêtre.
L'objet JWindow est une JFrame sans les contrôles d'usage. Elle sert à a icher une image de lancement de programme, comme Eclipse par exemple.
L'objet JEditorPane vous permet de créer un éditeur HTML et d'a icher le rendu du code écrit.
Vous pouvez gérer des mesures ou des taux via l'objet JSlider . En déplaçant le curseur, vous pourrez faire croître une valeur afin de l'utiliser.
Vous pouvez enjoliver la plupart de vos composants avec des bordures en utilisant l'objet BorderFactory qui vous permettra de créer di érents types de
traits.
Vous devez tous déjà avoir vu un arbre. Non pas celui du monde végétal, mais celui qui permet d'explorer des dossiers. Nous allons voir comment utiliser et
exploiter un tel objet et interagir avec lui : ne vous inquiétez pas, tout partira de zéro…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Exemple d'arbre
La chose bien pratique avec cet objet c'est que, même s'il ne ressemble pas à un chêne ou à un autre arbre, il est composé de la même façon ! En fait, lorsque vous
regardez bien un arbre, celui-ci est constitué de plusieurs sous-ensembles :
des racines ;
un tronc ;
des branches ;
des feuilles.
une racine : le répertoire le plus haut dans la hiérarchie ; ici, seul « Racine » est considéré comme une racine ;
une ou plusieurs branches : un ou plusieurs sous-répertoires, « Fichier enfant n° 1-2-3-4 » sont des branches (ou encore « Nœud n° 2-4-6 ») ;
une ou plusieurs feuilles : éléments se trouvant en bas de la hiérarchie, ici « Sous-fichier enfant n° 1-2-3-4 » ou encore « Nœud n° 1-3-5-7 » sont des feuilles.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//CTRL + SHIFT + O pour générer les imports nécessaires
[Link](true);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
//On ajoute la feuille ou la branche à la racine
[Link](rep);
}
//Nous créons, avec notre hiérarchie, un arbre
arbre = new JTree(racine);
//Que nous plaçons sur le ContentPane de notre JFrame à l'aide d'un scroll
[Link]().add(new JScrollPane(arbre));
}
Si vous avez du mal à vous y retrouver, essayez cette version de la méthode buildTree() :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
//Nous créons, avec notre hiérarchie, un arbre
arbre = new JTree(racine);
//Que nous plaçons sur le ContentPane de notre JFrame à l'aide d'un scroll
[Link]().add(new JScrollPane(arbre));
}
En ayant manipulé ces deux objets, vous devez vous rendre compte que vous construisez une véritable hiérarchie avant de créer et d'a icher votre arbre ! Ce type
d'objet est tout indiqué pour lister des fichiers ou des répertoires. D'ailleurs, nous avons vu comment faire lorsque nous avons abordé les flux. C'est avec un arbre
que nous allons a icher notre arborescence de fichiers :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public Fenetre(){
[Link](300, 300);
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
[Link]("Les arbres");
//On invoque la méthode de construction de l'arbre
listRoot();
[Link](true);
}
[Link](lecteur);
}
//Nous créons, avec notre hiérarchie, un arbre
arbre = new JTree([Link]);
//Que nous plaçons sur le ContentPane de notre JFrame à l'aide d'un scroll
[Link]().add(new JScrollPane(arbre));
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
int count = 0;
if([Link]())
return new DefaultMutableTreeNode([Link]());
else{
File[] list = [Link]();
if(list == null)
return new DefaultMutableTreeNode([Link]());
Ce type de code ne devrait plus vous faire peur. La figure suivante montre ce que ça me donne, après quelques secondes…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Arborescence de fichiers
Pas mal, mais du coup, le dossier « Racine » ne correspond à rien ! Heureusement, il existe une méthode dans l'objet JTree qui permet de ne pas a icher la
racine d'une arborescence : setRootVisible(Boolean ok); . Il su it donc de rajouter l'instruction setRootVisible(false); à la fin de la méthode
listRoot() de l'objet JTree , juste avant d'ajouter notre arbre au ContentPane .
Bon : vous arrivez à créer et a icher un arbre. Maintenant, voyons comment interagir avec !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//CTRL + SHIFT + O pour générer les imports nécessaires
[Link](true);
}
[Link](lecteur);
}
//Nous créons, avec notre hiérarchie, un arbre
arbre = new JTree([Link]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](false);
[Link](new TreeSelectionListener(){
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](subNode);
}
}
return node;
}
}
Votre arbre est maintenant réactif ! Lorsque vous sélectionnez un dossier ou un fichier, le nom de ce dernier s'a iche. Cela se fait grâce à la méthode
getLastSelectedPathComponent() : elle retourne un Object correspondant au dernier point de l'arbre qui a été cliqué. Il ne reste plus qu'à utiliser la
méthode toString() afin de retourner son libellé.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Nous avons réussi à a icher le nom du dernier nœud cliqué, mais nous n'allons pas nous arrêter là… Il peut être intéressant de connaître le chemin d'accès du
nœud dans l'arbre ! Surtout dans notre cas, puisque nous listons le contenu de notre disque.
Nous pouvons donc obtenir des informations supplémentaires sur une feuille ou une branche en recourant à un objet File , par exemple. L'objet TreeEvent
passé en paramètre de la méthode de l'interface vous apporte de précieux renseignements, dont la méthode getPath() qui vous retourne un objet TreePath .
Ce dernier contient les objets correspondant aux nœuds du chemin d'accès à un point de l'arbre. Ne vous inquiétez pas, vous n'avez pas à changer beaucoup de
choses pour obtenir ce résultat.
En fait, je n'ai modifié que la classe anonyme qui gère l'événement déclenché sur l'arbre. Voici la nouvelle version de cette classe anonyme :
[Link](new TreeSelectionListener(){
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
A ichage du chemin complet des nœuds
Vous pouvez voir que nous avons maintenant le chemin complet dans notre arbre et, vu que nous interagissons avec les fichiers de notre système, nous pourrons
en savoir plus.
Nous allons donc ajouter un « coin information » à droite de notre arbre, dans un conteneur à part.
Essayez de le faire vous-mêmes dans un premier temps, sachant que j'ai obtenu quelque chose comme la figure suivante.
Voir la correction
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
J'espère que vous n'avez pas eu trop de mal à faire ce petit exercice… Vous devriez maintenant commencer à savoir utiliser ce type d'objet, mais avant de passer
à autre chose, je vous propose d'apprendre à personnaliser un peu l'a ichage de notre arbre.
Icônes personnalisées
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//CTRL + SHIFT + O pour générer les imports nécessaires
public Fenetre(){
[Link](600, 350);
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
[Link]("Les arbres");
//On invoque la méthode de construction de l'arbre
initRenderer();
listRoot();
[Link](true);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private void listRoot(){
[Link] = new DefaultMutableTreeNode();
int count = 0;
for(File file : [Link]()){
DefaultMutableTreeNode lecteur = new DefaultMutableTreeNode([Link]());
try {
for(File nom : [Link]()){
DefaultMutableTreeNode node = new DefaultMutableTreeNode([Link]()+"\\");
[Link]([Link](nom, node));
}
} catch (NullPointerException e) {}
[Link](lecteur);
}
//Nous créons, avec notre hiérarchie, un arbre
arbre = new JTree([Link]);
[Link](false);
//On définit le rendu pour cet arbre
[Link]([Link][0]);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
JSplitPane split2 = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT,
new JScrollPane(arbre),
split);
[Link](200);
[Link]().add(split2);
}
if([Link]())
return new DefaultMutableTreeNode([Link]());
else{
File[] list = [Link]();
if(list == null)
return new DefaultMutableTreeNode([Link]());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
C'est simple, n'est-ce pas ? Vous définissez les nouvelles images et indiquez à l'arbre le modèle à utiliser !
Il existe une autre façon de changer l'a ichage (le design) de votre application. Chaque système d'exploitation possède son propre « design », mais vous avez pu
constater que vos applications Java ne ressemblent pas du tout à ce que votre OS (Operating System, ou système d'exploitation) vous propose d'habitude ! Les
couleurs, mais aussi la façon dont sont dessinés vos composants… Mais il y a un moyen de pallier ce problème : utiliser le « look and feel » de votre OS.
J'ai rajouté ces lignes de code dans le constructeur de mon objet, avant l'instruction setVisible(true) :
try {
//On force à utiliser le « look and feel » du système
[Link]([Link]());
//Ici on force tous les composants de notre fenêtre (this) à se redessiner avec le « look and feel » du système
[Link](this);
}
catch (InstantiationException e) {}
catch (ClassNotFoundException e) {}
catch (UnsupportedLookAndFeelException e) {}
catch (IllegalAccessException e) {}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Design de l'OS forcé
Bien sûr, vous pouvez utiliser d'autres « look and feel » que ceux de votre système et de Java. Voici un code qui permet de lister ces types d'a ichage et
d'instancier un objet Fenetre en lui spécifiant quel modèle utiliser :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("Nom du look and feel : " + title);
listRoot();
//On force l'utilisation
try {
[Link](lookAndFeel);
[Link](this);
}
catch (InstantiationException e) {}
catch (ClassNotFoundException e) {}
catch (UnsupportedLookAndFeelException e) {}
catch (IllegalAccessException e) {}
[Link](true);
}
//…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Di érents « look and feel »
TreePath : objet qui vous permet de connaître le chemin d'un nœud dans l'arbre. La voilà, notre classe concrète ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
TreeCellRenderer : interface permettant de modifier l'apparence d'un nœud ;
Vous allez voir que, même si ces objets sont nombreux, leur utilisation, avec un peu de pratique, n'est pas aussi compliquée que ça. Nous allons commencer par
quelque chose d'assez simple : modifier le libellé d'un nœud !
Il faudra commencer par le rendre éditable, via la méthode setEnabled(Boolean bok) de notre JTree . Attention, vous serez peut-être amenés à sauvegarder
le nouveau nom de votre nœud. Il faudra donc déclencher un traitement lors de la modification d'un nœud. Pour faire cela, nous allons utiliser l'objet
TreeModel et l'écouter afin de déterminer ce qui se passe avec notre arbre !
Voici un exemple de code utilisant un DefaultTreeModel (classe implémentant l'interface TreeModel ) ainsi qu'une implémentation de l'interface
TreeModelListener afin d'écouter cet objet :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
try {
for(File nom : [Link]()){
DefaultMutableTreeNode node = new DefaultMutableTreeNode([Link]()+"\\");
[Link]([Link](nom, node));
}
} catch (NullPointerException e) {}
[Link](lecteur);
}
/**
* Méthode appelée lorsqu'un noeud est inséré
*/
public void treeNodesInserted(TreeModelEvent event) {
[Link]("Un noeud a été inséré !");
}
/**
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
* Méthode appelée lorsqu'un noeud est supprimé
*/
public void treeNodesRemoved(TreeModelEvent event) {
[Link]("Un noeud a été retiré !");
}
/**
* Méthode appelée lorsque la structure d'un noeud a été modifiée
*/
public void treeStructureChanged(TreeModelEvent event) {
[Link]("La structure d'un noeud a changé !");
}
});
if([Link]())
return new DefaultMutableTreeNode([Link]());
else{
File[] list = [Link]();
if(list == null)
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
return new DefaultMutableTreeNode([Link]());
try {
[Link]([Link]());
}
catch (InstantiationException e) {}
catch (ClassNotFoundException e) {}
catch (UnsupportedLookAndFeelException e) {}
catch (IllegalAccessException e) {}
Fenetre fen = new Fenetre();
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Afin de pouvoir changer le nom d'un nœud, vous devez double-cliquer dessus avec un intervalle d'environ une demi-seconde entre chaque clic… Si vous
double-cliquez trop vite, vous déplierez le nœud !
Le dossier « toto » s'appelait « CNAM/ » : vous pouvez voir que lorsque nous changeons le nom d'un nœud, la méthode
treeNodesChanged(TreeModelEvent evt) est invoquée !
Vous voyez que, mis à part le fait que plusieurs objets sont mis en jeu, ce n'est pas si compliqué que ça…
Maintenant, je vous propose d’examiner la manière d'ajouter des nœuds à notre arbre. Pour ce faire, nous allons utiliser un bouton qui va nous demander de
spécifier le nom du nouveau nœud, via un JOptionPane .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private DefaultMutableTreeNode racine;
private DefaultTreeModel model;
private JButton bouton = new JButton("Ajouter");
public Fenetre(){
[Link](200, 300);
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
[Link]("JTree");
//On invoque la méthode de construction de l'arbre
listRoot();
[Link](new ActionListener(){
public void actionPerformed(ActionEvent event) {
if([Link]() != null){
JOptionPane jop = new JOptionPane();
String nodeName = [Link]("Saisir un nom de noeud");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](true);
}
int count = 0;
for(File file : [Link]())
{
DefaultMutableTreeNode lecteur = new DefaultMutableTreeNode([Link]());
try {
for(File nom : [Link]()){
DefaultMutableTreeNode node = new DefaultMutableTreeNode([Link]()+"\\");
[Link]([Link](nom, node));
}
} catch (NullPointerException e) {}
[Link](lecteur);
}
//Nous créons, avec notre hiérarchie, un arbre
arbre = new JTree();
[Link] = new DefaultTreeModel([Link]);
[Link](model);
[Link](false);
[Link](true);
[Link]().addTreeModelListener(new TreeModelListener() {
public void treeNodesChanged(TreeModelEvent evt) {
[Link]("Changement dans l'arbre");
Object[] listNoeuds = [Link]();
int[] listIndices = [Link]();
for (int i = 0; i < [Link]; i++) {
[Link]("Index " + listIndices[i] + ", noeud déclencheur : " + listNoeuds[i]);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
public void treeNodesInserted(TreeModelEvent event) {
[Link]("Un noeud a été inséré !");
}
public void treeNodesRemoved(TreeModelEvent event) {
[Link]("Un noeud a été retiré !");
}
public void treeStructureChanged(TreeModelEvent event) {
[Link]("La structure d'un noeud a changé !");
}
});
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
subNode = new DefaultMutableTreeNode([Link]());
}
[Link](subNode);
}
}
return node;
}
}
try {
[Link]([Link]());
}
catch (InstantiationException e) {}
catch (ClassNotFoundException e) {}
catch (UnsupportedLookAndFeelException e) {}
catch (IllegalAccessException e) {}
Fenetre fen = new Fenetre();
}
}
Vous remarquerez que nous avons ajouté des variables d'instances afin d'y avoir accès dans toute notre classe !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Là non plus, rien d'extraordinairement compliqué, mis à part cette portion de code :
parentNode = (DefaultMutableTreeNode)[Link]();
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(nodeName);
DefaultMutableTreeNode [Link](childNode);
[Link](childNode, parentNode, [Link]()-1);
[Link](parentNode);
Tout d'abord, nous récupérons le dernier nœud sélectionné avec la ligne 1. Ensuite, nous créons un nouveau nœud avec la ligne 2 et l'ajoutons dans le nœud
parent avec la ligne 3. Cependant, nous devons spécifier à notre modèle qu'il contient un nouveau nœud et donc qu'il a changé, au moyen des instructions des
lignes 4 et 5.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Pour supprimer un nœud, il su irait d'appeler [Link](node) .
Voilà : je pense que vous en savez assez pour utiliser les arbres dans vos futures applications !
Afin d'intercepter les événements sur tel ou tel composant, vous devez implémenter l'interface TreeSelectionListener .
Cette interface n'a qu'une méthode à redéfinir : public void valueChanged(TreeSelectionEvent event) .
L'a ichage des di érents éléments constituant un arbre peut être modifié à l'aide d'un DefaultTreeCellRenderer . Définissez et a ectez cet objet à votre
arbre pour en personnaliser l'a ichage.
Vous pouvez aussi changer le « look and feel » et utiliser celui de votre OS.
Les choses se compliquent dès que l'on doit manipuler les données à l'intérieur du tableau, car Java impose de séparer strictement l'a ichage et les
données dans le code.
Premiers pas
Les tableaux sont des composants qui permettent d'a icher des données de façon structurée.
Pour ceux qui ne savent pas ce que c'est, en voici un à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Exemple de tableau
public Fenetre(){
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
[Link]("JTable");
[Link](300, 120);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static void main(String[] args){
Fenetre fen = new Fenetre();
[Link](true);
}
}
Vous instanciez un objet JTable en lui passant en paramètres les données qu'il doit utiliser.
Les titres des colonnes de votre tableau peuvent être de type String ou de type Object , tandis que les données sont obligatoirement de type Object .
Vous verrez un peu plus loin qu'il est possible de mettre plusieurs types d'éléments dans un tableau. Mais nous n'en sommes pas là : il nous faut d'abord
comprendre comment fonctionne cet objet.
Les plus observateurs auront remarqué que j'ai mis le tableau dans un scroll… En fait, si vous avez essayé d'ajouter le tableau dans le contentPane sans scroll,
vous avez dû constater que les titres des colonnes n'apparaissent pas.
En e et, le scroll indique automatiquement au tableau l'endroit où il doit a icher ses titres ! Sans lui, vous seriez obligés de préciser où a icher l'en-tête du
tableau, comme ceci :
Je pense que nous avons fait le tour des préliminaires… Entrons dans le vif du sujet !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vos tableaux sont composés de cellules. Vous pouvez les voir facilement, elles sont encadrées de bordures noires et contiennent les données que vous avez mises
dans le tableau d' Object et de String . Celles-ci peuvent être retrouvées par leurs coordonnées (x, y) où x correspond au numéro de la ligne et y au numéro de
la colonne ! Une cellule est donc l'intersection d'une ligne et d'une colonne.
Afin de modifier une cellule, il faut récupérer la ligne et la colonne auxquelles elle appartient. Ne vous inquiétez pas, nous allons prendre tout cela point par point.
Tout d'abord, commençons par changer la taille d'une colonne et d'une ligne. Le résultat final ressemble à ce qu'on voit sur la figure suivante.
Changement de taille
Vous allez voir que le code utilisé est simple comme tout, encore fallait-il que vous sachiez quelles méthodes et quels objets utiliser… Voici le code permettant
d'obtenir ce résultat :
public Fenetre(){
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
[Link]("JTable");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](300, 240);
Object[][] data = {
{"Cysboy", "28 ans", "1.80 m"},
{"BZHHydde", "28 ans", "1.80 m"},
{"IamBow", "24 ans", "1.90 m"},
{"FunMan", "32 ans", "1.85 m"}
};
[Link](new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
changeSize(200, 80);
[Link](false);
[Link](true);
}
});
[Link](new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
changeSize(75, 16);
[Link](true);
[Link](false);
}
});
[Link](false);
[Link](change);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](retablir);
/**
* Change la taille d'une ligne et d'une colonne
* J'ai mis deux boucles afin que vous puissiez voir
* comment parcourir les colonnes et les lignes
*/
public void changeSize(int width, int height){
//Nous créons un objet TableColumn afin de travailler sur notre colonne
TableColumn col;
for(int i = 0; i < [Link](); i++){
if(i == 1){
//On récupère le modèle de la colonne
col = [Link]().getColumn(i);
//On lui affecte la nouvelle valeur
[Link](width);
}
}
for(int i = 0; i < [Link](); i++){
//On affecte la taille de la ligne à l'indice spécifié !
if(i == 1)
[Link](i, height);
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Tout comme pour les tableaux vus dans la première partie de cet ouvrage, les indices des lignes et des colonnes d'un objet JTable commencent à 0 !
Vous constatez que la ligne et la colonne concernées changent bien de taille lors du clic sur les boutons. Vous venez donc de voir comment changer la taille des
cellules de façon dynamique. Je dis ça parce que, au cas où vous ne l'auriez pas remarqué, vous pouvez changer la taille des colonnes manuellement. Il vous su it
de cliquer sur un séparateur de colonne, de maintenir le clic et de déplacer le séparateur, comme indiqué à la figure suivante.
Séparateurs
Par contre, cette instruction a dû vous sembler étrange : [Link]().getColumn(i); . En fait, vous devez savoir que c'est un objet qui fait le
lien entre votre tableau et vos données. Celui-ci est ce qu'on appelle un modèle de tableau (ça vous rappelle les modèles d'arbres, non ?). L'objet en question
s'appelle JTableModel et vous allez voir qu'il permet de faire des choses très intéressantes ! C'est lui qui stocke vos données… Toutes vos données !
public Fenetre(){
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
[Link]("JTable");
[Link](600, 140);
Object[][] data = {
{"Cysboy", new JButton("6boy"), new Double(1.80), new Boolean(true)},
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
{"BZHHydde", new JButton("BZH"), new Double(1.78), new Boolean(false)},
{"IamBow", new JButton("BoW"), new Double(1.90), new Boolean(false)},
{"FunMan", new JButton("Year"), new Double(1.85), new Boolean(true)}
};
Pour être le plus flexible possible, on doit créer son propre modèle qui va stocker les données du tableau. Il vous su it de créer une classe héritant de
AbstractTableModel qui — vous l'avez sûrement deviné — est une classe abstraite…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private JTable tableau;
private JButton change = new JButton("Changer la taille");
public Fenetre(){
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
[Link]("JTable");
[Link](600, 140);
Object[][] data = {
{"Cysboy", new JButton("6boy"), new Double(1.80), new Boolean(true)},
{"BZHHydde", new JButton("BZH"), new Double(1.78), new Boolean(false)},
{"IamBow", new JButton("BoW"), new Double(1.90), new Boolean(false)},
{"FunMan", new JButton("Year"), new Double(1.85), new Boolean(true)}
};
//Constructeur
public ZModel(Object[][] data, String[] title){
[Link] = data;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link] = title;
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Utilisation d'un modèle de tableau
Bon… Vous ne voyez plus les titres des colonnes. Ceci est dû au fait que vous n'avez redéfini que les méthodes abstraites de la classe AbstractTableModel . Si
nous voulons voir nos titres de colonnes apparaître, il faut redéfinir la méthode getColumnName(int col) . Elle devrait ressembler à ceci :
/**
* Retourne le titre de la colonne à l'indice spécifié
*/
public String getColumnName(int col) {
return [Link][col];
}
Exécutez à nouveau votre code, après avoir rajouté cette méthode dans votre objet ZModel : vous devriez avoir le même rendu que la figure suivante.
Regardez la figure suivante pour comprendre l'intérêt de gérer sa propre classe de modèle.
A ichage de checkbox
Vous avez vu ? Les booléens se sont transformés en cases à cocher ! Les booléens valant « vrai » sont devenus des cases cochées et les booléens valant « faux »
sont maintenant des cases non cochées ! Pour obtenir ça, j'ai redéfini une méthode dans mon modèle et le reste est automatique. Cette méthode permet de
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
retourner la classe du type de valeur d'un modèle et de transformer vos booléens en cases à cocher… Au moment où notre objet crée le rendu des cellules, il
invoque cette méthode et s'en sert pour créer certaines choses, comme ce que vous venez de voir.
Pour obtenir ce rendu, il vous su it de redéfinir la méthode getColumnClass(int col) . Cette méthode retourne un objet Class . Je vous laisse réfléchir un
peu…
Pour savoir comment faire, c'est juste en dessous :
Je ne sais pas si vous avez remarqué, mais vos cellules ne sont plus éditables ! Je vous avais prévenus que ce composant était pénible… En fait, vous devez aussi
informer votre modèle qu'il faut avertir l'objet JTable que certaines cellules peuvent être éditées et d'autres pas (le bouton, par exemple). Pour ce faire, il faut
redéfinir la méthode isCellEditable(int row, int col) qui, dans la classe mère, retourne systématiquement false …
Ajoutez donc ce morceau de code dans votre modèle pour renvoyer true :
Vos cellules sont à nouveau éditables. Cependant, vous n'avez pas précisé au modèle que la cellule contenant le bouton n'est pas censée être éditable…
Comment régler ce problème ? Grâce à la méthode getClass() de tout objet Java ! Vous pouvez déterminer de quelle classe est issu votre objet grâce au mot-
clé instanceof . Regardez comment on procède :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
return false;
return true;
}
Victoire ! Les cellules sont à nouveau éditables, sauf le JButton . D'ailleurs, je suppose que certains d'entre vous attendent toujours de le voir apparaître… Pour
cela, nous n'allons pas utiliser un modèle de tableau, mais un objet qui s'occupe de gérer le contenu et la façon dont celui-ci est a iché.
Les modèles font un pont entre ce qu'a iche JTable et les actions de l'utilisateur. Pour modifier l'a ichage des cellules, nous devrons utiliser
DefaultCellRenderer .
Le but du jeu est de définir une nouvelle façon de dessiner les composants dans les cellules. En définitive, nous n'allons pas vraiment faire cela, mais dire à notre
tableau que la valeur contenue dans une cellule donnée est un composant (bouton ou autre). Il su it de créer une classe héritant de
DefaultTableCellRenderer et de redéfinir la méthode
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) .
Il y en a, des paramètres ! Mais, dans le cas qui nous intéresse, nous n'avons besoin que d'un seul d'entre eux : value . Remarquez que cette méthode retourne
un objet Component . Nous allons seulement spécifier le type d'objet dont il s'agit suivant le cas.
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int
column) {
//Si la valeur de la cellule est un JButton, on transtype cette valeur
if (value instanceof JButton)
return (JButton) value;
else
return this;
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Une fois cette classe créée, il su it de signaler à notre tableau qu'il doit utiliser ce rendu de cellules grâce à l'instruction
[Link]([Link], new TableComponent()); . Le premier paramètre permet de dire à notre tableau de faire attention à
ce type d'objet et enfin, le second lui dit d'utiliser ce modèle de cellules.
Voilà notre bouton en chair et en os ! Je me doute bien que les plus taquins d'entre vous ont dû essayer de mettre plus d'un type de composant dans le tableau…
Et ils se retrouvent le bec dans l'eau car il ne prend en compte que les boutons pour le moment.
En fait, une fois que vous avez défini une classe héritée afin de gérer le rendu de vos cellules, vous avez fait le plus gros du travail… Tenez, si, par exemple, nous
voulons mettre ce genre de données dans notre tableau :
Object[][] data = {
{"Cysboy", new JButton("6boy"), new JComboBox(new String[]{"toto", "titi", "tata"}), new Boolean(true)},
{"BZHHydde", new JButton("BZH"), new JComboBox(new String[]{"toto", "titi", "tata"}), new Boolean(false)},
{"IamBow", new JButton("BoW"), new JComboBox(new String[]{"toto", "titi", "tata"}), new Boolean(false)},
{"FunMan", new JButton("Year"), new JComboBox(new String[]{"toto", "titi", "tata"}), new Boolean(true)}
};
… et si nous conservons l'objet de rendu de cellules tel qu'il est actuellement, nous obtiendrons la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Problème d'a ichage d'une combo
Les boutons s'a ichent toujours, mais pas les combos ! Je sais que certains d'entre vous ont presque trouvé la solution. Vous n'auriez pas ajouté ce qui suit dans
votre objet de rendu de cellule ?
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int
column) {
if (value instanceof JButton)
return (JButton) value;
//Lignes ajoutées
else if(value instanceof JComboBox)
return (JComboBox) value;
else
return this;
}
}
Ceux qui ont fait cela ont trouvé la première partie de la solution ! Vous avez bien spécifié à votre objet de retourner une valeur castée en cas de rencontre avec
une combo. Seulement, et j'en suis quasiment sûr, vous avez dû oublier de dire à votre tableau de faire attention aux boutons et aux combos ! Rappelez-vous cette
instruction :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Votre tableau ne fait attention qu'aux boutons ! Pour corriger le tir, il faut simplement changer [Link] en [Link] . Après avoir fait ces
deux modifications, vous devriez parvenir à la figure suivante.
Maintenant, vous devriez avoir saisi la manière d'utiliser les modèles de tableaux et les rendus de cellules… Cependant, vous aurez constaté que vos composants
sont inertes ! C'est dû au fait que vous allez devoir gérer vous-mêmes la façon dont réagissent les cellules. Cependant, avant d'aborder ce point, nous allons nous
pencher sur une autre façon d'a icher correctement des composants dans un JTable . Nous pouvons laisser de côté notre classe servant de modèle et nous
concentrer sur les composants.
public Fenetre(){
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
[Link]("JTable");
[Link](600, 180);
Object[][] data = {
{"Cysboy", "6boy", "Combo", new Boolean(true)},
{"BZHHydde", "BZH", "Combo", new Boolean(false)},
{"IamBow", "BoW", "Combo", new Boolean(false)},
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
{"FunMan", "Year", "Combo", new Boolean(true)}
};
}
public static void main(String[] args){
Fenetre fen = new Fenetre();
[Link](true);
}
}
De là, nous allons créer une classe qui a ichera un bouton dans les cellules de la seconde colonne et une combo dans les cellules de la troisième colonne… Le
principe est simple : créer une classe qui héritera de la classe JButton et qui implémentera l'interface TableCellRenderer . Nous allons ensuite dire à notre
tableau qu'il doit utiliser utiliser ce type de rendu pour la seconde colonne.
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean isFocus, int row, int
col) {
//On écrit dans le bouton ce que contient la cellule
setText((value != null) ? [Link]() : "");
//On retourne notre bouton
return this;
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Il nous su it maintenant de mettre à jour le tableau grâce à l'instruction [Link]("Age").setCellRenderer(new ButtonRenderer()); :
on récupère la colonne grâce à son titre, puis on applique le rendu !
Résultat en figure suivante.
Votre bouton est de nouveau éditable, mais ce problème sera réglé par la suite… Pour le rendu de la cellule numéro 3, je vous laisse un peu chercher, ce n'est pas
très di icile maintenant que vous avez appris cette méthode.
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean isFocus, int row, int
col) {
[Link]("Très bien");
[Link]("Bien");
[Link]("Mal");
return this;
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Rendu d'une combo
Eh oui, vous verrez que la combo est gérée presque automatiquement, alors qu'il vous faudra dire aux boutons ce qu'ils devront faire… Pour arriver à cela, nous
allons créer une classe qui permettra à notre tableau d'e ectuer des actions spécifiques grâce aux boutons.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On crée à nouveau un bouton
button = new JButton();
[Link](true);
//On lui attribue un listener
[Link](bListener);
}
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
//On précise le numéro de ligne à notre listener
[Link](row);
//Idem pour le numéro de colonne
[Link](column);
//On passe aussi le tableau en paramètre pour des actions potentielles
[Link](table);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("coucou du bouton : " + ((JButton)[Link]()).getText());
//On affecte un nouveau libellé à une autre cellule de la ligne
[Link]("New Value " + (++nbre), [Link], ([Link] -1));
}
}
}
Ce code n'est pas très di icile à comprendre… Vous commencez à avoir l'habitude de manipuler ce genre d'objet. Maintenant, afin d'utiliser cet objet avec notre
tableau, nous allons lui indiquer l'existence de cet éditeur dans la colonne correspondante avec cette instruction :
Bouton actif
Vous pouvez voir que lorsque vous cliquez sur un bouton, la valeur dans la cellule située juste à gauche est modifiée. L'utilisation est donc très simple. Imaginez
par conséquent que la gestion des combos est encore plus aisée !
Un peu plus tôt, je vous ai fait développer une classe permettant d'a icher la combo normalement. Cependant, il y a beaucoup plus facile… Vous avez pu voir que
la classe DefaultCellEditor pouvait prendre un objet en paramètre : dans l'exemple du JButton , il utilisait un JCheckBox . Vous devez savoir que cet objet
accepte d'autres types de paramètres :
un JComboBox ;
un JTextField .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Nous pouvons donc utiliser l'objet DefaultCellEditor directement en lui passant une combo en paramètre… Nous allons aussi enlever l'objet permettant
d'a icher correctement la combo afin que vous puissiez juger de l'e icacité de cette méthode.
Voici le nouveau code du constructeur de notre fenêtre :
public Fenetre(){
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
[Link]("JTable");
[Link](600, 180);
//Données de notre tableau
Object[][] data = {
{"Cysboy", "6boy", comboData[0], new Boolean(true)},
{"BZHHydde", "BZH", comboData[0], new Boolean(false)},
{"IamBow", "BoW", comboData[0], new Boolean(false)},
{"FunMan", "Year", comboData[0], new Boolean(true)}
};
//Titre du tableau
String title[] = {"Pseudo", "Age", "Taille", "OK ?"};
//Combo à utiliser
JComboBox combo = new JComboBox(comboData);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On définit l'éditeur par défaut pour la cellule en lui spécifiant quel type d'affichage prendre en compte
[Link]("Taille").setCellEditor(new DefaultCellEditor(combo));
[Link]().add(new JScrollPane(tableau), [Link]);
}
C'est d'une simplicité enfantine ! Le résultat est, en plus, très convaincant, comme le montre la figure suivante.
DefaultCellEditor et combo
Votre cellule se « transforme » en combo lorsque vous cliquez dessus ! En fait, lorsque le tableau sent une action sur cette cellule, il utilise l'éditeur que vous avez
spécifié pour celle-ci.
Si vous préférez que la combo soit a ichée directement même sans clic de souris, il vous su it de laisser l'objet gérant l'a ichage et le tour est joué. De même,
pour le bouton, si vous enlevez l'objet de rendu du tableau, celui-ci s'a iche comme un bouton lors du clic sur la cellule !
Il ne nous reste plus qu'à voir comment rajouter des informations dans notre tableau, et le tour est joué.
Certains d'entre vous l'auront remarqué, les boutons ont un drôle de comportement.
Cela est dû au fait que vous avez a ecté des comportements spéciaux à votre tableau… Il faut donc définir un modèle à utiliser afin de bien définir tous les
points comme l'a ichage, la mise à jour, etc.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Nous allons donc utiliser un modèle de tableau personnalisé où les actions seront définies par nos soins. Voici la classe Fenetre modifiée en conséquence :
public Fenetre(){
[Link](null);
[Link](JFrame.EXIT_ON_CLOSE);
[Link]("JTable");
[Link](600, 180);
//Données de notre tableau
Object[][] data = {
{"Cysboy", "6boy", comboData[0], new Boolean(true)},
{"BZHHydde", "BZH", comboData[0], new Boolean(false)},
{"IamBow", "BoW", comboData[0], new Boolean(false)},
{"FunMan", "Year", comboData[0], new Boolean(true)}
};
String title[] = {"Pseudo", "Age", "Taille", "OK ?"};
JComboBox combo = new JComboBox(comboData);
//Nous devons utiliser un modèle d'affichage spécifique pour pallier les bugs d'affichage !
ZModel zModel = new ZModel(data, title);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//En lui spécifiant quel type d'affichage prendre en compte
[Link]("Taille").setCellEditor(new DefaultCellEditor(combo));
[Link]().add(new JScrollPane(tableau), [Link]);
}
//Constructeur
public ZModel(Object[][] data, String[] title){
[Link] = data;
[Link] = title;
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
Vous aurez remarqué que nous construisons notre tableau par le biais de notre modèle, ce qui implique que nous devrons également passer par le modèle pour
modifier le tableau ! Conséquence directe : il va falloir modifier un peu notre classe ButtonEditor .
Voici la classe ButtonEditor utilisant le modèle de tableau pour gérer la modification des valeurs :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//CTRL + SHIFT + O pour générer les imports
public class ButtonEditor extends DefaultCellEditor {
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
//On affecte le numéro de ligne au listener
[Link](row);
//Idem pour le numéro de colonne
[Link](column);
//On passe aussi le tableau en paramètre pour des actions potentielles
[Link](table);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private int nbre = 0;
private JButton button;
Voilà : désormais, le bug d'a ichage devrait avoir disparu ! Je vous propose donc de continuer sur notre lancée.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
L'intérêt ? C'est très simple : l'ajout et la suppression dynamiques d'entrées dans un tableau se font via un modèle de rendu, vous vous en doutiez. Cependant,
avec cette notation, vous économisez une ligne de code et vous avez la possibilité d'a ecter diverses tâches à votre modèle de rendu, comme, par exemple,
formater les données…
Dans un premier temps, ajoutons et retirons des lignes à notre tableau. Nous garderons le même code que précédemment avec deux ou trois ajouts :
Le modèle par défaut défini lors de la création du tableau nous donne accès à deux méthodes fort utiles :
addRow(Object[] lineData) : ajoute une ligne au modèle et met automatiquement à jour le tableau ;
removeRow(int row) : e ace une ligne du modèle et met automatiquement à jour le tableau.
Avant de pouvoir utiliser ce modèle, nous allons devoir le récupérer. En fait, c'est notre tableau qui va nous le fournir en invoquant la méthode getModel() qui
retourne un objet TableModel . Attention, un cast sera nécessaire afin de pouvoir utiliser l'objet récupéré ! Par exemple :
((ZModel)[Link]()).removeRow() .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ajout et suppression de lignes
Vous constatez que j'ai ajouté un bouton « Ajouter une ligne » ainsi qu'un bouton « Supprimer la ligne » ; mis à part ça, l'IHM n'a pas changé.
Essayez de développer ces nouvelles fonctionnalités. Pour télécharger le code complet du chapitre, c'est par ici que ça se passe.
Celui-ci prend en paramètres un tableau d'objets à deux dimensions (un tableau de données) correspondant aux données à a icher, et un tableau de chaînes
de caractères qui, lui, a ichera les titres des colonnes.
Afin de gérer vous-mêmes le contenu du tableau, vous pouvez utiliser un modèle de données ( TableModel ).
Pour ajouter ou retirer des lignes à un tableau, il faut passer par un modèle de données. Ainsi, l'a ichage est mis à jour automatiquement.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
La gestion de l'a ichage brut (hors édition) des cellules peut se gérer colonne par colonne à l'aide d'une classe dérivant de TableCellRenderer .
La gestion de l'a ichage brut lors de l'édition d'une cellule se gère colonne par colonne avec une classe dérivant de DefaultCellEditor .
TP : le pendu
Ce TP est sûrement le plus di icile que vous aurez à réaliser ! Il fait appel à beaucoup d'éléments vus précédemment. Nous allons devoir réaliser un jeu de
pendu. Le principe est classique : vous devez trouver un mot secret lettre par lettre en faisant un minimum d'erreurs.
Nous en profiterons pour utiliser des design patterns, ces fameuses bonnes pratiques de programmation.
les points doivent être cumulés en tenant compte des mots trouvés et des erreurs commises ;
il faut vérifier si le joueur est dans le top dix, auquel cas on lui demande son pseudo, on enregistre les données et on le redirige vers la page des scores ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Écran principal et règles du jeu
Vous pouvez voir les écrans que j'ai obtenus à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Captures d'écran du jeu
Je vous fournis également les images que j'ai utilisées pour réaliser ce pendu.
Télécharger les images
Vous aurez besoin d'un « fichier dictionnaire » contenant de nombreux mots pour votre jeu :
Télécharger les fichiers
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.
Prérequis
Vous devrez utiliser les flux afin de parcourir le fichier texte qui contient plus de 336 000 lignes : il faudra donc choisir un nombre aléatoire entre 0 et 336 529, puis
récupérer le mot désigné. Pour obtenir un nombre aléatoire entre 0 et 336 529, j'ai codé ceci :
Afin de récupérer les mots ligne par ligne, j'ai utilisé un LineNumberReader : puisque cet objet retourne le numéro de la ligne en invoquant la méthode
getLineNumber() , il était tout indiqué ! Il y a aussi un point qui peut vous poser problème : la mise à jour du JPanel . Pour ma part, j'ai choisi la technique
suivante : tout retirer du conteneur grâce à la méthode removeAll() , replacer des composants puis invoquer la méthode revalidate() afin de modifier
l'a ichage.
Il faudra également que vous pensiez à gérer les caractères accentués, lorsque vous cliquerez sur le bouton « E » par exemple. Vous devrez donc aussi a icher les
lettres « E » accentuées.
Je ne vais pas tout vous dévoiler, il serait dommage de gâcher le plaisir. En revanche, j'insiste sur le fait que c'est un TP di icile, et qu'il vous faudra certainement
plusieurs heures avant d'en venir à bout. Prenez donc le temps de déterminer les problèmes, réfléchissez bien et codez proprement !
Je vous conseille vivement d'aller relire les chapitres traitant des design patterns, car j'en ai utilisé ici ; de plus, j'ai rangé mes classes en packages.
Allez, en avant !
Correction
Une fois n'est pas coutume, je ne vais pas inscrire ici tous les codes source, mais plutôt vous fournir tout mon projet Eclipse contenant un .jar exécutable ; et
pour cause, il contient beaucoup de classes, comme vous le pouvez le voir à la figure suivante.
Récupérer le projet
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Classes du TP
Une fois Eclipse ouvert, e ectuez un clic droit dans la zone où se trouvent vos projets, puis cliquez sur Import et choisissez Existing project dans
General (voir figure suivante).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Importer un projet existant dans Eclipse
Il ne vous reste plus qu'à spécifier l'endroit où vous avez décompressé l'archive .jar que je vous ai fournie, et le tour est joué.
Une fois l'archive décompressée, vous devriez pouvoir lancer le fichier .jar par un double-clic. Si rien ne se produit, mettez à jour vos variables
d'environnement (voir le premier chapitre).
Prenez bien le temps de lire et comprendre le code. Si vous n'arrivez pas à tout faire maintenant, essayez de commencer par réaliser les choses les plus simples,
vous pourrez toujours améliorer votre travail plus tard lorsque vous vous sentirez plus à l'aise !
Vous pourrez constater que j'ai rangé mon code d'une façon assez étrange, avec un package [Link] et un [Link] … Cette façon de procéder
correspond à un autre pattern de conception permettant de séparer le code en couches capables d'interagir entre elles : c'est le sujet du chapitre suivant.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ce chapitre va vous présenter un des design patterns les plus connus : MVC. Il va vous apprendre à découper vos codes en trois parties : modèle, vue et
contrôleur. C'est un pattern composé, ce qui signifie qu'il est constitué d'au moins deux patterns (mais rien n'empêche qu'il y en ait plus).
Nous allons voir cela tout de suite, inutile de tergiverser plus longtemps !
Premiers pas
Dans les chapitres précédents, nous avons agi de la manière suivante :
découverte du pattern.
Ici, nous procéderons autrement : puisque le pattern MVC est plus complexe à aborder, nous allons entrer directement dans le vif du sujet. Le schéma présenté à
la figure suivante en décrit le principe ; il ne devrait pas être étranger à ceux d'entre vous qui auraient déjà fait quelques recherches concernant ce pattern.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Schéma du pattern MVC
Avant d'expliquer ce schéma, nous devons faire le point sur ce que sont réellement ces trois entités.
La vue
Ce que l'on nomme « la vue » est en fait une IHM. Elle représente ce que l'utilisateur a sous les yeux. La vue peut donc être :
une application graphique Swing , AWT , SWT pour Java ( Form pour C#…) ;
etc.
Le modèle
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Le modèle peut être divers et varié. C'est là que se trouvent les données. Il s'agit en général d'un ou plusieurs objets Java. Ces objets s'apparentent généralement
à ce qu'on appelle souvent « la couche métier » de l'application et e ectuent des traitements absolument transparents pour l'utilisateur. Par exemple, on peut
citer des objets dont le rôle est de gérer une ou plusieurs tables d'une base de données. En trois mots, il s'agit du cœur du programme !
Dans le chapitre précédent, nous avons confectionné un jeu du pendu. Dans cette application, notre fenêtre Swing correspond à la vue et l'objet Model
correspond au modèle.
Le contrôleur
Cet objet - car il s'agit aussi d'un objet - permet de faire le lien entre la vue et le modèle lorsqu'une action utilisateur est intervenue sur la vue. C'est cet objet qui
aura pour rôle de contrôler les données.
Maintenant que toute la lumière est faite sur les trois composants de ce pattern, je vais expliquer plus précisément la façon dont il travaille.
Afin de travailler sur un exemple concret, nous allons reprendre notre calculatrice issue d'un TP précédent.
l'utilisateur e ectue une action sur votre calculatrice (un clic sur un bouton) ;
l'action est captée par le contrôleur, qui va vérifier la cohérence des données et éventuellement les transformer afin que le modèle les comprenne. Le
contrôleur peut aussi demander à la vue de changer ;
le modèle reçoit les données et change d'état (une variable qui change, par exemple) ;
le modèle notifie la vue (ou les vues) qu'il faut se mettre à jour ;
l'a ichage dans la vue (ou les vues) est modifié en conséquence en allant chercher l'état du modèle.
Je vous disais plus haut que le pattern MVC était un pattern composé : à ce stade de votre apprentissage, vous pouvez isoler deux patterns dans cette architecture.
Le pattern observer se trouve au niveau du modèle. Ainsi, lorsque celui-ci va changer d'état, tous les objets qui l'observeront seront mis au courant
automatiquement, et ce, avec un couplage faible !
Le deuxième est plus di icile à voir mais il s'agit du pattern strategy ! Ce pattern est situé au niveau du contrôleur. On dit aussi que le contrôleur est la stratégie (en
référence au pattern du même nom) de la vue. En fait, le contrôleur va transférer les données de l'utilisateur au modèle et il a tout à fait le droit de modifier le
contenu.
Ceux qui se demandent pourquoi utiliser le pattern strategy pourront se souvenir de la raison d'être de ce pattern : encapsuler les morceaux de code qui changent
!
En utilisant ce pattern, vous prévenez les risques potentiels de changement dans votre logique de contrôle. Il vous su ira d'utiliser une autre implémentation de
votre contrôleur afin d'avoir des contrôles di érents.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ceci dit, vous devez tout de même savoir que le modèle et le contrôleur sont intimement liés : un objet contrôleur pour notre calculatrice ne servira que pour
notre calculatrice ! Nous pouvons donc autoriser un couplage fort entre ces deux objets.
Je pense qu'il est temps de se mettre à coder !
Le modèle
Le modèle est l'objet qui sera chargé de stocker les données nécessaires à un calcul (nombre et opérateur) et d'avoir le résultat. Afin de prévoir un changement
éventuel de modèle, nous créerons le notre à partir d'un supertype de modèle : de cette manière, si un changement s'opère, nous pourrons utiliser les di érentes
classes filles de façon polymorphe.
Avant de foncer tête baissée, réfléchissons à ce que notre modèle doit être capable d'e ectuer. Pour réaliser des calculs simples, il devra :
calculer le résultat ;
renvoyer le résultat ;
Très bien : voila donc la liste des méthodes que nous trouverons dans notre classe abstraite.
Comme vous le savez, nous allons utiliser le pattern observer afin de faire communiquer notre modèle avec d'autres objets. Il nous faudra donc une
implémentation de ce pattern ; la voici, dans un package [Link] .
[Link]
package [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public void notifyObserver(String str);
}
[Link]
package [Link];
Notre classe abstraite devra donc implémenter ce pattern afin de centraliser les implémentations. Puisque notre supertype implémente le pattern observer, les
classes héritant de cette dernière hériteront aussi des méthodes de ce pattern !
Voici donc le code de notre classe abstraite que nous placerons dans le package
[Link] .
[Link]
package [Link];
import [Link];
import [Link];
import [Link];
//Effectue le calcul
public abstract void calcul();
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Affichage forcé du résultat
public abstract void getResultat();
Ce code est clair et simple à comprendre. Maintenant, nous allons créer une classe concrète héritant de AbstractModel .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
package [Link];
import [Link];
public class Calculator extends AbstractModel{
//Définit l'opérateur
public void setOperateur(String ope){
//On lance le calcul
calcul();
//Définit le nombre
public void setNombre(String result){
//On concatène le nombre
[Link] += result;
//On met à jour
notifyObserver([Link]);
}
//Force le calcul
public void getResultat() {
calcul();
}
//Réinitialise tout
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public void reset(){
[Link] = 0;
[Link] = "0";
[Link] = "";
//Mise à jour !
notifyObserver([Link]([Link]));
}
//Calcul
public void calcul(){
//S'il n'y a pas d'opérateur, le résultat est le nombre saisi
if([Link]("")){
[Link] = [Link]([Link]);
}
else{
//Si l'opérande n'est pas vide, on calcule avec l'opérateur de calcul
if(){
if([Link]("+"))
[Link] += [Link]([Link]);
if([Link]("-"))
[Link] -= [Link]([Link]);
if([Link]("*"))
[Link] *= [Link]([Link]);
if([Link]("/")){
try{
[Link] /= [Link]([Link]);
}catch(ArithmeticException e){
[Link] = 0;
}
}
}
}
[Link] = "";
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On lance aussi la mise à jour !
notifyObserver([Link]([Link]));
}
}
Voilà, notre modèle est prêt à l'emploi ! Nous allons donc continuer à créer les composants de ce pattern.
Le contrôleur
Celui-ci sera chargé de faire le lien entre notre vue et notre modèle. Nous créerons aussi une classe abstraite afin de définir un supertype de variable pour utiliser,
le cas échéant, des contrôleurs de façon polymorphe.
Que doit faire notre contrôleur? C'est lui qui va intercepter les actions de l'utilisateur, qui va modeler les données et les envoyer au modèle. Il devra donc :
avertir le modèle pour qu'il se réinitialise dans le cas d'un clic sur le bouton reset ;
Voilà donc notre liste de méthodes pour cet objet. Cependant, puisque notre contrôleur doit interagir avec le modèle, il faudra qu'il possède une instance de notre
modèle.
package [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
protected AbstractModel calc;
protected String operateur = "", nbre = "";
protected ArrayList<String> listOperateur = new ArrayList<String>();
//Définit l'opérateur
public void setOperateur(String ope){
[Link] = ope;
control();
}
//Définit le nombre
public void setNombre(String nombre){
[Link] = nombre;
control();
}
//Efface
public void reset(){
[Link]();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Méthode de contrôle
abstract void control();
}
Nous avons défini les actions globales de notre objet de contrôle et vous constatez aussi qu'à chaque action dans notre contrôleur, celui-ci invoque la méthode
control() . Celle-ci va vérifier les données et informer le modèle en conséquence.
Nous allons voir maintenant ce que doit e ectuer notre instance concrète. Voici donc, sans plus tarder, notre classe.
[Link]
package [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Si le nombre est conforme
if([Link]("^[0-9.]+$"))
[Link]([Link]);
[Link] = "";
[Link] = "";
}
}
Vous pouvez voir que cette classe redéfinit la méthode control() et qu'elle permet d'indiquer les informations à envoyer à notre modèle. Celui-ci mis à jour, les
données à a icher dans la vue seront envoyées via l'implémentation du pattern observer entre notre modèle et notre vue. D'ailleurs, il ne nous manque plus
qu'elle, alors allons-y !
La vue
Voici le plus facile à développer et ce que vous devriez maîtriser le mieux… La vue sera créée avec le package [Link] . Je vous donne donc le code source
de notre classe que j'ai mis dans le package [Link] .
[Link]
package [Link];
//CTRL + SHIFT + O pour générer les imports
public class Calculette extends JFrame implements Observer{
String[] tab_string = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ".", "=", "C", "+", "-", "*", "/"};
JButton[] tab_button = new JButton[tab_string.length];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
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 = "";
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](new Dimension(220, 30));
switch(i){
case 11 :
tab_button[i].addActionListener(opeListener);
[Link](tab_button[i]);
break;
case 12 :
tab_button[i].setForeground([Link]);
tab_button[i].addActionListener(new ResetListener());
tab_button[i].setPreferredSize(dim2);
[Link](tab_button[i]);
break;
case 13 :
case 14 :
case 15 :
case 16 :
tab_button[i].setForeground([Link]);
tab_button[i].addActionListener(opeListener);
tab_button[i].setPreferredSize(dim2);
[Link](tab_button[i]);
break;
default :
[Link](tab_button[i]);
tab_button[i].addActionListener(new ChiffreListener());
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
break;
}
}
[Link](ecran);
[Link]([Link]([Link]));
[Link](panEcran, [Link]);
[Link](chiffre, [Link]);
[Link](operateur, [Link]);
}
[Link](((JButton)[Link]()).getText());
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Implémentation du pattern observer
public void update(String str) {
[Link](str);
}
}
Vous devez être à même de comprendre ce code, puisqu'il ressemble beaucoup à notre calculette réalisée dans le TP du chapitre correspondant. Vous constaterez
que la vue contient le contrôleur (juste avant le constructeur de la classe).
Toutes nos classes sont à présent opérationnelles. Il ne nous manque plus qu'une classe de test afin d'observer le résultat. Elle crée les trois composants qui vont
dialoguer entre eux : le modèle (données), la vue (fenêtre) et le contrôleur qui lie les deux.
La voici :
import [Link].*;
import [Link].*;
import [Link];
Testez ce code : le tout fonctionne très bien ! Tous nos objets sont interconnectés et dialoguent facilement, comme le montre la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Notre calculatrice MVC
Comme vous connaissez la façon de travailler de ce pattern, nous allons décortiquer ce qui se passe.
Lorsque nous cliquons sur un chi re :
Il informe le modèle.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Il se passera la même chose lorsque nous cliquerons sur le bouton reset .
Oui, bien sûr. Même sans modèle ! Rappelez-vous de la raison d'exister du design pattern : prévenir des modifications de codes ! Avec une telle architecture, vous
pourrez travailler à trois en même temps sur le code : une personne sur la vue, une sur le modèle, une sur le contrôleur.
J'émets toutefois quelques réserves concernant ce pattern. Bien qu'il soit très utile grâce à ses avantages à long terme, celui-ci complique grandement
votre code et peut le rendre très di icile à comprendre pour une personne extérieure à l'équipe de développement. Même si le design pattern permet de
résoudre beaucoup de problèmes, attention à la « patternite aigüe » : son usage trop fréquent peut rendre le code incompréhensible et son entretien
impossible à réaliser.
Avec ce pattern, le code est découpé en trois parties logiques qui communiquent entre elles :
Le modèle (données)
La vue (fenêtre)
Utiliser ce pattern permet de découpler trois acteurs d'une application, ce qui permet plus de souplesse et une maintenance plus aisée du code.
Le Drag'n Drop
Cette notion est somme toute assez importante à l'heure actuelle : beaucoup de gens l'utilisent, ne serait-ce que pour déplacer des fichiers dans leur
système d'exploitation ou encore faire des copies sur une clé USB.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Pour rappel, le Drag'n Drop - traduit par « Glisser-Déposer » - revient à sélectionner un élément graphique d'un clic gauche, à le déplacer grâce à la souris
tout en maintenant le bouton enfoncé et à le déposer à l'endroit voulu en relâchant le bouton. En Java, cette notion est arrivée avec JDK 1.2, dans le
système graphique awt . Nous verrons comment ceci était géré car, même si ce système est fondu et simplifié avec swing , vous devrez utiliser l'ancienne
gestion de ce comportement, version awt .
Je vous propose de commencer par un exemple simple, en utilisant swing , puis ensuite de découvrir un cas plus complet en utilisant tous les rouages de
ces événements, car il s'agit encore et toujours d'événements.
Présentation
La première chose à faire en swing pour activer le drag'n drop, c'est d'activer cette fonctionnalité dans les composants concernés.
Voici un code de test :
public Test1(){
super("Test de Drag'n Drop");
setSize(300, 200);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]([Link]);
[Link](new BorderLayout());
[Link](text2, [Link]);
[Link](text, [Link]);
[Link](pan2, [Link]);
add(pan, [Link]);
setVisible(true);
}
Vous avez pu constater que le drag'n drop était vraiment très simple à activer…
Récapitulons. Nous avons une fenêtre contenant trois composants : un JTextArea avec le drag'n drop activé et deux JTextField dont seul celui du dessus a
l'option activée.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Lancement du programme
La figure suivante donne le résultat après avoir sélectionné une portion de texte et l'avoir glissée dans le JTextField n° 1.
Texte cliqué-glissé
Enfin, vous trouverez à la figure suivante le résultat d'un déplacement du contenu du JTextField n° 1 vers le JTextField n° 2.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Changement de JTextField
Étant donné que ce dernier JTextField est dépourvu de l'option désirée, vous ne pouvez plus déplacer le texte.
J'ai essayé de faire la même chose avec un JLabel et ça n'a pas fonctionné !
C'est tout à fait normal. Par défaut, le drag'n drop n'est disponible que pour certains composants. D'abord, il ne faut pas confondre l'action « drag » et l'option «
drop ». Certains composants autorisent les deux alors que d'autres n'autorisent que le drag. Voici un tableau récapitulatif des actions autorisées par composant :
JEditorPane X X
JColorChooser X X
JFileChooser X .
JTextPane X X
JTextField X X
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
JTextArea X X
JFormattedTextField X X
JPasswordTextField . X
JLabel . .
JTable X .
JTree X .
JList X .
Certains composants de ce tableau autorisent soit l'export de données, soit l'import de données, soit les deux, soit aucun des deux. Certains composants n'ont
aucun comportement lorsque nous y déposons des données… Ceci est dû à leur complexité et à leurs modes de fonctionnement. Par exemple, donner un
comportement par défaut à un JTree n'est pas une mince a aire. Lors d'un drop, doit-il :
ajouter l'élément ?
De ce fait, le comportement est laissé aux bons soins du développeur, en l'occurrence, vous.
Par contre, il faut que vous gardiez en mémoire que lorsqu'on parle de « drag », il y a deux notions implicites à prendre en compte : le « drag déplacement » et le «
drag copie ».
la copie ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
le déplacement.
Par exemple, sous Windows, lorsque vous déplacez un fichier avec un drag'n drop dans un dossier sans changer de disque dur, ce fichier est entièrement déplacé :
cela revient à faire un couper/coller. En revanche, si vous e ectuez la même opération en maintenant la touche Ctrl , l'action du drag'n drop devient l'équivalent
d'un copier/coller.
L'action « drag déplacement » indique donc les composants autorisant, par défaut, l'action de type couper/coller, l'action « drag copie » indique que les
composants autorisent les actions de type copier/coller. La finalité, bien sûr, étant de déposer des données à l'endroit souhaité.
Gardez bien en tête que ce sont les fonctionnalités activées par défaut sur ces composants.
Tu veux dire que nous pourrions ajouter cette fonctionnalité à notre JLabel ?
Pour répondre à cette question, nous allons devoir mettre le nez dans le fonctionnement caché de cette fonctionnalité.
Fonctionnement
Comme beaucoup d'entre vous ont dû le deviner, le transfert des informations entre deux composants se fait grâce à trois composantes essentielles :
un composant d'origine ;
un composant cible.
Cette vision, bien qu'exacte dans la théorie, se simplifie dans la pratique, pas de panique. Pour schématiser ce que je viens de vous dire, voici un petit diagramme
en figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Fonctionnement du drag'n drop
Ce dernier est assez simple à comprendre : pendant l'opération de drag'n drop, les données transitent d'un composant à l'autre via un objet. Dans l'API Swing, le
mécanisme de drag'n drop est encapsulé dans l'objet JComponent dont tous les objets graphiques héritent, ce qui signifie que tous les objets graphiques
peuvent implémenter cette fonctionnalité.
Afin d'activer le drag'n drop sur un composant graphique qui ne le permet pas par défaut, nous devons utiliser la méthode
setTransferHandler(TransferHandler newHandler) de l'objet JComponent . Cette méthode prend un objet TransferHandler en paramètre : c'est celui-
ci qui lance le mécanisme de drag'n drop.
Les composants du tableau récapitulatif (hormis le JLabel ) ont tous un objet TransferHandler par défaut. Le drag'n drop s'active par la méthode
setDragEnabled(true) sur la plupart des composants, mais comme vous avez pu le constater, pas sur le JLabel … Afin de contourner cela, nous devons lui
spécifier un objet TransferHandler réalisé par nos soins.
Attention, toutefois ! Vous pouvez définir un TransferHandler pour un objet possédant déjà un comportement par défaut, mais cette action supplantera le
mécanisme par défaut du composant : redéfinissez donc les comportements avec prudence !
Retournons à notre JLabel . Afin de lui ajouter les fonctionnalités voulues, nous devons lui a ecter un nouveau TransferHandler . Une fois que ce nouvel
objet lui sera assigné, nous lui ajouterons un événement souris afin de lancer l'action de drag'n drop : je vous rappelle que l'objet TransferHandler ne permet
que le transit des données, il ne gère pas les événements ! Dans notre événement, nous avons juste à récupérer le composant initiateur du drag, récupérer son
objet TransferHandler et invoquer sa méthode exportAsDrag(JComponent comp, InputEvent event, int action) .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public LabelContentDemo(){
setTitle("Drag'n Drop avec un JLabel !");
setSize(300, 100);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//-------------------------------------------------------------------
//On crée le nouvel objet pour activer le drag'n drop
[Link](new TransferHandler("text"));
//On spécifie au composant qu'il doit envoyer ses données via son objet TransferHandler
[Link](new MouseAdapter(){
//On utilise cet événement pour que les actions soient visibles dès le clic de souris…
//Nous aurions pu utiliser mouseReleased, mais, niveau IHM, nous n'aurions rien vu
public void mousePressed(MouseEvent e){
//On récupère le JComponent
JComponent lab = (JComponent)[Link]();
//Du composant, on récupère l'objet de transfert : le nôtre
TransferHandler handle = [Link]();
//On lui ordonne d'amorcer la procédure de drag'n drop
[Link](lab, e, [Link]);
}
});
//-------------------------------------------------------------------
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
JLabel destLib = new JLabel("Destination de drag : ", [Link]);
JTextField dest = new JTextField();
//On active le comportement par défaut de ce composant
[Link](true);
[Link](srcLib);
[Link](src);
[Link](destLib);
[Link](dest);
setContentPane(pan);
setVisible(true);
}
Sur la figure suivante, on déplace le contenu de notre source vers le champ texte.
Avant le drag
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Texte déplacé
Enfin, sur la figure suivante, on déplace un fragment du contenu de notre champ texte vers notre JLabel .
Vous devez avoir plusieurs questions. Déjà, pour ceux qui ne l'auraient pas remarqué (ou essayé), l'objet de transfert n'a pas de constructeur sans argument !
Cette instruction ne compilera pas : TransferHandler trans = new TransferHandler(); . Par contre, le constructeur utilisé fonctionne parfaitement pour
un JLabel TransferHandler trans = new TransferHandler("text"); . Pourquoi ? Tout simplement parce que la chaîne de caractères passée en
paramètre correspond à une propriété JavaBean utilisable par l'objet.
la classe doit être Serializable pour pouvoir sauvegarder et restaurer l'état des instances de cette classe ;
les propriétés privées de la classe (variables d'instance) doivent être accessibles publiquement via des méthodes accesseurs ( get ou set ) suivies du nom
de la propriété avec la première lettre transformée en majuscule ;
En fait, notre objet de transfert va utiliser la propriété « text » de notre objet JLabel , ceci afin de récupérer son contenu et de le faire transiter. Nous verrons plus
tard comment faire pour les cas où nous ne connaissons pas le nom de la propriété…
Ensuite, nous avons récupéré l'objet TransferHandler depuis notre composant : nous le lui avions a ecté avec un setter , nous pouvons le récupérer avec
un getter .
Là où les choses deviennent intéressantes, c'est lorsque nous invoquons la méthode [Link](lab, e, [Link]); . C'est cette
instruction qui amorce réellement le drag'n drop. Les trois paramètres servent à initialiser les actions à e ectuer et à déterminer quand et sur qui les faire :
le second paramètre indique à notre objet l'événement sur lequel il doit déclencher le transfert ;
le dernier indique l'action qui doit être e ectuée : copie, déplacement, rien…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Comme je vous l'avais dit, il existe plusieurs types d'actions qui peuvent être e ectuées lors du drop, celles-ci sont paramétrables via l'objet TransferHandle :
[Link] : n'autorise que l'action lien sur les données du composant cible ; cela revient à créer un raccourci ;
Attention, l'objet TransferHandler n'accepte que les actions COPY lorsqu'il est instancié avec le paramètre « text » : si vous modifiez la valeur ici, votre drag'n
drop ne fonctionnera plus.
Alors, même si nous avons réussi à faire un JLabel avec l'option drag'n drop, celui-ci sera restreint ?
Non, mais si nous sommes parvenus à créer un nouveau TranferHandler , pour arriver à débrider notre composant, nous allons devoir encore approfondir…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
La classe TransferHandler
Nous y retrouvons nos types de transferts, la méthode exportAsDrag(…) et tout plein de nouveautés… C'est aussi dans cette classe que se trouvent les
méthodes pour la gestion du copier/coller traditionnel.
Le but est maintenant de déplacer les données du JLabel vers notre zone de texte façon « couper/coller ». Vous vous en doutez, nous allons devoir redéfinir le
comportement de certaines des méthodes de notre objet de transfert. Ne vous inquiétez pas, nous allons y aller en douceur. Voici la liste des méthodes que nous
allons utiliser pour arriver à faire ce que nous cherchons :
import [Link];
/**
* Méthode permettant à l'objet de savoir si les données reçues
* via un drop sont autorisées à être importées
* @param info
* @return boolean
*/
public boolean canImport([Link] info) {}
/**
* C'est ici que l'insertion des données dans notre composant est réalisée
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
* @param support
* @return boolean
*/
public boolean importData([Link] support){}
/**
* Cette méthode est invoquée à la fin de l'action DROP
* Si des actions sont à faire ensuite, c'est ici qu'il faudra coder le comportement désiré
* @param c
* @param t
* @param action
*/
protected void exportDone(JComponent c, Transferable t, int action){}
/**
* Dans cette méthode, nous allons créer l'objet utilisé par le système de drag'n drop
* afin de faire circuler les données entre les composants
* Vous pouvez voir qu'il s'agit d'un objet de type Transferable
* @param c
* @return
*/
protected Transferable createTransferable(JComponent c) {}
/**
* Cette méthode est utilisée afin de déterminer le comportement
* du composant vis-à-vis du drag'n drop : nous retrouverons
* nos variables statiques COPY, MOVE, COPY_OR_MOVE, LINK ou NONE
* @param c
* @return int
*/
public int getSourceActions(JComponent c) {}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Commençons par définir le comportement souhaité pour notre composant : le déplacement. Cela se fait via la méthode
public int getSourceActions(JComponent c) . Nous allons utiliser les variables statiques de la classe mère pour définir l'action autorisée :
Maintenant, assurons-nous qu'il sera toujours possible d'importer des données d'un autre composant en les déposant dessus. Pour cela, nous allons redéfinir les
méthodes d'import de données public boolean canImport([Link] info) et
public boolean importData([Link] support) . Remarquez ce paramètre bizarre : [Link] .
Rappelez-vous les classes internes : la classe TransferSupport est à l'intérieur de la classe TransferHandler . Cet objet a un rôle très important : la
communication entre les composants. C'est lui qui véhicule l'objet encapsulant nos données. C'est aussi lui, pour des composants plus complexes tels qu'un
tableau, un arbre ou une liste, qui fournit l'emplacement où a eu lieu l'action drop.
Voici ce que vont contenir nos méthodes :
L'objet TransferSupport nous o re une méthode permettant de contrôler le type de données supportées par notre drag'n drop. Une liste de « type MIME »
(signifie Multipurpose Internet Mail Extensions. C'est une façon de typer certains fichiers comme les images, les PDF, etc.) est disponible dans l'objet
DataFlavor . Ici, nous avons utilisé [Link] , qui signifie « chaîne de caractères », comme vous avez pu le deviner. Voici la liste des types
d'éléments disponibles via l'objet DataFlavor :
[Link] : autorise une image, soit la classe [Link] correspondant au type MIME « image/x-java-image » ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link] : autorise n'importe quel objet Java ;
[Link] : autorise soit une chaîne de caractères, soit la classe [Link] correspondant au type MIME « application/x-java-
serialized-object ».
La seconde étape de notre démarche consiste à autoriser l'import de données vers notre composant grâce à la méthode
public boolean importData([Link] support) :
//On récupère notre objet Transferable, celui qui contient les données en transit
Transferable data = [Link]();
String str = "";
try {
//Nous récupérons nos données en spécifiant ce que nous attendons
str = (String)[Link]([Link]);
} catch (UnsupportedFlavorException e){
[Link]();
} catch (IOException e) {
[Link]();
}
return true;
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Voilà : à ce stade, nous avons redéfini la copie du champ de texte vers notre JLabel . Voici notre objet en l'état :
public LabelContentDemo(){
setTitle("Drag'n Drop avec un JLabel !");
setSize(300, 100);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//--------------------------------------------------------
//On utilise notre nouvel objet MyTransferHandle
[Link](new MyTransferHandler());
[Link](new MouseAdapter(){
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
JLabel destLib = new JLabel("Destination de drag : ", [Link]);
JTextField dest = new JTextField();
[Link](true);
[Link](srcLib);
[Link](src);
[Link](destLib);
[Link](dest);
setContentPane(pan);
setVisible(true);
}
Et maintenant, le plus dur : e acer le contenu de notre objet une fois la copie des données e ectuée.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Transferable data = [Link]();
String str = "";
try {
str = (String)[Link]([Link]);
} catch (UnsupportedFlavorException e){
[Link]();
} catch (IOException e) {
[Link]();
}
return false;
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Vous pouvez tester à nouveau votre code, cette fois le rendu est conforme à nos attentes. Vous venez de recréer la fonction drag'n drop pour un composant. Bravo
!
Avant de poursuivre dans cette voie, rappelez-vous qu'il faut définir l'action que doit e ectuer notre composant lors du dépôt de nos données. C'est possible
grâce à l'objet DropMode que nous pouvons utiliser via la méthode setDropMode(DropMode dropMode) . Voici la liste des modes disponibles :
USE_SELECTION
ON
INSERT
ON_OR_INSERT
INSERT_COLS
INSERT_ROWS
ON_OR_INSERT_COLS
ON_OR_INSERT_ROWS
Vous l'aurez compris : certains modes sont utilisables par des tableaux et d'autres non… Afin que vous puissiez vous faire votre propre idée sur le sujet, je vous
invite à les essayer dans l'exemple qui va suivre. C'est grâce à cela que nous allons spécifier le mode de fonctionnement de notre arbre.
Maintenant que nous savons comment spécifier le mode de fonctionnement, il ne nous reste plus qu'à trouver comment, et surtout où insérer le nouvel élément.
C'est là que notre ami le TransfertSupport entre en jeu. Cet objet permet de récupérer un objet DropLocation contenant toutes les informations
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
nécessaires au bon positionnement des données dans le composant cible.
En fait, par l'objet TransfertSupport , vous pourrez déduire un objet DropLocation propre à votre composant, par exemple :
L'avantage de ces spécifications, c'est qu'elles permettent d'avoir accès à des informations fort utiles :
isInsertRow
isInsert getChildIndex isInsertColumn
getIndex getPath getRow
getColumn
Maintenant que je vous ai présenté la marche à suivre et les objets à utiliser, je vous propose un exemple qui, je pense, parle de lui-même et est assez commenté
pour que vous puissiez vous y retrouver. Voici les classes utilisées.
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
if(!canImport(support))
return false;
try {
str = (String)[Link]([Link]);
} catch (UnsupportedFlavorException e){
[Link]();
} catch (IOException e) {
[Link]();
}
return false;
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public int getSourceActions(JComponent c) {
return MOVE;
}
}
[Link]
JTree tree;
public TreeTransferHandler(JTree tree){
[Link] = tree;
}
return true;
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Transferable data = [Link]();
String str = "";
try {
str = (String)[Link]([Link]);
} catch (UnsupportedFlavorException e){
[Link]();
} catch (IOException e) {
[Link]();
}
[Link]([Link](nouveau));
[Link](path);
return true;
}
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//CTRL + SHIFT + O pour générer les imports
public class TreeDragDemo extends JFrame{
JTree tree;
public TreeDragDemo(){
setTitle("Drag'n Drop avec un JLabel !");
setSize(400, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//------------------------------------------------------
//On utilise notre nouvel objet MyTransferHandle
[Link](new MyTransferHandler());
[Link](new MouseAdapter(){
[Link](true);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
tree = new JTree(getModel());
[Link](true);
[Link](new TreeTransferHandler(tree));
[Link](src);
[Link](new JScrollPane(tree));
[Link](new ItemListener(){
if([Link]("USE_SELECTION"))
[Link](DropMode.USE_SELECTION);
if([Link]("ON"))
[Link]([Link]);
if([Link]("INSERT"))
[Link]([Link]);
if([Link]("ON_OR_INSERT"))
[Link](DropMode.ON_OR_INSERT);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
});
add(pan, [Link]);
add(combo, [Link]);
setVisible(true);
}
[Link](tuto);
[Link](forum);
La figure suivante vous montre ce que j'ai obtenu après quelques manipulations.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ajout de nœud via drag'n drop
E et de déplacement
À la lecture de tous ces chapitres, vous devriez être à même de comprendre et d'assimiler le fonctionnement du code qui suit. Son objectif est de simuler le
déplacement de vos composants sur votre IHM, un peu comme sur les trois figures suivantes.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Après avoir relâché le bouton sur un autre composant
En fait, le principe revient à définir un GlassPane à votre fenêtre, composant personnalisé que nous avons fait hériter de JPanel . C'est lui qui va se charger de
dessiner les images des composants sur sa surface, dont nous aurons au préalable défini la transparence. Sur chaque composant, nous allons devoir définir les
actions à e ectuer à chaque événement souris : deux classes sont codées à cet e et… Ensuite, il ne reste plus qu'à faire notre test.
Voilà les codes sources promis.
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private BufferedImage img;
//Les coordonnées de l'image
private Point location;
//La transparence de notre glace
private Composite transparence;
public MyGlassPane(){
//Afin de ne peindre que ce qui nous intéresse
setOpaque(false);
//On définit la transparence
transparence = [Link](AlphaComposite.SRC_OVER, 0.55f);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](true);
}
}
}
[Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//CTRL + SHIFT + O pour générer les imports
public class MouseGlassMotionListener extends MouseAdapter{
/**
* Méthode fonctionnant sur le même principe que la classe précédente
* mais cette fois sur l'action de déplacement
*/
public void mouseDragged(MouseEvent event) {
//Vous connaissez maintenant…
Component c = [Link]();
[Link]
public Fenetre(){
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
super("Test de GlassPane");
setSize(400, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](bouton1);
[Link](label);
add(pan, [Link]);
[Link](text);
[Link](bouton2);
add(pan2, [Link]);
setGlassPane(glass);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
Pour des composants comme les JTree , JTable ou autres, vous aurez certainement à faire des modifications pour que ça fonctionne !
Et voilà : j'espère que ça vous a plu ! Vous devriez désormais aborder le drag'n drop avec plus de sérénité. Il vous reste encore des choses à explorer, mais rien qui
devrait vous bloquer : vous n'êtes plus des Zéros !
Le drag'n drop n'est disponible via la méthode setDragEnabled(true); que pour certains composants.
Plusieurs comportements sont possibles pour les déplacements de données : la copie ou le déplacement.
Le drag'n drop permet de récupérer des données d'un composant source pour les transmettre à un composant cible, le tout via un objet : l'objet
TransferHandler .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous pouvez activer le drag'n drop sur un composant en utilisant la méthode setTransferHandler(TransferHandler newHandler) héritée de
JComponent .
La procédure de drag'n drop est réellement lancée lors de l'appel à la méthode [Link](lab, e, [Link]); , qui permet
de déterminer qui lance l'action, sur quel événement, ainsi que l'action qui doit être e ectuée.
Afin d'avoir le contrôle du mécanisme de drag'n drop, vous pouvez réaliser votre propre TransferHandler .
Ce dernier dispose d'une classe interne permettant de gérer la communication entre les composants (l'objet [Link] ) et
permet aussi de s'assurer que les données reçues sont bien du type attendu.
le redimensionnement ;
le changement de couleur ;
le changement de valeur ;
Vos applications graphiques seront plus performantes et plus sûres lorsque vous utiliserez ce thread pour e ectuer tous les changements qui pourraient
intervenir sur votre IHM.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Or depuis un certain temps, nous ne travaillons plus en mode console mais en mode graphique. Et, je vous le donne en mille, un troisième thread est lancé qui se
nomme l'EDT (Event Dispatch Thread). Comme je vous le disais, c'est dans celui-ci que tous les changements portant sur des composants sont exécutés. Voici un
petit schéma illustrant mes dires (figure suivante).
La philosophie de Java est que toute modification apportée à un composant se fait obligatoirement dans l'EDT : lorsque vous utilisez une méthode
actionPerformed , celle-ci, son contenu compris, est exécutée dans l'EDT (c'est aussi le cas pour les autres intercepteurs d'événements). La politique de Java
est simple : toute action modifiant l'état d'un composant graphique doit se faire dans un seul et unique thread, l'EDT. Vous vous demandez sûrement pourquoi.
C'est simple, les composants graphiques ne sont pas « thread-safe » : ils ne peuvent pas être utilisés par plusieurs threads simultanément et assurer un
fonctionnement sans erreurs ! Alors, pour s'assurer que les composants sont utilisés au bon endroit, on doit placer toutes les interactions dans l'EDT.
Par contre, cela signifie que si dans une méthode actionPerformed nous avons un traitement assez long, c'est toute notre interface graphique qui sera figée !
Vous vous souvenez de la première fois que nous avons tenté de contrôler notre animation ? Lorsque nous cliquions sur le bouton pour la lancer, notre interface
était bloquée étant donné que la méthode contenant une boucle infinie n'était pas dépilée du thread dans lequel elle était lancée. D'ailleurs, si vous vous
souvenez bien, le bouton s'a ichait comme si on n'avait pas relâché le clic ; c'était dû au fait que l'exécution de notre méthode se faisait dans l'EDT, bloquant ainsi
toutes les actions sur nos composants.
Imaginez la ligne comme une tête de lecture. Il y a déjà quelques événements à faire dans l'EDT :
la création de la fenêtre ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
la création et mise à jour de composants ;
Seulement voilà, nous cliquons sur un bouton engendrant un long, un très long traitement dans l'EDT (dernier bloc) : du coup, toute notre IHM est figée ! Non pas
parce que Java est lent, mais parce que nous avons exécuté un traitement au mauvais endroit. Il existe toutefois quelques méthodes thread-safe :
paint() et repaint() ;
Utiliser l'EDT
Java vous fournit la classe SwingUtilities qui o re plusieurs méthodes statiques permettant d'insérer du code dans l'EDT :
invokeLater(Runnable doRun) : exécute le thread en paramètre dans l'EDT et rend immédiatement la main au thread principal ;
invokeAndWait(Runnable doRun) : exécute le thread en paramètre dans l'EDT et attend la fin de celui-ci pour rendre la main au thread principal ;
isEventDispatchThread() : retourne vrai si le thread dans lequel se trouve l'instruction est dans l'EDT.
Maintenant que vous savez comment exécuter des instructions dans l'EDT, il nous faut un cas concret :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](JFrame.EXIT_ON_CLOSE);
[Link](null);
[Link](true);
updateBouton();
[Link]("Reprise du thread principal");
}
Au lancement de ce test, vous constatez que le thread principal ne reprend la main qu'après la fin de la méthode updateBouton() , comme le montre la figure
suivante.
La solution pour rendre la main au thread principal avant la fin de la méthode, vous la connaissez : créez un nouveau thread, mais cette fois vous allez également
exécuter la mise à jour du bouton dans l'EDT. Voilà donc ce que nous obtenons :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
static int count = 0;
static JButton bouton = new JButton("Pause");
public static void main(String[] args){
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
else{
[Link]("Lancement dans l' EDT");
[Link](t);
}
}
}
}).start();
}
}
Ce code est rudimentaire, mais il a l'avantage de vous montrer comment utiliser les méthodes présentées. Cependant, pour bien faire, j'aurais aussi dû inclure la
création de la fenêtre dans l'EDT, car tout ce qui touche aux composants graphiques doit être mis dans celui-ci.
Pour finir notre tour du sujet, il manque encore la méthode invokeAndWait() . Celle-ci fait la même chose que sa cousine, mais comme je vous le disais, elle
bloque le thread courant jusqu'à la fin de son exécution. De plus, elle peut lever deux exceptions : InterruptedException et InvocationTargetException .
Depuis la version 6 de Java, une classe est mise à disposition pour e ectuer des traitements lourds et interagir avec l'EDT.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
SwingWorker .
Vu que cette classe est abstraite, vous allez devoir redéfinir une méthode : doInBackground() . Elle permet de redéfinir ce que doit faire l'objet en tâche de fond.
Une fois cette tâche e ectuée, la méthode doInBackground() prend fin. Vous avez la possibilité de redéfinir la méthode done() , qui a pour rôle d'interagir
avec votre IHM tout en s'assurant que ce sera fait dans l'EDT. Implémenter la méthode done() est optionnel, vous n'êtes nullement tenus de le faire.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
return null;
}
Vous constatez que le traitement se fait bien en tâche de fond, et que votre composant est mis à jour dans l'EDT. La preuve à la figure suivante.
Je vous disais plus haut que vous pouviez interagir avec l'EDT pendant le traitement. Pour ce faire, il su it d'utiliser la méthode setProgress(int progress)
combinée avec l'événement PropertyChangeListener , qui sera informé du changement d'état de la propriété progress .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static void main(String[] args){
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
};
//On écoute le changement de valeur pour la propriété
[Link](new PropertyChangeListener(){
//Méthode de l'interface
public void propertyChange(PropertyChangeEvent event) {
//On vérifie tout de même le nom de la propriété
if("progress".equals([Link]())){
if([Link]())
[Link]("Dans le listener donc dans l'EDT ! ");
//On récupère sa nouvelle valeur
[Link]("Pause " + (Integer) [Link]());
}
}
});
//On lance le SwingWorker
[Link]();
}
}
Utilisation de setProgress(int i)
Les méthodes que vous avez vues jusqu'ici sont issues de la classe SwingWorker , qui implémente l'interface [Link] , o rant les
méthodes suivantes :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
isCancelled() : retourne vrai si l'action a été interrompue ;
Nous pouvons donc utiliser ces méthodes dans notre objet SwingWorker afin de récupérer le résultat d'un traitement. Pour le moment, nous n'avons pas utilisé
la généricité de cette classe. Or, comme l'indique le titre de cette section, SwingWorker peut prendre deux types génériques. Le premier correspond au type de
renvoi de la méthode doInBackground() et, par extension, au type de renvoi de la méthode get() . Le deuxième est utilisé comme type de retour
intermédiaire pendant l'exécution de la méthode doInBackground() .
Afin de gérer les résultats intermédiaires, vous pouvez utiliser les méthodes suivantes :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
protected Integer doInBackground() throws Exception {
int i;
for(i = 0; i < 5; i++){
try {
//On change la propriété d'état
setProgress(i);
//On publie un résultat intermédiaire
publish("Tour de boucle N° " + (i+1));
[Link](1000);
} catch (InterruptedException e) {
[Link]();
}
}
return i;
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
};
//On écoute le changement de valeur pour la propriété
[Link](new PropertyChangeListener(){
//Méthode de l'interface
public void propertyChange(PropertyChangeEvent event) {
//On vérifie tout de même le nom de la propriété
if("progress".equals([Link]())){
if([Link]())
[Link]("Dans le listener donc dans l'EDT ! ");
//On récupère sa nouvelle valeur
[Link]("Pause " + (Integer) [Link]());
}
}
});
//On lance le SwingWorker
[Link]();
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Voilà : vous savez maintenant comment utiliser l'EDT et les SwingWorker . Vos applications n'en seront que plus réactives !
Au lancement d'un programme Java, trois threads se lancent : le thread principal, celui gérant les tâches de fond et l'EDT.
Java préconise que toute modification des composants graphiques se fasse dans l'EDT.
Si vos IHM se figent, c'est peut-être parce que vous avez lancé un traitement long dans l'EDT.
Afin d'améliorer la réactivité de vos applications, vous devez choisir au mieux dans quel thread vous allez traiter vos données.
Java o re la classe SwingUtilities , qui permet de lancer des actions dans l'EDT depuis n'importe quel thread.
Depuis Java 6, la classe SwingWorker(<T, V>) vous o re la possibilité de lancer des traitements dans un thread en vous assurant que les mises à jour des
composants se feront dans l'EDT.
J'espère que vous avez appris tout plein de choses et que vous commencez à faire des choses sympa avec ce langage de programmation.
Il y a de quoi faire niveau IHM... Vous devez vous en rendre compte. ^^
Pour commencer, je pense qu'un petit rappel sur le fonctionnement des bases de données s'impose.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les bases de données (BDD) permettent de stocker des données. Mais concrètement, comment cela fonctionne-t-il ? En quelques mots, il s'agit d'un système de
fichiers contenant les données de votre application. Cependant, ces fichiers sont totalement transparents pour l'utilisateur d'une base de données, donc
totalement transparents pour vous ! La di érence avec les fichiers classiques se trouve dans le fait que ce n'est pas vous qui les gérez : c'est votre BDD qui les
organise, les range et, le cas échéant, vous retourne les informations qui y sont stockées. De plus, plusieurs utilisateurs peuvent accéder simultanément aux
données dont ils ont besoin, sans compter que de nos jours, les applications sont amenées à traiter une grande quantité de données, le tout en réseau. Imaginez-
vous gérer tout cela manuellement alors que les BDD le font automatiquement…
Les données sont ordonnées par « tables », c'est-à-dire par regroupements de plusieurs valeurs. C'est vous qui créerez vos propres tables, en spécifiant quelles
données vous souhaiterez y intégrer. Une base de données peut être vue comme une gigantesque armoire à tiroirs dont vous spécifiez les noms et qui
contiennent une multitude de fiches dont vous spécifiez aussi le contenu.
Je sais, un schéma est toujours le bienvenu, je vous invite donc à jeter un œil à la figure suivante.
Dans cette base de données, nous trouvons deux tables : une dont le rôle est de stocker des informations relatives à des personnes (noms, prénoms et âges) ainsi
qu'une autre qui s'occupe de stocker des pays, avec leur nom et leur capitale.
Si je reprends ma comparaison ci-dessus, la BDD symbolise l'armoire, chaque table représente un tiroir et chaque ligne de la table correspond à une fiche de ce
tiroir ! De plus, ce qui est formidable avec les BDD, c'est que vous pouvez les interroger en leur posant des questions via un langage précis. Vous pouvez interroger
votre base de données en lui donnant les instructions suivantes :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
« Donne-moi la fiche de la table Personne pour le nom HERBY » ;
etc.
Le langage permettant d'interroger des bases de données est le langage SQL (Structured Query Language ou, en français, « langage de requête structurée »). Grâce
aux BDD, vos données sont stockées, classées par vos soins et identifiables facilement sans avoir à gérer votre propre système de fichiers.
Pour utiliser une BDD, vous avez besoin de deux éléments : la base de données et ce qu'on appelle le SGBD (Système de Gestion de Base de Données).
Cette partie ne s'intéresse pas au langage SQL. Vous pouvez cependant trouver un cours sur le Site du Zéro qui traite de MySQL, un SGBDR (« R » signifie «
relationnelles ») qui utilise le langage SQL. Je vous invite à lire le chapitre sur l'insertion de données ainsi que le suivant sur la sélection de données.
PostgreSQL ;
MySQL ;
SQL Server ;
Oracle ;
Access.
Toutes ces bases de données permettent d'e ectuer les actions que je vous ai expliquées plus haut. Chacune possède des spécificités : certaines sont payantes
(Oracle), d'autres sont plutôt permissives avec les données qu'elles contiennent (MySQL), d'autres encore sont dotées d'un système de gestion très simple à
utiliser (MySQL), etc. C'est à vous de faire votre choix en regardant par exemple sur Internet ce qu'en disent les utilisateurs. Pour cette partie traitant des bases de
données, mon choix s'est porté sur PostgreSQL qui est gratuit et complet.
Installation de PostgreSQL
Téléchargez une version de PostgreSQL pour Windows, Linux ou Mac OS X. Je vous invite à décompresser l'archive téléchargée et à exécuter le fichier.
À partir de maintenant, si je ne mentionne pas une fenêtre de l'assistant d'installation particulière, vous pouvez laisser les réglages par défaut.
L'installation commence et il vous est demandé votre langue : choisissez et validez. Vous serez invités, par la suite, à saisir un mot de passe pour l'utilisateur,
comme à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Choix du mot de passe
Un mot de passe vous sera également demandé pour le « superadministrateur » (voir figure suivante).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Choix du mot de passe pour le superutilisateur
À la fin de la préinstallation, vous aurez le choix d'exécuter ou non le « Stack Builder » ; ce n'est pas nécessaire, il permet juste d'installer d'autres logiciels en
rapport avec PostgreSQL.
Le serveur est à présent installé : il doit en être de même pour le SGBD ! Pour vérifier que l'installation s'est bien déroulée, ouvrez le menu « Démarrer » et
rendez-vous dans Tous les programmes (sous Windows) : l'encart PostgreSQL 8.3 (le numéro de version peut être plus récent) doit ressembler à la figure
suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Menu « Démarrer » avec PostgreSQL
Dans ce dossier, deux exécutables permettent respectivement de lancer et d'arrêter le serveur. Le dernier exécutable, « pgAdmin III », correspond à notre SGBD :
lancez-le, nous allons configurer notre serveur. Dans le menu Fichier , choisissez Ajouter un serveur… , comme indiqué à la figure.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Configuration du serveur
Hôte : correspond à l'adresse du serveur sur le réseau ; ici, le serveur est situé sur votre ordinateur, écrivez donc « localhost ».
Vous n'avez normalement pas besoin de modifier le port ; dans le cas contraire, insérez la valeur qui figure sur l'image, à savoir 5432.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Votre première base de données
Nous reviendrons sur tout cela, mais vous pouvez observer que votre serveur, nommé « SDZ », possède une base de données appelée « postgres » ne contenant
aucune table. Nous allons maintenant apprendre à créer une base, des tables et surtout faire un bref rappel sur ce fameux langage SQL.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.
à chaque classe est attribué un professeur pour chacune des matières dispensées ;
un professeur peut enseigner plusieurs matières et exercer ses fonctions dans plusieurs classes.
Vous vous rendez compte qu'il y a beaucoup d'informations à gérer. En théorie, nous devrions établir un dictionnaire des données, vérifier à qui appartient quelle
donnée, poursuivre avec une modélisation à la façon MCD (Modèle Conceptuel de Données) et simplifier le tout selon certaines règles, pour terminer avec un MPD
(Modèle Physique de Données). Nous raccourcirons le processus : je vous fournis à la figure suivante un modèle tout prêt que je vous expliquerai tout de même.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Modèle de notre BDD
Tous ces éléments correspondent à nos futures tables ; les attributs qui s'y trouvent se nomment des « champs ». Tous les acteurs mentionnés figurent dans ce
schéma (classe, professeur, élève…). Vous constatez que chaque acteur possède un attribut nommé « id » correspondant à son identifiant : c'est un champ de
type entier qui s'incrémentera à chaque nouvelle entrée ; c'est également grâce à ce champ que nous pouvons créer des liens entre les acteurs.
Vous devez savoir que les flèches du schéma signifient « a un » ; de ce fait, un élève « a une » classe.
Certaines tables contiennent un champ se terminant par « _k ». Quelques-unes de ces tables possèdent deux champs de cette nature, pour une raison très
simple : parce que nous avons décidé qu'un professeur pouvait enseigner plusieurs matières, nous avons alors besoin de ce qu'on appelle une « table de
jointures ». Ainsi, nous pouvons spécifier que tel professeur enseigne telle ou telle matière et qu'une association professeur/matière est assignée à une classe. Ces
liens se feront par les identifiants ( id ).
De plus - il est di icile de ne pas avoir remarqué cela - chaque champ possède un type ( int , double , date , boolean …). Nous savons maintenant tout ce
qui est nécessaire pour construire notre BDD !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Pour cette opération, rien de plus simple : pgAdmin met à notre disposition un outil qui facilite la création de bases de données et de tables (exécuter tout cela à
la main avec SQL, c'est un peu fastidieux). Pour créer une nouvelle base de données, e ectuez un clic droit sur Bases de données , comme àa la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Créer les caractéristiques
Renseignez le nom de la base de données (« Ecole » dans notre cas) et choisissez l'encodage UTF-8. Cet encodage correspond à un jeu de caractères étendu qui
autorise les caractères spéciaux. Une fois cela fait, vous devriez obtenir quelque chose de similaire à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Première BDD
Vous pouvez désormais voir la nouvelle base de données ainsi que le script SQL permettant de la créer. Il ne nous reste plus qu'à créer les tables à l'aide du bon
type de données…
Commençons par la table classe , étant donné que c'est l'une des tables qui n'a aucun lien avec une autre. La procédure est la même que précédemment : il
vous su it d'e ectuer un clic droit sur Tables cette fois, comme le montre la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ajouter une table
son nom ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Nommer la table
Ajoutez ensuite les champs, comme le montrent les deux figures suivantes (j'ai ajouté des préfixes aux champs pour qu'il n'y ait pas d'ambiguïté dans les requêtes
SQL).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ajout d'une colonne à la table
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ajout de la colonne cls_id
Le champ cls_id est de type serial afin qu'il utilise une séquence (le champ s'incrémente ainsi automatiquement). Nous allons aussi lui ajouter une
contrainte de clé primaire.
Placez donc maintenant la contrainte de clé primaire sur votre identifiant, comme représenté à la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ajout d'une contrainte de clé primaire
Cliquez sur « Ajouter ». Choisissez la colonne cls_id et cliquez sur « Ajouter ». Validez ensuite le tout (figure suivante).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ajout d'une contrainte
Vous avez vu comment créer une table avec PostgreSQL, mais je ne vais pas vous demander de le faire pour chacune d'entre elles, je ne suis pas méchant à ce
point. Vous n'allez donc pas créer toutes les tables et tous les champs de cette manière, puisque cet ouvrage a pour but de vous apprendre à utiliser les BDD avec
Java, pas avec le SGBD… Je vous invite donc à télécharger une archive .zip contenant le script SQL de création des tables restantes ainsi que de leur contenu.
Une fois le dossier décompressé, il ne vous reste plus qu'à ouvrir le fichier avec PostgreSQL en vous rendant dans l'éditeur de requêtes SQL, comme indiqué à la
figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Vous pouvez à présent ouvrir le fichier que je vous ai fourni en cliquant sur Fichier > Ouvrir puis choisir le fichier .sql . Exécutez la requête en appuyant sur
F5 ou dirigez-vous vers le menu Requête et choisissez l'action Exécuter . Fermez l'éditeur de requêtes.
Votre base est maintenant entièrement créée, et en plus elle contient des données !
Ceux d'entre vous qui ont déjà installé une imprimante savent que leur machine a besoin d'un driver (appelé aussi pilote, c'est une sorte de mode d'emploi utilisé
par l'ordinateur) pour que la communication puisse s'e ectuer entre les deux acteurs. Ici, c'est la même chose : pgAdmin utilise un driver pour se connecter à la
base de données. Étant donné que les personnes qui ont développé les deux logiciels travaillent main dans la main, il n'y aura pas de problème de
communication ; mais qu'en sera-t-il pour Java ?
En fait, avec Java, vous aurez besoin de drivers, mais pas sous n'importe quelle forme : pour vous connecter à une base de données, il vous faut un fichier .jar
qui correspond au fameux pilote et qui contient tout ce dont vous aurez besoin pour vous connecter à une base PostgreSQL.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
MySQL ;
SQL Server ;
Oracle ;
d'autres bases.
Un bémol toutefois : vous pouvez aussi vous connecter à une BDD en utilisant les pilotes ODBC (Open DataBase Connectivity) présents dans Windows. Cela
nécessite cependant d'installer les pilotes dans Windows et de les paramétrer dans les sources de données ODBC pour, par la suite, utiliser ces pilotes ODBC afin
de se connecter à la BDD dans un programme Java. Je ne parlerai donc pas de cette méthode puisqu'elle ne fonctionne que pour Windows.
Pour trouver le driver JDBC qu'il vous faut, une rapide recherche à l'aide de votre moteur de recherche répondra à vos attentes, comme indiqué à la
figure suivante.
Sur la page de téléchargement des pilotes pour PostgreSQL, choisissez la dernière version disponible ; pour ma part, j'ai opté pour la version JDBC4. La version
JDBC4 o re des nouveautés et une souplesse d'utilisation accrue de JDBC, mais vous devez savoir qu'il existe trois autres types de drivers JDBC ; au total, il en
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
existe donc quatre :
des drivers JDBC de type 1 : JDBC-ODBC, ce type utilise l'interface ODBC pour se connecter à une base de données (on en a déjà parlé) ; au niveau de la
portabilité, on trouve mieux ;
des drivers JDBC de type 2 : ils intègrent les pilotes natifs et les pilotes Java ; en fait, la partie Java traduit les instructions en natif afin d'être comprises et
interprétées par les pilotes natifs ;
des drivers JDBC de type 3 : écrit entièrement en Java, ce type convertit les appels en un langage totalement indépendant du SGBD ; un serveur intégré
traduit ensuite les instructions dans le langage souhaité par le SGBD ;
des drivers JDBC de type 4 : des pilotes convertissant directement les appels JDBC en instructions compréhensibles par le SGBD ; ce type de drivers est codé
et proposé par les éditeurs de BDD.
Téléchargez donc le fichier .jar dans la rubrique Download du site dédié : [Link] . Nous nous pencherons bientôt sur son
utilisation, mais une question se pose encore : où placer l'archive ? Vous avez deux solutions :
Le tout est de savoir si votre application est vouée à être exportée sur di érents postes ; dans ce cas, l'approche CLASSPATH est la plus judicieuse (sinon, il faudra
ajouter l'archive dans tous les JRE…). En ce qui nous concerne, nous utiliserons la deuxième méthode afin de ne pas surcharger nos projets. Je vous laisse donc
placer l'archive téléchargée dans le dossier susmentionné.
Connexion
La base de données est prête, les tables sont créées, remplies et nous possédons le driver nécessaire ! Il ne nous reste plus qu'à nous connecter. Créons un
nouveau projet dans Eclipse avec une classe contenant une méthode public static void main(String[] args) . Voici le code source permettant la
connexion :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
String user = "postgres";
String passwd = "postgres";
} catch (Exception e) {
[Link]();
}
}
}
Dans un premier temps, nous avons créé une instance de l'objet Driver présent dans le fichier .jar que nous avons téléchargé. Il est inutile de créer une
véritable instance de ce type d'objet ; j'entends par là que l'instruction [Link] driver = new [Link]() n'est pas nécessaire.
Nous utilisons alors la réflexivité afin d'instancier cet objet.
À ce stade, il existe comme un pont entre votre programme Java et votre BDD, mais le trafic routier n'y est pas encore autorisé : il faut qu'une connexion soit
e ective afin que le programme et la base de données puissent communiquer. Cela se réalise grâce à cette ligne de code :
l'URL de connexion ;
le nom de l'utilisateur ;
L'URL de connexion est indispensable à Java pour se connecter à n'importe quelle BDD. La figure suivante illustre la manière dont se décompose cette URL.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
URL de connexion à une BDD via JDBC
Le premier bloc correspond au début de l'URL de connexion, qui commence toujours par jdbc: . Dans notre cas, nous utilisons PostgreSQL, la dénomination
postgresql: suit donc le début de l'URL. Si vous utilisez une source de données ODBC, il faut écrire jdbc:odbc: . En fait, cela dépend du pilote JDBC et
permet à Java de savoir quel pilote utiliser.
Dans le deuxième bloc se trouve la localisation de la machine physique sur le réseau ; ici, nous travaillons en local, nous utilisons donc //localhost:5432 . En
e et, le nom de la machine physique est suivi du numéro de port utilisé.
Enfin, dans le dernier bloc, pour ceux qui ne l'auraient pas deviné, il s'agit du nom de notre base de données.
Les informations des deux derniers blocs dépendent du pilote JDBC utilisé. Pour en savoir plus, consultez sa documentation.
Connexion e ective
Cette procédure lève une exception en cas de problème (mot de passe invalide…).
L'avantage d'utiliser les fichiers .jar comme drivers de connexion est que vous n'êtes pas tenus d'initialiser le driver par une méthode telle que la réflexivité,
tout se passe dans Java. Puisqu'un rappel du protocole à utiliser est présent dans l'URL de connexion, tout est optimal et Java s'en sort tout seul ! Ne vous
étonnez donc pas si vous ne voyez plus l'instruction [Link]("[Link]") par la suite.
JDBC permet à des programmes Java de communiquer avec des bases de données.
Une base de données est un système de fichiers stockant des informations regroupées dans des tables.
Il existe plusieurs types de drivers JDBC à utiliser selon la façon dont vous souhaitez vous connecter à la BDD.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Pour vous connecter à votre BDD, vous devez utiliser l'objet Connection fourni par l'objet DriverManager .
Celui-ci prend en paramètre une URL de connexion permettant d'identifier le type de base de données, l'adresse du serveur et le nom de la base à interroger,
en plus du nom d'utilisateur et du mot de passe de connexion.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Création d'un objet Statement
Statement state = [Link]();
//L'objet ResultSet contient le résultat de la requête SQL
ResultSet result = [Link]("SELECT * FROM classe");
//On récupère les MetaData
ResultSetMetaData resultMeta = [Link]();
[Link]("\n**********************************");
//On affiche le nom des colonnes
for(int i = 1; i <= [Link](); i++)
[Link]("\t" + [Link](i).toUpperCase() + "\t *");
[Link]("\n**********************************");
while([Link]()){
for(int i = 1; i <= [Link](); i++)
[Link]("\t" + [Link](i).toString() + "\t |");
[Link]("\n---------------------------------");
[Link]();
[Link]();
} catch (Exception e) {
[Link]();
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Recherche dans la table classe
Les metadatas (ou, plus communément, les métadonnées) constituent en réalité un ensemble de données servant à décrire une structure. Dans notre cas,
elles permettent de connaître le nom des tables, des champs, leur type…
J'ai simplement exécuté une requête SQL et récupéré les lignes retournées. Mais détaillons un peu plus ce qu'il s'est passé. Déjà, vous avez pu remarquer que j'ai
spécifié l'URL complète pour la connexion : sinon, comment savoir à quelle BDD se connecter ?
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ce dernier point mis à part, les choses se sont déroulées en quatre étapes distinctes :
fermeture des objets utilisés (bien que non obligatoire, c'est recommandé).
L'objet Statement permet d'exécuter des instructions SQL, il interroge la base de données et retourne les résultats. Ensuite, ces résultats sont stockés dans
l'objet ResultSet , grâce auquel on peut parcourir les lignes de résultats et les a icher.
Comme je vous l'ai mentionné, l'objet Statement permet d'exécuter des requêtes SQL. Ces dernières peuvent être de di érents types :
CREATE ;
INSERT ;
UPDATE ;
SELECT ;
DELETE .
L'objet Statement est fourni par l'objet Connection grâce à l'instruction [Link]() . Ce que j'ai fait, ensuite, c'est demander à mon objet
Statement d'exécuter une requête SQL de type SELECT : SELECT * FROM classe . Elle demande à la BDD de nous envoyer toutes les classes.
Puisque cette requête retourne un résultat contenant beaucoup de lignes, contenant elles-mêmes plusieurs colonnes, j'ai stocké ce résultat dans un objet
ResultSet , qui permet d'e ectuer diverses actions sur des résultats de requêtes SQL.
Ici, j'ai utilisé un objet de type ResultSetMetaData afin de récupérer les métadonnées de ma requête, c'est-à-dire ses informations globales. J'ai ensuite utilisé
cet objet afin de récupérer le nombre de colonnes renvoyé par la requête SQL ainsi que leur nom. Cet objet de métadonnées permet de récupérer des
informations très utiles, comme :
le nom de la table à laquelle appartient la colonne (dans le cas d'une jointure de tables) ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
etc.
Il existe aussi un objet DataBaseMetaData qui fournit des informations sur la base de données.
[Link]("\n**********************************");
//On affiche le nom des colonnes
for(int i = 1; i <= [Link](); i++)
[Link]("\t" + [Link](i).toUpperCase() + "\t *");
[Link]("\n**********************************");
Je me suis servi de la méthode retournant le nombre de colonnes dans le résultat afin de récupérer le nom de la colonne grâce à son index.
Attention, contrairement aux indices de tableaux, les indices de colonnes SQL commencent à 1 !
while([Link]()){
for(int i = 1; i <= [Link](); i++)
[Link]("\t" + [Link](i).toString() + "\t |");
[Link]("\n---------------------------------");
}
J'utilise une première boucle me permettant alors de parcourir chaque ligne via la boucle for tant que l'objet ResultSet retourne des lignes de résultats. La
méthode next() permet de positionner l'objet sur la ligne suivante de la liste de résultats. Au premier tour de boucle, cette méthode place l'objet sur la
première ligne. Si vous n'avez pas positionné l'objet ResultSet et que vous tentez de lire des données, une exception est levée !
Je suis parti du principe que le type de données de mes colonnes était inconnu, mais étant donné que je les connais, le code suivant aurait tout aussi bien
fonctionné :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
while([Link]()){
[Link]("\t" + [Link]("cls_id") + "\t |");
[Link]("\t" + [Link]("cls_nom") + "\t |");
[Link]("\n---------------------------------");
}
Je connais désormais le nom des colonnes retournées par la requête SQL. Je connais également leur type, il me su it donc d'invoquer la méthode adéquate de
l'objet ResultSet en utilisant le nom de la colonne à récupérer. En revanche, si vous essayez de récupérer le contenu de la colonne cls_nom avec la méthode
getInt("cls_nom") , vous aurez une exception !
Il existe une méthode getXXX() par type primitif ainsi que quelques autres correspondant aux types SQL :
getArray(int colummnIndex) ;
getAscii(int colummnIndex) ;
getBigDecimal(int colummnIndex) ;
getBinary(int colummnIndex) ;
getBlob(int colummnIndex) ;
getBoolean(int colummnIndex) ;
getBytes(int colummnIndex) ;
getCharacter(int colummnIndex) ;
getDate(int colummnIndex) ;
getDouble(int colummnIndex) ;
getFloat(int colummnIndex) ;
getInt(int colummnIndex) ;
getLong(int colummnIndex) ;
getObject(int colummnIndex) ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
getString(int colummnIndex) .
Pour finir, je n'ai plus qu'à fermer mes objets à l'aide des instructions [Link]() et [Link]() .
Avant de voir plus en détail les possibilités qu'o rent ces objets, nous allons créer deux ou trois requêtes SQL afin de nous habituer à la façon dont tout cela
fonctionne.
Entraînons-nous
Le but du jeu est de coder les résultats que j'ai obtenus. Voici, en figure suivante, ce que vous devez récupérer en premier. Je vous laisse chercher dans quelle
table nous allons travailler.
Entraînement à la recherche
Cherchez bien… Bon, vous avez sûrement trouvé, il n'y avait rien de compliqué. Voici une des corrections possibles :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//CTRL + SHIFT + O pour générer les imports
public class Exo1 {
while([Link]()){
[Link]("\t" + [Link]("prof_nom") + "\t |");
[Link]("\t" + [Link]("prof_prenom") + "\t |");
[Link]("\n---------------------------------");
}
[Link]();
[Link]();
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
} catch (Exception e) {
[Link]();
}
}
}
Autre recherche
Ne vous faites pas exploser la cervelle tout de suite, on ne fait que commencer ! Voici un code possible afin d'obtenir ce résultat :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
String url = "jdbc:postgresql://localhost:5432/Ecole";
String user = "postgres";
String passwd = "postgres";
while([Link]()){
if()){
nom = [Link]("prof_nom");
[Link](nom + " " + [Link]("prof_prenom") + " enseigne : ");
}
[Link]("\t\t\t - " + [Link]("mat_nom"));
}
[Link]();
[Link]();
} catch (Exception e) {
[Link]();
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Dernière ligne droite
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Statement state = [Link]();
while([Link]()){
if()){
nomClass = [Link]("cls_nom");
[Link]("Classe de " + nomClass + " :");
}
if()){
nom = [Link]("prof_nom");
[Link]("\t * " + nom + " " + [Link]("prof_prenom") + " enseigne : ");
}
[Link]("\t\t\t - " + [Link]("mat_nom"));
}
[Link]();
[Link]();
} catch (Exception e) {
[Link]();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Statement
Vous avez vu comment obtenir un objet Statement . Mais je ne vous ai pas tout dit… Vous savez déjà que pour récupérer un objet Statement , vous devez le
demander gentiment à un objet Connection en invoquant la méthode createStatement() . Ce que vous ne savez pas, c'est que vous pouvez spécifier des
paramètres pour la création de l'objet Statement . Ces paramètres permettent di érentes actions lors du parcours des résultats via l'objet ResultSet .
TYPE_FORWARD_ONLY : le résultat n'est consultable qu'en avançant dans les données renvoyées, il est donc impossible de revenir en arrière lors de la lecture
;
TYPE_SCROLL_SENSITIVE : le parcours peut se faire vers l'avant ou vers l'arrière et le curseur peut se positionner n'importe où, mais si des changements
surviennent dans la base pendant la lecture, il ne seront pas visibles ;
TYPE_SCROLL_INSENSITIVE : à la di érence du précédent, les changements sont directement visibles lors du parcours des résultats.
CONCUR_READONLY : les données sont consultables en lecture seule, c'est-à-dire que l'on ne peut modifier des valeurs pour mettre la base à jour ;
CONCUR_UPDATABLE : les données sont modifiables ; lors d'une modification, la base est mise à jour.
Par défaut, les ResultSet issus d'un Statement sont de type TYPE_FORWARD_ONLY pour le parcours et CONCUR_READONLY pour les actions
réalisables.
Ces paramètres sont des variables statiques de la classe ResultSet , vous savez donc comment les utiliser. Voici comment créer un Statement permettant à
l'objet ResultSet de pouvoir être lu d'avant en arrière avec possibilité de modification :
Vous avez appris à créer des Statement avec des paramètres, mais saviez-vous qu'il existait un autre type de Statement ?
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Les requêtes préparées
Il va falloir vous accrocher un tout petit peu… De tels objets sont créés exactement de la même façon que des Statement classiques, sauf qu'au lieu de cette
instruction :
Jusqu'ici, rien de spécial. Cependant, une di érence est déjà e ective à ce stade : la requête SQL est précompilée ! Cela a pour e et de réduire le temps
d'exécution dans le moteur SQL de la BDD. C'est normal, étant donné qu'il n'aura pas à compiler la requête. En règle générale, on utilise ce genre d'objet pour des
requêtes contenant beaucoup de paramètres ou pouvant être exécutées plusieurs fois. Il existe une autre di érence de taille entre les objets
PreparedStatement et Statement : dans le premier, on peut utiliser des paramètres à trous !
En fait, vous pouvez insérer un caractère spécial dans vos requêtes et remplacer ce caractère grâce à des méthodes de l'objet PreparedStatement en spécifiant
sa place et sa valeur (son type étant défini par la méthode utilisée).
Voici un exemple :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
String query = "SELECT prof_nom, prof_prenom FROM professeur";
//Premier trou pour le nom du professeur
query += " WHERE prof_nom = ?";
//Deuxième trou pour l'identifiant du professeur
query += " OR prof_id = ?";
[Link]();
[Link]();
} catch (Exception e) {
[Link]();
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Requête préparée
C'est simple : vous vous souvenez de la liste des méthodes de l'objet ResultSet récupérant des données ? Ici, elle est identique, sauf que l'on trouve setXXX()
à la place de getXXX() . Tout comme son homologue sans trou, cet objet peut prendre les mêmes types de paramètres pour la lecture et pour la modification
des données lues :
Sachez enfin qu'il existe aussi une méthode retournant un objet ResultSetMetaData : il s'agit de getMetaData() .
Pour en terminer avec les méthodes de l'objet PreparedStatement que je présente ici (il en existe d'autres), [Link]() permet de
réinitialiser la requête préparée afin de retirer toutes les valeurs renseignées. Si vous ajoutez cette méthode à la fin du code que je vous ai présenté et que vous
a ichez à nouveau le contenu de l'objet, vous obtenez la figure suivante.
ResultSet : le retour
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Maintenant que nous avons vu comment procéder, nous allons apprendre à nous promener dans nos objets ResultSet . En fait, l'objet ResultSet o re
beaucoup de méthodes permettant d'explorer les résultats, à condition que vous ayez bien préparé l'objet Statement .
vous positionner sur une ligne par rapport à votre emplacement actuel : [Link](-3) .
Je vous ai concocté un morceau de code que j'ai commenté et qui met tout cela en oeuvre.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Connection conn = [Link](url, user, passwd);
Statement state = [Link](ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
[Link]("\n\t---------------------------------------");
[Link]("\tLECTURE STANDARD.");
[Link]("\t---------------------------------------");
while([Link]()){
[Link]("\tNom : "+[Link]("prof_nom") +" \t prénom : "+[Link]("prof_prenom"));
//On regarde si on se trouve sur la dernière ligne du résultat
if([Link]())
[Link]("\t\t* DERNIER RESULTAT !\n");
i++;
}
//Une fois la lecture terminée, on contrôle si on se trouve bien à l'extérieur des lignes de résultat
if([Link]())
[Link]("\tNous venons de terminer !\n");
[Link]("\t---------------------------------------");
[Link]("\tLecture en sens contraire.");
[Link]("\t---------------------------------------");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
if([Link]())
[Link]("\t\t* RETOUR AU DEBUT !\n");
}
[Link]("\t---------------------------------------");
[Link]("\tAprès positionnement absolu du curseur à la place N° "+ i/2 + ".");
[Link]("\t---------------------------------------");
//On positionne le curseur sur la ligne i/2
//Peu importe où on se trouve
[Link](i/2);
while([Link]())
[Link]("\tNom : "+[Link]("prof_nom") +" \t prénom : "+ [Link]("prof_prenom"));
[Link]("\t---------------------------------------");
[Link]("\tAprès positionnement relatif du curseur à la place N° "+(i-(i-2)) + ".");
[Link]("\t---------------------------------------");
//On place le curseur à la ligne actuelle moins i-2
//Si on n'avait pas mis de signe moins, on aurait avancé de i-2 lignes
[Link](-(i-2));
while([Link]())
[Link]("\tNom : "+[Link]("prof_nom") +" \t prénom : "+[Link]("prof_prenom"));
[Link]();
[Link]();
} catch (Exception e) {
[Link]();
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Utilisation d'un ResultSet
Il est très important de noter l'endroit où vous vous situez dans le parcours de la requête !
Il existe des emplacements particuliers. Par exemple, si vous n'êtes pas encore positionnés sur le premier élément et que vous procédez à un
[Link](1) , vous vous retrouvez sur le premier élément. De même, un [Link](0) correspond à un [Link]() .
Ce qui signifie que lorsque vous souhaitez placer le curseur sur la première ligne, vous devez utiliser absolute(1) quel que soit l'endroit où vous vous trouvez !
En revanche, cela nécessite que le ResultSet soit de type TYPE_SCROLL_SENSITIVE ou TYPE_SCROLL_INSENSITIVE , sans quoi vous aurez une exception.
[Link]() ;
[Link]() ;
[Link]() ;
[Link]() ;
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
etc.
Ici, vous devez remplacer getXXX() par updateXXX() . Ces méthodes de mise à jour des données prennent deux paramètres :
C'est simple :
et ainsi de suite.
Changer la valeur d'un champ est donc très facile. Cependant, il faut, en plus de changer les valeurs, valider ces changements pour qu'ils soient e ectifs : cela se
fait par la méthode updateRow() . De la même manière, vous pouvez annuler des changements grâce à la méthode cancelRowUpdates() . Sachez que si vous
devez annuler des modifications, vous devez le faire avant la méthode de validation, sinon l'annulation sera ignorée.
Je vous propose d'étudier un exemple de mise à jour :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On va chercher une ligne dans la base de données
String query = "SELECT prof_id, prof_nom, prof_prenom FROM professeur " + "WHERE prof_nom = 'MAMOU'";
ResultSet res = [Link](query);
[Link]();
//Et voilà !
[Link]("*********************************");
[Link]("APRES REMODIFICATION : ");
[Link]("\tNOM : " + [Link]("prof_nom") + " - PRENOM : " + [Link]("prof_prenom") + "\n");
[Link]();
[Link]();
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
} catch (Exception e) {
[Link]();
}
}
}
En quelques instants, les données ont été modifiées dans la base de données, nous avons donc réussi à relever le défi !
Nous allons maintenant voir comment exécuter les autres types de requêtes avec Java.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
try {
[Link]("[Link]");
String url = "jdbc:postgresql://localhost:5432/Ecole";
String user = "postgres";
String passwd = "postgres";
res = [Link](query);
[Link]();
//On affiche à nouveau
[Link]("\n\t\t APRES MAJ : ");
[Link]("\t\t * NOM : " + [Link]("prof_nom") + " - PRENOM :" + [Link]("prof_prenom"));
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On effectue une mise à jour
[Link](1, "Daniel");
[Link]();
res = [Link](query);
[Link]();
//On affiche une nouvelle fois
[Link]("\n\t\t REMISE A ZERO : ");
[Link]("\t\t * NOM : " + [Link]("prof_nom") + " - PRENOM :" + [Link]("prof_prenom"));
[Link]();
[Link]();
[Link]();
} catch (ClassNotFoundException e) {
[Link]();
} catch (SQLException e) {
[Link]();
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Ici, nous avons utilisé un PreparedStatement pour compliquer immédiatement, mais nous aurions tout aussi bien pu utiliser un simple Statement et
invoquer la méthode executeUpdate(String query) .
Vous savez quoi ? Pour les autres types de requêtes, il su it d'invoquer la même méthode que pour la mise à jour. En fait, celle-ci retourne un booléen indiquant si
le traitement a réussi ou échoué. Voici quelques exemples :
Lorsque vous exécutez une requête de type INSERT , CREATE , UPDATE ou DELETE , le type de cette requête modifie les données présentes dans la base. Une
fois qu'elle est exécutée, le moteur SQL valide directement ces modifications !
Cependant, vous pouvez avoir la main sur ce point (figure suivante).
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Gestion manuelle des transactions
Comme cela, c'est vous qui avez le contrôle sur vos données afin de maîtriser l'intégrité de vos données. Imaginez que vous deviez exécuter deux requêtes, une
modification et une insertion, et que vous partiez du principe que l'insertion dépend de la mise à jour… Comment feriez-vous si de mauvaises données étaient
mises à jour ? L'insertion qui en découle serait mauvaise. Cela, bien sûr, si le moteur SQL valide automatiquement les requêtes exécutées.
Pour gérer manuellement les transactions, on spécifie au moteur SQL de ne pas valider automatiquement les requêtes SQL grâce à une méthode (qui ne
concernera toutefois pas l'objet Statement , mais l'objet Connection ) prenant un booléen en paramètre :
} catch (Exception e) {
[Link]();
}
}
}
Lorsque vous souhaitez que vos requêtes soient prises en compte, il vous faut les valider en utilisant la méthode [Link]() .
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
En mode setAutoCommit(false) , si vous ne validez pas vos requêtes, elles ne seront pas prises en compte.
Vous pouvez revenir à tout moment au mode de validation automatique grâce à setAutoCommit(true) .
Voici un exemple :
[Link](query);
[Link]();
[Link]();
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
} catch (Exception e) {
[Link]();
}
}
}
Vous pouvez exécuter ce code autant de fois que vous voulez, vous obtiendrez toujours la même chose que sur la figure suivante.
Transaction manuelle
Vous voyez que malgré sa présence, la requête de mise à jour est inopérante. Vous pouvez voir les modifications lors de l'exécution du script, mais étant donné
que vous ne les avez pas validées, elles sont annulées à la fin du code. Pour que la mise à jour soit e ective, il faudrait e ectuer un [Link]() avant la fin
du script.
Il existe des objets ResultSetMetaData et DataBaseMetaData donnant accès à des informations globales sur une requête (le premier) ou une base de
données (pour le second).
Il existe un autre objet qui fonctionne de la même manière que l'objet ResultSet , mais qui précompile la requête et permet d'utiliser un système de
requête à trous : l'objet PreparedStatement .
Avec un ResultSet autorisant l'édition des lignes, vous pouvez invoquer la méthode updateXXX() suivie de la méthode updateRow() .
Pour la mise à jour, la création ou la suppression de données, vous pouvez utiliser la méthode executeUpdate(String query) .
En utilisant les transactions manuelles, toute instruction non validée par la méthode commit() de l'objet Connection est annulée.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Limiter le nombre de connexions
Vous savez désormais comment vous connecter à une BDD depuis Java. Je vous ai montré comment lire et modifier des données. Après vous avoir fait
découvrir tout cela, je me suis dit que montrer une approche un peu plus objet ne serait pas du luxe. C'est vrai, établir sans arrêt la connexion à notre base
de données commence à être fastidieux. Je vous propose donc d'y remédier avec ce chapitre en découvrant le pattern singleton.
Pourquoi veux-tu absolument qu'on ait une seule instance de notre objet Connection ?
Parce que cela ne sert pas à grand-chose de réinitialiser la connexion à votre BDD. Rappelez-vous que la connexion sert à établir le pont entre votre base et votre
application. Pourquoi voulez-vous que votre application se connecte à chaque fois à la BDD ? Une fois la connexion e ective, pourquoi vouloir l'établir de nouveau
? Votre application et votre BDD peuvent discuter !
Bon, c'est vrai qu'avec du recul, cela paraît superflu… Du coup, comment fais-tu pour garantir qu'une seule instance de Connection existe dans
l'application ?
C'est ici que le pattern singleton intervient ! Ce pattern est peut-être l'un des plus simples à comprendre même, s'il contient un point qui va vous faire bondir : le
principe de base est d'interdire l'instanciation d'une classe, grâce à un constructeur déclaré private .
Le pattern singleton
Nous voulons qu'il soit impossible de créer plus d'un objet de connexion. Voici une classe qui permet de s'assurer que c'est le cas :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Nom du user
private String user = "postgres";
//Mot de passe de l'utilisateur
private String passwd = "postgres";
//Objet Connection
private static Connection connect;
//Constructeur privé
private SdzConnection(){
try {
connect = [Link](url, user, passwd);
} catch (SQLException e) {
[Link]();
}
}
//Méthode qui va nous retourner notre instance et la créer si elle n'existe pas
public static Connection getInstance(){
if(connect == null){
new SdzConnection();
}
return connect;
}
}
Nous avons ici une classe avec un constructeur privé : du coup, impossible d'instancier cet objet et d'accéder à ses attributs, puisqu'ils sont déclarés private !
Notre objet Connection est instancié dans le constructeur privé et la seule méthode accessible depuis l'extérieur de la classe est getInstance() . C'est donc
cette méthode qui a pour rôle de créer la connexion si elle n'existe pas, et seulement dans ce cas.
Pour en être bien sûrs, nous allons faire un petit test… Voici le code un peu modifié de la méthode getInstance() :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("INSTANCIATION DE LA CONNEXION SQL ! ");
}
else{
[Link]("CONNEXION SQL EXISTANTE ! ");
}
return connect;
}
Cela nous montre quand la connexion est réellement créée. Ensuite, il ne nous manque plus qu'un code de test. Oh ! Ben ça alors ! J'en ai un sous la main :
[Link]().setAutoCommit(false);
} catch (SQLException e) {
[Link]();
}
}
}
La méthode en question est appelée quatre fois. Que croyez-vous que ce code va a icher ? Quelque chose comme la figure suivante !
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Utilisation d'un singleton
Vous avez la preuve que l'instanciation ne se fait qu'une seule fois et donc que notre connexion à la BDD est unique ! La classe SdzConnection peut être un peu
simplifiée :
Attention toutefois, vous devrez rajouter la déclaration static à vos paramètres de connexion.
Vous pouvez relancer le code de test, vous verrez qu'il fonctionne toujours ! J'avais commencé par insérer un constructeur privé car vous deviez savoir que cela
existait, mais remarquez que c'était superflu dans notre cas…
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Par contre, dans une application multithreads, pour être sûrs d'éviter les conflits, il vous su it de synchroniser la méthode getInstance() et le tour est joué.
Mais - parce qu'il y a un mais - cette méthode ne règle le problème qu'avant l'instanciation de la connexion. Autrement dit, une fois la connexion instanciée, la
synchronisation ne sert plus à rien.
Le problème du multithreading ne se pose pas vraiment pour une connexion à une BDD puisque ce singleton sert surtout de passerelle entre votre BDD et votre
application. Cependant, il peut exister d'autres objets que des connexions SQL qui ne doivent être instanciés qu'une fois ; tous ne sont pas aussi laxistes
concernant le multithreading.
Voyons donc comment parfaire ce pattern avec un exemple autre qu'une connexion SQL.
//Constructeur privé
private SdzSingleton(){
[Link] = "Mon singleton";
[Link]("\t\tCRÉATION DE L'INSTANCE ! ! !");
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
return single;
}
//Accesseur
public String getName(){
return [Link];
}
}
Ce n'est pas que je manquais d'inspiration, c'est juste qu'avec une classe toute simple, on comprend mieux les choses… Et voici notre classe de test :
Un petit singleton
La politique du singleton est toujours bonne. Mais je vais vous poser une question : quand croyez-vous que la création d'une instance soit la plus judicieuse ? Ici,
nous avons exécuté notre code et l'instance a été créée lorsqu'on l'a demandée pour la première fois ! C'est le principal problème que posent le singleton et le
multithreading : la première instance… Une fois celle-ci créée, les problèmes se font plus rares.
Pour limiter les ennuis, nous allons donc laisser cette lourde tâche à la JVM, dès le chargement de la classe, en instanciant notre singleton lors de sa déclaration :
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public class SdzSingleton {
//Le singleton
private static SdzSingleton single = new SdzSingleton();
//Variable d'instance
private String name = "";
//Constructeur privé
private SdzSingleton(){
[Link] = "Mon singleton";
[Link]("\t\tCRÉATION DE L'INSTANCE ! ! !");
}
return single;
}
//Accesseur
public String getName(){
return [Link];
}
}
Avec ce code, c'est la machine virtuelle qui s'occupe de charger l'instance du singleton, bien avant que n'importe quel thread vienne taquiner la méthode
getInstance() …
Il existe une autre méthode permettant de faire cela, mais elle ne fonctionne parfaitement que depuis le JDK 1.5. On appelle cette méthode « le verrouillage à
double vérification ». Elle consiste à utiliser le mot clé volatile combiné au mot clé synchronized .
Pour les lecteurs qui l'ignorent, déclarer une variable volatile permet d'assurer un accès ordonné des threads à une variable (plusieurs threads peuvent
accéder à cette variable), marquant ainsi le premier point de verrouillage. Ensuite, la double vérification s'e ectue dans la méthode getInstance() : on
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
e ectue la synchronisation uniquement lorsque le singleton n'est pas créé.
private SdzSingleton(){
[Link] = "Mon singleton";
[Link]("\n\t\tCRÉATION DE L'INSTANCE ! ! !");
}
Pour économiser les ressources, vous ne devriez créer qu'un seul objet de connexion.
Ce pattern repose sur un constructeur privé associé à une méthode retournant l'instance créée dans la classe elle-même.
Afin de pallier au problème du multithreading, il vous su it d'utiliser le mot clé synchronized dans la déclaration de votre méthode de récupération de
l'instance, mais cette synchronisation n'est utile qu'une fois. À la place, vous pouvez instancier l'objet au chargement de la classe par la JVM, avant tout appel
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
à celle-ci.
TP : un testeur de requêtes
Vous avez appris un tas de choses sur JDBC et il est grand temps que vous les mettiez en pratique !
Dans ce TP, je vais vous demander de réaliser un testeur de requêtes SQL. Vous ne voyez pas où je veux en venir ? Lisez donc la suite…
créer une IHM permettant la saisie d'une requête SQL dans un champ ;
lancer l'exécution de la requête grâce à un bouton se trouvant dans une barre d'outils ;
un petit message en bas de fenêtre a ichera le temps d'exécution de la requête ainsi que le nombre de lignes retournées.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Au lancement
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Exécution d'une requête
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Erreur dans une requête
Je n'ai plus qu'à vous souhaiter bonne chance et bon courage ! Let's go !
Correction
Classe [Link]
package [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
/**
* URL de connection
*/
private static String url = "jdbc:postgresql://localhost:5432/Ecole";
/**
* Nom du user
*/
private static String user = "postgres";
/**
* Mot de passe du user
*/
private static String passwd = "postgres";
/**
* Objet Connection
*/
private static Connection connect;
/**
* Méthode qui va retourner notre instance
* et la créer si elle n'existe pas...
* @return
*/
public static Connection getInstance(){
if(connect == null){
try {
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
connect = [Link](url, user, passwd);
} catch (SQLException e) {
[Link](null, [Link](), "ERREUR DE CONNEXION ! ", JOptionPane.ERROR_MESSAGE);
}
}
return connect;
}
}
Classe [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
import [Link];
/**
* ToolBar pour le lancement des requêtes
*/
private JToolBar tool = new JToolBar();
/**
* Le bouton
*/
private JButton load = new JButton(new ImageIcon("img/[Link]"));
/**
* Le délimiteur
*/
private JSplitPane split;
/**
* Le conteneur de résultat
*/
private JPanel result = new JPanel();
/**
* Requête par défaut pour le démarrage
*/
private String requete = "SELECT * FROM classe";
/**
* Le composant dans lequel taper la requête
*/
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
private JTextArea text = new JTextArea(requete);
/**
* Constructeur
*/
public Fenetre(){
setSize(900, 600);
setTitle("TP JDBC");
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
initToolbar();
initContent();
initTable(requete);
}
/**
* Initialise la toolbar
*/
private void initToolbar(){
[Link](new Dimension(30, 35));
[Link](null);
[Link](new ActionListener(){
public void actionPerformed(ActionEvent event){
initTable([Link]());
}
});
[Link](load);
getContentPane().add(tool, [Link]);
}
/**
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
* Initialise le contenu de la fenêtre
*/
public void initContent(){
//Vous connaissez ça...
[Link](new BorderLayout());
split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, new JScrollPane(text), result);
[Link](100);
getContentPane().add(split, [Link]);
}
/**
* Initialise le visuel avec la requête saisie dans l'éditeur
* @param query
*/
public void initTable(String query){
try {
//On crée un statement
long start = [Link]();
Statement state = [Link]().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ON
LY);
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Petite manipulation pour obtenir le nombre de lignes
[Link]();
int rowCount = [Link]();
Object[][] data = new Object[[Link]()][[Link]()];
j++;
}
} catch (SQLException e) {
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Dans le cas d'une exception, on affiche une pop-up et on efface le contenu
[Link]();
[Link](new JScrollPane(new JTable()), [Link]);
[Link]();
[Link](null, [Link](), "ERREUR ! ", JOptionPane.ERROR_MESSAGE);
}
}
/**
* Point de départ du programme
* @param args
*/
public static void main(String[] args){
Fenetre fen = new Fenetre();
[Link](true);
}
}
Bien sûr, ce code n'est pas parfait, vous pouvez l'améliorer ! Voilà d'ailleurs quelques pistes :
vous pouvez utiliser un autre composant que moi pour la saisie de la requête, par exemple un JTextPane pour la coloration syntaxique ;
vous pouvez également créer un menu qui vous permettra de sauvegarder vos requêtes ;
vous pouvez également créer un tableau interactif autorisant la modification des données.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Avec le pattern DAO (Data Access Object), vous allez voir comment procéder et surtout, comment rendre le tout stable !
Eleve ;
Matiere ;
Professeur ;
Classe .
Et, si nous suivons la logique des relations entre nos tables, nos classes sont liées suivant le diagramme de classes correspondant à la figure suivante.
Grâce à ce diagramme, nous voyons les liens entre les objets : une classe est composée de plusieurs élèves et de plusieurs professeurs, et un professeur peut
exercer plusieurs matières. Les tables de jointures de la base sont symbolisées par la composition dans nos objets.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Une fois que cela est fait, nous devons coder ces objets avec les accesseurs et les mutateurs adéquats :
On appelle ce genre d'objet des « POJO », pour Plain Old Java Object ! Ce qui nous donne ces codes source :
Classe [Link]
package [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
return nom;
}
Classe [Link]
package [Link];
public Matiere(){}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public int getId() {
return id;
}
Classe [Link]
package [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public Professeur(int id, String nom, String prenom) {
[Link] = id;
[Link] = nom;
[Link] = prenom;
}
public Professeur(){}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public Set<Matiere> getListMatiere() {
return listMatiere;
}
Classe [Link]
package [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public Classe(int id, String nom) {
[Link] = id;
[Link] = nom;
}
public Classe(){}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link](prof);
}
Nous avons des objets prêts à l'emploi. Maintenant, comment faire en sorte que ces objets puissent recevoir les données de notre base ? Au lieu de faire des essais
à tâtons, nous allons définir le pattern DAO et voir comment il fonctionne avant de l'implémenter.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.
Le pattern DAO
Contexte
Vous disposez de données sérialisées dans une base de données et vous souhaitez les manipuler avec des objets Java. Cependant, votre entreprise est en pleine
restructuration et vous ne savez pas si vos données vont :
Comment faire en sorte de ne pas avoir à modifier toutes les utilisations de nos objets ? Comment réaliser un système qui pourrait s'adapter aux futures
modifications de supports de données ? Comment procéder afin que les objets que nous allons utiliser restent tels qu'ils sont ?
Le pattern DAO
Ce pattern permet de faire le lien entre la couche d'accès aux données et la couche métier d'une application (vos classes). Il permet de mieux maîtriser les
changements susceptibles d'être opérés sur le système de stockage des données ; donc, par extension, de préparer une migration d'un système à un autre (BDD
vers fichiers XML, par exemple…). Ceci se fait en séparant accès aux données (BDD) et objets métiers (POJO).
Je me doute que tout ceci doit vous sembler très flou. C'est normal, mais ne vous en faites pas, je vais tout vous expliquer… Déjà, il y a cette histoire de séparation
des couches métier et des couches d'accès aux données. Il s'agit ni plus ni moins de faire en sorte qu'un type d'objet se charge de récupérer les données dans la
base et qu'un autre type d'objet (souvent des POJO) soit utilisé pour manipuler ces données. Schématiquement, ça nous donne la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Fonctionnement du pattern DAO
Les objets que nous avons créés plus haut sont nos POJO, les objets utilisés par le programme pour manipuler les données de la base. Les objets qui iront
chercher les données en base devront être capables d'e ectuer des recherches, des insertions, des mises à jour et des suppressions ! Par conséquent, nous
pouvons définir un super type d'objet afin d'utiliser au mieux le polymorphisme… Nous allons devoir créer une classe abstraite (ou une interface) mettant en
oeuvre toutes les méthodes sus-mentionnées.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Comment faire pour demander à nos objets DAO de récupérer tel type d'objet ou de sérialiser tel autre ? Avec des cast ?
Soit avec des cast, soit en créant une classe générique (figure suivante) !
Classe DAO
Afin de ne pas surcharger les codes d'exemples, j'ai volontairement utilisé des objets Statement mais il aurait mieux valu utiliser des requêtes préparées
( PreparedStatement ) pour des questions de performances et de sécurité.
Classe [Link]
package [Link];
import [Link];
import [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public DAO(Connection conn){
[Link] = conn;
}
/**
* Méthode de création
* @param obj
* @return boolean
*/
public abstract boolean create(T obj);
/**
* Méthode pour effacer
* @param obj
* @return boolean
*/
public abstract boolean delete(T obj);
/**
* Méthode de mise à jour
* @param obj
* @return boolean
*/
public abstract boolean update(T obj);
/**
* Méthode de recherche des informations
* @param id
* @return T
*/
public abstract T find(int id);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Classe [Link]
package [Link];
//CTRL + SHIFT + O pour générer les imports
public class EleveDAO extends DAO<Eleve> {
public EleveDAO(Connection conn) {
super(conn);
}
try {
ResultSet result = [Link](
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY).executeQuery("SELECT * FROM eleve WHERE elv_id = " + id);
if([Link]())
eleve = new Eleve(
id,
[Link]("elv_nom"),
[Link]("elv_prenom"
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
));
} catch (SQLException e) {
[Link]();
}
return eleve;
}
}
Classe [Link]
package [Link];
//CTRL + SHIFT + O pour générer les imports
public class MatiereDAO extends DAO<Matiere> {
public MatiereDAO(Connection conn) {
super(conn);
}
try {
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
ResultSet result = [Link](
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY
).executeQuery("SELECT * FROM matiere WHERE mat_id = " + id);
if([Link]())
matiere = new Matiere(id, [Link]("mat_nom"));
} catch (SQLException e) {
[Link]();
}
return matiere;
}
}
Classe [Link]
package [Link];
//CTRL + SHIFT + O pour générer les imports
public class ProfesseurDAO extends DAO<Professeur> {
public ProfesseurDAO(Connection conn) {
super(conn);
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public Professeur find(int id) {
Professeur professeur = new Professeur();
try {
ResultSet result = [Link](
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY
).executeQuery(
"SELECT * FROM professeur "+
"LEFT JOIN j_mat_prof ON jmp_prof_k = prof_id " +
"AND prof_id = "+ id +
" INNER JOIN matiere ON jmp_mat_k = mat_id"
);
if([Link]()){
professeur = new Professeur(id, [Link]("prof_nom"), [Link]("prof_prenom"));
[Link]();
MatiereDAO matDao = new MatiereDAO([Link]);
while([Link]())
[Link]([Link]([Link]("mat_id")));
}
} catch (SQLException e) {
[Link]();
}
return professeur;
}
Classe [Link]
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
package [Link];
//CTRL + SHIFT + O pour générer les imports
public class ClasseDAO extends DAO<Classe> {
public ClasseDAO(Connection conn) {
super(conn);
}
if([Link]()){
classe = new Classe(id, [Link]("cls_nom"));
result = [Link]().executeQuery(
"SELECT prof_id, prof_nom, prof_prenom from professeur " +
"INNER JOIN j_mat_prof ON prof_id = jmp_prof_k " +
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
"INNER JOIN j_cls_jmp ON jmp_id = jcm_jmp_k AND jcm_cls_k = " + id
);
while([Link]())
[Link]([Link]([Link]("prof_id")));
while([Link]())
[Link]([Link]([Link]("elv_id")));
}
} catch (SQLException e) {
[Link]();
}
return classe;
}
}
Pour ne pas compliquer la tâche, je n'ai détaillé que la méthode de recherche des données, les autres sont des coquilles vides. Mais vous devriez être
capables de faire ça tout seuls.
Premier test
Nous avons réalisé une bonne partie de ce pattern, nous allons pouvoir faire notre premier test.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Voici un code de test :
import [Link];
//CTRL + SHIFT + O pour générer les imports
public class FirstTest {
public static void main(String[] args) {
//Testons des élèves
DAO<Eleve> eleveDao = new EleveDAO([Link]());
for(int i = 1; i < 5; i++){
Eleve eleve = [Link](i);
[Link]("Elève N°" + [Link]() + " - " + [Link]() + " " + [Link]());
}
[Link]("\n********************************\n");
[Link]("\n********************************\n");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
[Link]("\nListe des professeurs :");
for(Professeur prof : [Link]())
[Link](" - " + [Link]() + " " + [Link]());
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Test de notre DAO
Vous avez compris comment tout ça fonctionnait ? Je vous laisse quelques instants pour lire, tester, relire, tester à nouveau… Nous utilisons des objets
spécifiques afin de rechercher dans la base des données. Nous nous en servons pour instancier des objets Java habituels.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Le pattern factory
Nous allons aborder ici une notion importante : la fabrication d'objets ! En e et, le pattern DAO implémente aussi ce qu'on appelle le pattern factory. Celui-ci
consiste à déléguer l'instanciation d'objets à une classe.
En fait, une fabrique ne fait que ça. En général, lorsque vous voyez ce genre de code dans une classe :
class A{
public Object getData(int type){
Object obj;
//----------------------
if(type == 0)
obj = new B();
else if(type == 1)
obj = new C();
else
obj = new D();
//----------------------
[Link]();
[Link]();
}
}
… vous constatez que la création d'objets est conditionnée par une variable et que, selon cette dernière, l'objet instancié n'est pas le même. Nous allons donc
extraire ce code pour le mettre dans une classe à part :
package [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
return new C();
else
return new D();
}
}
Du coup, lorsque nous voudrons instancier les objets de la fabrique, nous l'utiliserons à présent comme ceci :
B b = [Link](0);
C c = [Link](1);
//…
Pourquoi faire tout ça ? En temps normal, nous travaillons avec des objets concrets, non soumis au changement. Cependant, dans le cas qui nous intéresse, nos
objets peuvent être amenés à changer. Et j'irai même plus loin : le type d'objet utilisé peut changer !
L'avantage d'utiliser une fabrique, c'est que les instances concrètes (utilisation du mot clé new ) se font à un seul endroit ! Donc, si nous devons faire des
changements, il ne se feront qu'à un seul endroit. Si nous ajoutons un paramètre dans le constructeur, par exemple…
Je vous propose maintenant de voir comment ce pattern est implémenté dans le pattern DAO.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Diagramme de classe de notre factory
package [Link];
//CTRL + SHIFT + O pour générer les imports
public class DAOFactory {
protected static final Connection conn = [Link]();
/**
* Retourne un objet Classe interagissant avec la BDD
* @return DAO
*/
public static DAO getClasseDAO(){
return new ClasseDAO(conn);
}
/**
* Retourne un objet Professeur interagissant avec la BDD
* @return DAO
*/
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public static DAO getProfesseurDAO(){
return new ProfesseurDAO(conn);
}
/**
* Retourne un objet Eleve interagissant avec la BDD
* @return DAO
*/
public static DAO getEleveDAO(){
return new EleveDAO(conn);
}
/**
* Retourne un objet Matiere interagissant avec la BDD
* @return DAO
*/
public static DAO getMatiereDAO(){
return new MatiereDAO(conn);
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
[Link]("\n\t****************************************");
[Link]("\n\t****************************************");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
}
}
Vous pouvez être fiers de vous ! Vous venez d'implémenter le pattern DAO utilisant une fabrique. C'était un peu e rayant, mais, au final, ce n'est rien du tout.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
On a bien compris le principe du pattern DAO, ainsi que la combinaison DAO - factory. Cependant, on ne voit pas comment gérer plusieurs systèmes de
sauvegarde de données. Faut-il modifier les DAO à chaque fois ?
Non, bien sûr… Chaque type de gestion de données (PostgreSQL, XML, MySQL…) peut disposer de son propre type de DAO. Le vrai problème, c'est de savoir
comment récupérer les DAO, puisque nous avons délégué leurs instanciations à une fabrique. Vous allez voir : les choses les plus compliquées peuvent être aussi
les plus simples.
De l'usine à la multinationale
Résumons de quoi nous disposons :
une classe permettant d'instancier les objets d'accès aux données (la Factory).
Le fait est que notre structure actuelle fonctionne pour notre système actuel. Ah ! Mais ! Qu'entends-je, qu'ouïs-je ? Votre patron vient de trancher ! Vous allez
utiliser PostgreSQL et du XML !
C'est bien ce qu'on disait plus haut… Comment gérer ça ? On ne va pas mettre des if(){…}else{…} dans la fabrique, tout de même ?
Vous voulez insérer des conditions afin de savoir quel type d'instance retourner : ça ressemble grandement à une portion de code pouvant être déclinée en
fabrique !
Oui ! Notre fabrique actuelle nous permet de construire des objets accédant à des données se trouvant dans une base de données PostgreSQL. Mais maintenant,
le défi consiste à utiliser aussi des données provenant de fichiers XML.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Notre situation actuelle
Je pense que vous êtes tous d'accord pour dire que ces deux usines ont un processus de fabrication très similaire. Par là, j'entends que nous allons utiliser les
mêmes méthodes sur les objets sortant de ces deux usines. Voyez ça un peu comme une grande marque de pain qui aurait beaucoup de boulangeries dans tous
les pays du monde ! Cette firme a un savoir-faire évident, mais aussi des particularités : le pain ne se fait pas à l'identique dans tous les endroits du globe.
Pour vous, c'est comme si vous passiez commande directement au siège social, qui va charger l'usine spécialisée de produire ce qui répondra à vos attentes.
Schématiquement, ça donne la figure suivante.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Une factory de factory
Lorsque je vous dis ça, vous devez avoir une réaction quasi immédiate : « héritage - polymorphisme » ! Ce qui va changer le plus, par rapport à notre ancienne
fabrique, c'est que nous n'utiliserons plus de méthodes statiques, mais des méthodes d'une instance concrète, et pour cause : il est impossible de créer une classe
abstraite ou une interface avec des méthodes statiques destinée à la redéfinition !
Nous allons donc créer une classe abstraite pour nos futures fabriques. Elle devra avoir les méthodes permettant de récupérer les di érents DAO et une méthode
permettant d'instancier la bonne fabrique ! Je vous ai préparé un diagramme de classe à la figure suivante, vous comprendrez mieux.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Diagramme de classe de nos factory
Classe [Link]
package [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//Retourne un objet Classe interagissant avec la BDD
public abstract DAO getClasseDAO();
Classe [Link]
package [Link];
//CTRL + SHIFT + O pour générer les imports
public class DAOFactory extends AbstractDAOFactory{
protected static final Connection conn = [Link]();
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
return new ClasseDAO(conn);
}
Classe [Link]
package [Link];
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
public DAO getProfesseurDAO() {
return null;
}
}
Vous devez y voir plus clair : même si la classe XMLDAOFactory ne fait rien du tout, vous voyez le principe de base et c'est l'important ! Nous avons maintenant
une hiérarchie de classes capables de travailler ensemble.
Je reprends le dernier exemple que nous avions réalisé, avec quelques modifications…
[Link]("\n\t************************************");
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
//On récupère la liste des élèves
[Link]("\n\tCelle-ci contient " + [Link]().size() + " élève(s)");
for(Eleve eleve : [Link]())
[Link]("\t\t - " + [Link]() + " " + [Link]());
[Link]("\n\t***********************************");
Et le résultat est le même qu'avant ! Tout fonctionne à merveille ! Si vous utilisez un jour l'usine de fabrication XML, vous n'aurez qu'une seule ligne de code à
changer :
Voilà : vous en savez plus sur ce pattern de conception et vous devriez être à même de coder le reste des méthodes (insertion, mise à jour et suppression). Il n'y a
rien de compliqué, ce sont juste des requêtes SQL. ;-)
Le pattern DAO vous permet de lier vos tables avec des objets Java.
Interagir avec des bases de données en encapsulant l'accès à celles-ci permet de faciliter la migration vers une autre base en cas de besoin.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD
Afin d'être vraiment le plus souple possible, on peut laisser la création de nos DAO à une factory codée par nos soins.
Pour gérer di érents types de DAO (BDD, XML, fichiers…), on peut utiliser une factory qui se chargera de créer nos factory de DAO.
Voilà : ce cours touche à sa fin. J'espère qu'il vous a plu et vous aura permis d'aborder Java en toute simplicité. Cependant, malgré son contenu, Java o re encore
beaucoup de fonctionnalités que ce cours n'aura pas abordé, notamment :
RMI ou Remote Method Invocation, API qui permet de développer des objets pouvant être appelés sur des machines distantes. En fait, vous appelez un objet
comme s'il était instancié depuis votre application alors qu'il se trouve en réalité quelque part sur le réseau. Ceci permet, entre autre, de développer des
applications dites client - serveur ;
JMF ou Java Media Framework, collection d'objets qui permet de travailler avec des fichiers multimédia (vidéo et son) ;
JOGL , API qui permet, tout comme Java 3D , de faire de la 3D mais cette fois en faisant un pont entre Java et la très célèbre bibliothèque OpenGL ;
Java EE ou Java Enterprise Edition, API de conception de sites web dynamiques très utilisée ;
J2ME ou Java 2 Micro Edition, API dédiée aux appareils mobiles (comme les smartphones) ;
LWJGL ou Lightweight Java Game Library, API qui o re la possibilité de créer des jeux vidéo.
Je ne peux pas tout nommer, mais vous êtes désormais en mesure de faire vos propres recherches pour découvrir toutes ces autres fonctionnalités de Java !
J'espère sincèrement que ce cours vous a permis de mieux comprendre le fonctionnement du langage Java et qu'il vous permettra d'aborder toutes ces API plus
facilement et plus sereinement.
Create PDF in your applications with the Pdfcrowd HTML to PDF API PDFCROWD