286
BTS IRIS – Cours et Travaux Pratiques Programmation Java (version 0.1) A. Lebret, TSIRIS, © Lycée Diderot, 1996/2006 en conformité avec le référentiel du BTS IRIS Activité Principale Partie 8 :Codage et Réalisation Tâches T3.3, T3.4 et T3.5 Temps nécessaire 232 h 1/286

Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

  • Upload
    others

  • View
    13

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

BTS IRIS – Cours et Travaux Pratiques

Programmation Java

(version 0.1)

A. Lebret, TSIRIS, © Lycée Diderot, 1996/2006

en conformité avec le référentiel du BTS IRIS

Activité Principale

Partie 8 :Codage et Réalisation

Tâches T3.3, T3.4 et T3.5

Temps nécessaire 232 h

1/286

Page 2: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Le présent document est mis à disposition sous un contrat Creative Commons.

Vous êtes libres :• de reproduire, distribuer et communiquer cette

création au public

Selon les conditions suivantes :

Paternité. Vous devez citer le nom de l'auteur original.

Pas d'Utilisation Commerciale. Vous n'avez pas le droit d'utiliser cette création à des fins commerciales.

Pas de Modification. Vous n'avez pas le droit de modifier, de transformer ou d'adapter cette création.

• A chaque réutilisation ou distribution, vous devez faire apparaître clairement aux autres les conditions contractuelles de mise à disposition de cette création.

• Chacune de ces conditions peut être levée si vous obtenez l'autorisation du titulaire des droits.

Ce qui précède n'affecte en rien vos droits en tant qu'utilisateur (exceptions au droit d'auteur : copies réservées à l'usage privé du copiste, courtes citations, parodie...)

Ceci est le Résumé Explicatif du Code Juridique (la version intégrale du contrat).

Alain Lebret

2/286

Page 3: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Table des matièresAU SUJET DES SÉANCES DE TRAVAUX PRATIQUES JAVA ..................................................................................12CHAPITRE 1 - INTRODUCTION À LA PROGRAMMATION ORIENTÉE OBJET ..........................................................13

1.Un peu de vocabulaire................................................................................................................131.1.Qu'entend-on par objet ?.....................................................................................................131.2.Classer les objets en catégories...........................................................................................141.3.Communication entre objets...............................................................................................16

1.3.1.Les diagrammes de collaborations..............................................................................161.3.2.Les diagrammes de séquences.....................................................................................17

1.4.Relations entre les classes...................................................................................................171.4.1.L'héritage.....................................................................................................................181.4.2.L'association................................................................................................................201.4.3.L'agrégation.................................................................................................................211.4.4.La composition............................................................................................................21

1.5.Le diagramme de classes.....................................................................................................221.6.L'encapsulation des données...............................................................................................231.7.Le polymorphisme..............................................................................................................241.8.Évolution de l'état d'un objet...............................................................................................24

2.Les méthodes pour modéliser avec des objets............................................................................243.Exercices.....................................................................................................................................25

CHAPITRE 2 - INTRODUCTION AU LANGAGE JAVA ........................................................................................271.Historique du langage.................................................................................................................272.Se procurer et installer Java........................................................................................................273.Quelques généralités sur Java.....................................................................................................28

3.1.Compilation native et bytecode...........................................................................................303.2.La machine virtuelle Java ..................................................................................................303.3.Les premières notions pour programmer............................................................................31

4.Un premier programme...............................................................................................................335.Génération automatique de la documentation............................................................................34

5.1.L'outil javadoc.....................................................................................................................345.1.1.Réalisation d'une documentation simple.....................................................................355.1.2.Utilisation des commentaires javadoc.........................................................................355.1.3.Profiter du format HTML............................................................................................35

5.2.Quelques instructions javadoc............................................................................................365.2.1.Documentation de la classe.........................................................................................365.2.2.Documentation des méthodes......................................................................................36

6.Exercices.....................................................................................................................................377.Séance de TP N°1 : Installation et configuration de la plate-forme Java...................................38

7.1.Installation et configuration du JDK 5.0 ............................................................................387.2.Mise en oeuvre du JDK 5.0.................................................................................................387.3.Travail sur les codes sources...............................................................................................397.4.Conditions...........................................................................................................................39

CHAPITRE 3 - LES TYPES PRIMITIFS ............................................................................................................421.La déclaration des variables........................................................................................................422.Portée d'une variable...................................................................................................................433.Les types numériques..................................................................................................................44

3.1.Les types numériques entiers..............................................................................................44a)Le type byte..................................................................................................................44

3/286

Page 4: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

b)Le type short ................................................................................................................45c)Le type int ...................................................................................................................45d)Le type long..................................................................................................................45

3.2.Les types numériques décimaux.........................................................................................453.2.1.Pourquoi les machines calculent-elles toujours faux ?................................................453.2.2.Les deux types de réels : float et double.....................................................................47

a)Le type float..................................................................................................................47b)Le type double..............................................................................................................47

4.Le type booléen...........................................................................................................................475.Le type caractère.........................................................................................................................476.Tableau récapitulatif...................................................................................................................487.Exercices.....................................................................................................................................49

CHAPITRE 4 - OPÉRATEURS ET EXPRESSIONS DU LANGAGE .............................................................................501.Les expressions numériques.......................................................................................................50

1.1.Introduction.........................................................................................................................501.2.Expressions.........................................................................................................................511.3.L'opérateur d'affectation =..................................................................................................511.4.Les opérateurs numériques..................................................................................................52

2.Les expressions booléennes........................................................................................................543.Les expressions retournant des chaînes de caractères................................................................554.Les affectations et les opérateurs associés..................................................................................555.Les autres opérateurs du langage................................................................................................566.Priorité des opérateurs................................................................................................................567.Les erreurs de codage dans les expressions................................................................................588.Exercices.....................................................................................................................................599.Séance de TP N°2 : Déboguer un programme............................................................................61

9.1.Travail demandé..................................................................................................................619.2.Conditions...........................................................................................................................61

CHAPITRE 5 - LES INSTRUCTIONS DU LANGAGE ............................................................................................631.Les instructions de contrôle .......................................................................................................63

1.1.L'instruction if...else ...........................................................................................................631.2.L'instruction switch ............................................................................................................64

2.Les instructions de boucle ..........................................................................................................662.1.L'instruction for ..................................................................................................................662.2.L'instruction while ..............................................................................................................672.3.L'instruction do...while .......................................................................................................672.4.Les instructions break et continue ......................................................................................68

3.Exercices.....................................................................................................................................684.Séance de TP N°3 : Faire des Choix...........................................................................................69

4.1.Travail demandé..................................................................................................................694.2.Conditions...........................................................................................................................69

5.Séance de TP N°4 : Faire des répétitions....................................................................................715.1.Travail demandé..................................................................................................................715.2.Conditions...........................................................................................................................71

CHAPITRE 6 - LE MODÈLE OBJET DE JAVA .................................................................................................721.Définition d'une classe en Java...................................................................................................72

1.1.Les attributs de la classe......................................................................................................721.2.Les méthodes.......................................................................................................................731.3.La surcharge de méthodes...................................................................................................74

2.Instanciation d'objet....................................................................................................................75

4/286

Page 5: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

2.1.L'allocation de mémoire......................................................................................................752.2.Les constructeurs.................................................................................................................752.3.Les finaliseurs.....................................................................................................................77

3.Le ramasse-miettes.....................................................................................................................774.Méthodes et attributs statiques....................................................................................................78

4.1.main() : le point d'entrée du programme.............................................................................784.2.Partage d'informations.........................................................................................................78

5.L'héritage....................................................................................................................................795.1.Ce qu'il ne faut surtout pas faire..........................................................................................795.2.Ce qu'il faut faire.................................................................................................................805.3.L'intérêt...............................................................................................................................83

6.Le polymorphisme......................................................................................................................846.1.Définition............................................................................................................................846.2.La liaison dynamique (dynamic binding)...........................................................................85

7.Utiliser l'abstraction....................................................................................................................867.1.Définir une classe abstraite.................................................................................................867.2.Définir une interface...........................................................................................................87

8.Attributs et méthodes constants..................................................................................................898.1.Attributs constants...............................................................................................................898.2.Méthodes finales.................................................................................................................908.3.Paramètres constants...........................................................................................................90

9.Les classes internes ....................................................................................................................9110.Les paquetages..........................................................................................................................91

10.1.Création d'un paquetage....................................................................................................9210.1.1.Le mot réservé package.............................................................................................9210.1.2.Placer les fichiers d'un paquetage..............................................................................92

10.2.Importer les classes d'un paquetage..................................................................................9210.2.1.Positionnement de la variable CLASSPATH............................................................9210.2.2.Le mot réservé import...............................................................................................9310.2.3.Accéder directement à une classe d'un paquetage.....................................................93

10.3.Compresser un paquetage.................................................................................................9410.3.1.L'utilitaire jar.............................................................................................................9410.3.2.L'archivage et CLASSPATH....................................................................................9410.3.3.Une petite subtilité.....................................................................................................94

11.Les droits d'accès......................................................................................................................9511.1.L'encapsulation..................................................................................................................95

11.1.1.L'accès public............................................................................................................9511.1.2.L'accès private...........................................................................................................9511.1.3.L'accès protected.......................................................................................................9611.1.4.L'accès friendly.........................................................................................................9611.1.5.Quelques conventions................................................................................................97

12.Exercices...................................................................................................................................9713.Séance de TP N°5 : Utiliser des classes et objets – Création d'une classe Calculette............100

13.1.Travail demandé..............................................................................................................10013.2.Conditions.......................................................................................................................100

CHAPITRE 7 - LES CHAÎNES DE CARACTÈRES .............................................................................................1021.Création et utilisation d'objets de la classe String....................................................................1022.Quelques méthodes de la classe String.....................................................................................1033.Exercices...................................................................................................................................1044.Séance de TP N°6 : Utiliser la classe String – Amélioration de la Calculette [1]....................105

5/286

Page 6: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

4.1.Travail demandé................................................................................................................1054.2.Conditions.........................................................................................................................105

5.Séance de TP N°7 : Aller plus loin dans les concepts objets – Amélioration de la Calculette [2].....................................................................................................................................................106

5.1.Travail demandé................................................................................................................1065.2.Conditions.........................................................................................................................107

CHAPITRE 8 - LES TABLEAUX EN JAVA .....................................................................................................1091.Définition d'un tableau..............................................................................................................1092.Une autre façon de définir un tableau.......................................................................................1093.Longueur d'un tableau et dépassement des bornes...................................................................1104.Manipuler un tableau d'objets...................................................................................................1115.Un tableau à deux dimensions..................................................................................................1126.La classe Arrays........................................................................................................................1137.Exercices...................................................................................................................................1138.Séance de TP N°8 : Collectionner un nombre fixe d'objets – Amélioration de la Calculette [3].....................................................................................................................................................114

8.1.Travail demandé................................................................................................................1148.2.Conditions.........................................................................................................................114

CHAPITRE 9 - LES EXCEPTIONS ...............................................................................................................1161.Traitement de l'exception..........................................................................................................116

1.1.Le mot réservé try.............................................................................................................1161.2.Le mot réservé catch.........................................................................................................1161.3.Le mot réservé finally.......................................................................................................1161.4.Quelques exceptions.........................................................................................................117

2.Relayer l'exception avec throws..............................................................................................1173.Définir ses propres exceptions..................................................................................................1174.Lever une exception avec throw..............................................................................................1185.Séance de TP N°9 : Gérer les exceptions – Amélioration de la Calculette [4].........................119

5.1.Travail demandé................................................................................................................1195.2.Conditions.........................................................................................................................119

CHAPITRE 10 - RÉALISER UNE INTERFACE GRAPHIQUE ...............................................................................1201.Introduction...............................................................................................................................120

1.1.Conteneurs et composants.................................................................................................1201.2.L'API AWT ......................................................................................................................1211.3.L'API Swing .....................................................................................................................1211.4.Un exemple.......................................................................................................................121

1.4.1.Version AWT............................................................................................................1211.4.2.Version SWING........................................................................................................123

1.5.Les sujets abordés par ce chapitre ....................................................................................1242.La gestion des événements en Java...........................................................................................124

2.1.Traitement d'événements simples.....................................................................................1252.1.1.Définition d'un auditeur.............................................................................................1252.1.2.Enregistrement d'un auditeur.....................................................................................1262.1.3.Définition de classes privées ....................................................................................1272.1.4.Définition de classes anonymes ...............................................................................1282.1.5.Les classes d'adaptation ............................................................................................128

2.2.Types d'événements supportés par AWT et Swing ..........................................................1292.2.1.Gestion du clavier......................................................................................................129

a)Intercepter des frappes de touches..............................................................................130b)Intercepter des frappes composées de touches...........................................................131

6/286

Page 7: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

2.3.Gérer la souris...................................................................................................................133a)Utiliser les « clics » de la souris.................................................................................133

2.4.Mais encore ?....................................................................................................................1363.Stratégies de placement des composants..................................................................................136

3.1.Affecter une stratégie de placement à un conteneur.........................................................1363.2.La stratégie de placement FlowLayout.............................................................................1373.3.La stratégie de placement BorderLayout..........................................................................1383.4.La stratégie de placement GridLayout..............................................................................1393.5.La stratégie de placement GridBagLayout........................................................................1413.6.La stratégie de placement CardLayout..............................................................................143

4.Les composants graphiques......................................................................................................1454.1.Utilisation d'un bouton......................................................................................................1454.2.Utilisation d'un label.........................................................................................................1454.3.Utilisation d'une « boîte à cocher »...................................................................................1454.4.Utilisation d'une « boîte de liste ».....................................................................................1474.5.Utilisation d'une « boite de choix »...................................................................................1494.6.Utilisation d'une « zone de saisie de texte »......................................................................1504.7.Valider l'ajout d'un composant..........................................................................................1514.8.Utilisation d'un menu........................................................................................................152

5.Complément sur les conteneurs................................................................................................1535.1.Utilisation d'une « boîte de dialogue »..............................................................................1535.2.Utiliser des ascenseurs......................................................................................................1545.3.Utilisation d'un conteneur avec ascenseurs.......................................................................156

6.Conclusion................................................................................................................................1577.Séance de TP N°10 : Réaliser une Interface Graphique avec AWT – Amélioration de la Calculette [5]................................................................................................................................158

7.1.Travail demandé................................................................................................................1587.2.Conditions.........................................................................................................................159

8.Séance de TP N°11 : Réaliser une Interface Graphique avec Swing – Amélioration de la Calculette [6]................................................................................................................................159

8.1.Travail demandé................................................................................................................1598.2.Conditions.........................................................................................................................159

CHAPITRE 11 - SAISIR DES DONNÉES OU UTILISER DES FICHIERS ...................................................................1601.Généralités sur les flux de données...........................................................................................1602.Saisir des données envoyées par le clavier...............................................................................160

2.1.Première méthode.............................................................................................................1602.2.Deuxième méthode...........................................................................................................161

3.Rediriger la sortie standard vers un fichier...............................................................................1624.Manipuler les fichiers textes.....................................................................................................162

4.1.écrire dans un fichier texte................................................................................................1624.2.Lire dans un fichier texte..................................................................................................163

4.2.1.Premier exemple........................................................................................................1634.2.2.Deuxième exemple....................................................................................................163

5.Manipuler les fichiers binaires..................................................................................................1645.1.écrire dans un fichier binaire.............................................................................................1645.2.Lire dans un fichier binaire...............................................................................................165

6.Utiliser la classe File.................................................................................................................1667.Sérialisation..............................................................................................................................168

7.1.Présentation du concept....................................................................................................1687.1.1.Les problématiques....................................................................................................168

7/286

Page 8: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

7.1.2.Les solutions..............................................................................................................1687.2.Le support de sérialisation en Java...................................................................................169

7.2.1.Utilisation de la sérialisation.....................................................................................1697.2.2.Coder une classe sérialisable.....................................................................................1707.2.3.Un piège à éviter........................................................................................................171

8.Séance de TP N°12 : Travailler avec les flux d'entrée/sortie – Amélioration de la Calculette [7].....................................................................................................................................................172

8.1.Travail demandé................................................................................................................1728.2.Conditions.........................................................................................................................172

CHAPITRE 12 - COMMUNIQUER À TRAVERS UN RÉSEAU ...............................................................................1731.Quelques définitions.................................................................................................................1732.Connexion en mode « non connecté » ......................................................................................1733.Connexion en mode « connecté » .............................................................................................1754.Séance de TP N°13 : Travailler avec les réseaux sous UDP – Amélioration de la Calculette [8].....................................................................................................................................................178

4.1.Travail demandé................................................................................................................1784.2.Conditions.........................................................................................................................179

5.Séance de TP N°14 : Travailler avec les réseaux sous TCP – Amélioration de la Calculette [9].....................................................................................................................................................180

5.1.Travail demandé................................................................................................................1805.2.Conditions.........................................................................................................................181

CHAPITRE 13 - UTILISATION DES THREADS ...............................................................................................1821.Introduction...............................................................................................................................182

1.1.Les threads........................................................................................................................1821.2.Multithreading et processus..............................................................................................183

2.Les threads en Java...................................................................................................................1843.Création et démarrage d'un thread............................................................................................185

3.1.étendre la classe Thread....................................................................................................1853.2.Implémenter l'interface Runnable.....................................................................................1873.3.Utiliser la méthode join()..................................................................................................188

4.Gestion des threads...................................................................................................................1884.1.Cycle de vie d'un thread....................................................................................................1894.2.Démarrage, suspension, reprise et arrêt d'un thread..........................................................1894.3.Gestion de la priorité d'un thread......................................................................................1904.4.Gestion d'un groupe de threads.........................................................................................1914.5.Un exemple: Dessiner un disque.......................................................................................191

5.Synchronisation de threads et accès aux ressources partagées.................................................1935.1.Notions de verrous............................................................................................................1945.2.Attendre l'accès à une ressource........................................................................................1945.3.Le modèle « producteur/consommateur ».........................................................................1955.4.Exemple de la « boite aux lettres »...................................................................................197

6.Utiliser un démon......................................................................................................................1997.Séance de TP N°15 : Utiliser les threads – Amélioration de la Calculette [10].......................202

7.1.Travail demandé................................................................................................................2027.2.Conditions.........................................................................................................................202

CHAPITRE 14 - INTERFACER JAVA ET LE MONDE NATIF ...............................................................................2031.Exécuter des applications natives.............................................................................................2032.Interfacer Java et C/C++...........................................................................................................205

2.1.Introduction à JNI.............................................................................................................2052.2.Caractéristiques de JNI.....................................................................................................206

8/286

Page 9: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

2.2.1.Les en-têtes jni.h et jni_md.h....................................................................................2062.2.2.Les types de base et leurs équivalents natifs.............................................................2062.2.3.Les objets et leurs équivalents natifs.........................................................................2072.2.4.Déclaration des fonctions..........................................................................................2072.2.5.Accéder à des chaînes de caractères..........................................................................2072.2.6.Accéder à des tableaux..............................................................................................2082.2.7.Accéder à des attributs d'objets.................................................................................2082.2.8.Appeler une méthode.................................................................................................209

2.3.Exemple : la classe PortES................................................................................................2092.3.1.Génération du fichier PortES.h.................................................................................2102.3.2.Création du code source natif PortES.c.....................................................................2112.3.3.Compilation du code natif.........................................................................................2122.3.4.Installation et test.......................................................................................................2122.3.5.Automatiser une partie de la procédure.....................................................................212

3.Séance de TP N°16 : Lancer une application native – Amélioration de la Calculette [11]......2143.1.Travail demandé................................................................................................................2143.2.Conditions.........................................................................................................................214

CHAPITRE 15 - ALGORITHMIQUE EN JAVA ................................................................................................2161.Structures de données ..............................................................................................................2162.Les méthodes de tri et de recherche..........................................................................................216

2.1.Introduction.......................................................................................................................2162.2.Les méthodes de tri...........................................................................................................216

2.2.1.Le tri à bulle..............................................................................................................216a)Principe.......................................................................................................................216b)Algorithme.................................................................................................................216c)Nombre d'échanges maximal......................................................................................217

2.2.2.Le tri par sélection.....................................................................................................217a)Principe.......................................................................................................................217b)Algorithme.................................................................................................................217c)Nombre d'échanges maximal......................................................................................218

2.2.3.Le tri par insertion.....................................................................................................218a)Principe.......................................................................................................................218b)Algorithme.................................................................................................................218c)Nombre d'échanges maximal :....................................................................................219

2.2.4.Le tri rapide...............................................................................................................2192.3.Les méthodes de recherche...............................................................................................219

2.3.1.La recherche linéaire.................................................................................................219a)Principe.......................................................................................................................219b)Algorithme.................................................................................................................219

2.3.2.La recherche dichotomique (ou binaire)...................................................................219a)Principe.......................................................................................................................219b)Algorithme.................................................................................................................220

3.Cryptographie...........................................................................................................................2204.Compression.............................................................................................................................2205.Séance de TP N°17 : Listes, Files et Piles en Java...................................................................221

5.1.Travail demandé................................................................................................................2215.1.1.Préparation du paquetage structures..........................................................................2215.1.2.Mise en oeuvre de la classe Pile................................................................................221

5.2.Conditions.........................................................................................................................2216.Séance de TP N°18 : Tri et Recherche en Java.........................................................................224

9/286

Page 10: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

6.1.Travail demandé................................................................................................................2246.1.1.Tri d'un tableau d'entiers...........................................................................................2246.1.2.Recherche dans un tableau d'entiers..........................................................................224

6.2.Conditions.........................................................................................................................2257.Séance de TP N°19 : Cryptographie en langage Java...............................................................225

7.1.Travail demandé................................................................................................................2257.1.1.Préparation du paquetage crypto...............................................................................2257.1.2.Implémentation dans le paquetage crypto.................................................................2267.1.3.Cryptanalyse : Casser les chiffres.............................................................................226

7.2.Conditions.........................................................................................................................2268.Séance de TP N°20 : Compression de données en langage Java..............................................227

8.1.Travail demandé................................................................................................................2278.1.1.Préparation du paquetage compression.....................................................................2278.1.2.Implémentation dans le paquetage compression.......................................................228

8.2.Conditions.........................................................................................................................228CHAPITRE 16 - LA GÉNÉRICITÉ ..............................................................................................................229

1.Introduction...............................................................................................................................2292.Une classe Boite........................................................................................................................2293.Types génériques......................................................................................................................2304.Conventions pour nommer les paramètres de type...................................................................2315.Méthodes et constructeurs génériques......................................................................................2326.Paramètres génériques..............................................................................................................233

CHAPITRE 17 - QUATRE TP SUPPLÉMENTAIRES EN JAVA ...........................................................................2341.Séance de TP N°21 : Ressources partagées avec les threads....................................................234

1.1.Travail demandé................................................................................................................2341.2.Conditions.........................................................................................................................235

2.Séance de TP N°22 : Communiquer via la liaison série RS-232 en Java.................................2352.1.Travail demandé................................................................................................................2352.2.Conditions.........................................................................................................................236

3.Séance de TP N°23 : Le parcours du Cavalier aux échecs.......................................................2363.1. Travail demandé...............................................................................................................2363.2.Conditions.........................................................................................................................238

4.Séance de TP N°23 : Le jeu du Morpion en réseau..................................................................2384.1. Travail demandé...............................................................................................................239

4.1.1.Fonctionnement de l'application................................................................................239a)Côté Serveur :.............................................................................................................239b)Côté Client :...............................................................................................................241

4.2.Travail demandé................................................................................................................2434.2.1.Client pour le jeu du Morpion...................................................................................2434.2.2.Serveur pour le jeu du Morpion................................................................................244

4.3.Conditions.........................................................................................................................244CHAPITRE 18 - ANNEXES .......................................................................................................................245ANNEXE 1 – UTILISATION D'ECLIPSE .......................................................................................................246ANNEXE 2 - IMAGES ET ANIMATIONS ........................................................................................................250ANNEXE 3 – INTERNATIONALISATION EN JAVA ..........................................................................................259ANNEXE 4 - TRAVAILLER AVEC UNE BASE DE DONNÉES MYSQL ................................................................261GLOSSAIRE ...........................................................................................................................................271MOTS RÉSERVÉS DU LANGAGE .................................................................................................................276BIBLIOGRAPHIE .....................................................................................................................................288

10/286

Page 11: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

11/286

Page 12: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Au sujet des séances de travaux pratiques Java

AU SUJET DES SÉANCES DE TRAVAUX PRATIQUES JAVA

L'objectif de ce document « Cours et Travaux Pratiques » d'une durée totale de 232 heures (cours : 40 heures, TP : 192 heures) est de vous introduire le langage de programmation Java. Sachez cependant que votre maîtrise de ce langage ne sera proportionnelle qu'au temps que vous y aurez vraiment consacré en toute autonomie.Vous effectuerez un compte-rendu électronique au format ODT (Open Document Text) à l'aide de la suite OpenOffice et, dans lequel vous inclurez la description de vos essais personnels lors de la lecture des différents chapitres, ainsi que celle des manipulations qui peuvent vous être demandées. Des captures d'écran pourront être incluses à l'aide d'outils tels que gnome-panel-screenshot (capture d'écran) et gimp (retouche d'images).

La note du TP sera constituée par :• la vérification hebdomadaire de l'état d'avancement du rapport ;• deux entretiens individuels de 10mn qui interviendront en cours de séances.

et tiendra compte du comportement et du degré d'autonomie à chaque séance.

Typographie :vocabulaire/concept, mot réservé du langage, mot de l'API, code source, répertoire, commande.

Pour vos recherches documentaires, outre les ouvrages cités en bibliographie, je vous conseille sur Internet :

• le Guide francophone des conventions de codage pour la programmation en langage Java d'Hugo Etiévant : ftp://ftp-developpez.com/cyberzoide/java/JavaStyle.pdf ;

• le site du dictionnaire Java : http://javaalmanac.com ;• les Java Tips : http://www.java-tips.org/ ;• le site de Réal Gagnon : http://www.rgagnon.com/howto.html ; • l'excellent livre en ligne de Bruce Eckel (traduit en français) : http://penserenjava.free.fr ;• le cours (en anglais) Javanotes de David Eck : http://math.hws.edu/javanotes5/ ;• le livre électronique Programmation Java pour les enfants, les parents et les grands parents

de Jacob Fain : http://www.xoteam.com/fr/products/JavaEnfants_20060615.pdf ;• le cours Du C/C++ à Java d'Emmanuel Puybaret : http://www.eteks.com/coursjava/.• le site de Rémi Forax : http://www-igm.univ-mlv.fr/~forax/.

Pour le développement lors des séances de travaux pratiques, je vous conseille les logiciels suivants :

• le JDK 5.0 : http://java.sun.com ;• l'éditeur de texte Jext écrit en Java : http://www.jext.org/ ;• l'IDE de développement Eclipse : http://www.eclipse.org/ ;• l'outil ArgoUML pour la modélisation objet : http://argouml.tigris.org/.

12/286

Page 13: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

CHAPITRE 1 - INTRODUCTION À LA PROGRAMMATION ORIENTÉE OBJET

La programmation orientée objet (Object-Oriented Programming en anglais), cherche à décrire un problème de façon la plus proche possible de la spécification de ce dernier. Apparue dans les années 1970 avec le langage Smalltalk, elle connaît un essor formidable dix ans plus tard avec l'apparition de langages tels qu'OjectiveC, C++ ou encore Eiffel, puis enfin Java au milieu des années 1990. L'approche de la programmation par objet propose une entité logicielle appelée objet, qui, associée à des concepts tels que l'encapsulation des données, les messages entre objets, l'héritage, etc., confère au langage une expressivité accrue par rapport à la programmation dite procédurale réalisée avec des langage tels que C. Ce type de programmation est associée à des méthodes de modélisation spécifiques, telle que la notation UML. Nous allons, dans les paragraphes qui suivent, tenter d'en extraire une partie du vocabulaire.

1. Un peu de vocabulaire

1.1. Qu'entend-on par objet ?

Un objet peut représenter une réalité physique comme une voiture, un téléphone portable, etc., ou encore virtuelle comme un numéro de téléphone, une opération mathématique, etc., et dépend du contexte dans lequel il est utilisé.

L'unité de base d'un programme écrit dans un langage dit « orienté objet », est l'objet.

Définition Un objet est une entité logicielle autonome modélisant l’aspect statique et dynamique d’une partie du monde réel.

Cette entité est constitué par l'ensemble :

IDENTITÉ + ÉTAT + COMPORTEMENT

• l'identité est l'identifiant qui permet de distinguer l'objet parmi

d'autres objets du même type dans le système ;• l'état est l'ensemble des valeurs instantanées que prennent les

attributs et il évolue dans le temps. Les attributs de l'objet (on parle aussi de données membres) sont les données caractérisant l'état de l’objet ;

• son comportement décrit les actions que peut effectuer l'objet ainsi que les réactions qu'il peut avoir en réponses à des messages envoyés par d'autres objets. Le comportement est défini par des

13/286

Page 14: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

méthodes (encore appelées opérations, services ou fonctions membres). Celles-ci permettent à l'objet d’agir sur ses attributs pour modifier son état et ainsi réagir aux sollicitations extérieures.

La figure suivante présente quelques objets de la vie courante :

• tous ces objet ont une identité. Par exemple, la smart et la twingo sont toutes les deux des

voitures de noms différents ;• de la même manière, tous ces objets ont un état. Par exemple, la smart et la twingo ont

comme attributs leur marque (Mercedes et Renault), leur couleur(beige et jaune), leur immatriculation (45BEJ91 et 123CDE75), etc. ;

• enfin, tous ces objets ont un comportement. Par exemple, smart comme twingo peuvent avancer(), reculer(), etc..

On regroupe souvent les termes d'attributs et de méthodes sous celui de champs de l'objet.

1.2. Classer les objets en catégories

Les objets smart et twingo sont tous les deux des voitures, threepio et r2d2 sont tous les deux des robots, etc.. Il est donc possible, comme dans le monde réel, de constituer des groupes d’objets ayant les mêmes attributs, mais pas les mêmes valeurs !Le concept permettant de classer les objets en catégories est celui de classe.

Définition : une classe est un type de données abstrait regroupant les caractéristiques (attributs et méthodes) communes à des objets et permettant de créer des objets possédant ces propriétés.

14/286

Figure 1: Objets de la vie courante.

Page 15: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

Une classe représente la structure, le squelette commun à un ensemble d’objets.

Une classe est une abstraction d’objets.

Un objet est une instance de classe.

Dans le cas des objets smart et twingo, nous pouvons définir une classe Voiture composée des attributs marque, couleur, immatriculation, etc., et des méthodes demarrer(), freiner(), etc.. On représentera les classes graphiquement de la manière suivante :

Donc par exemple, pour la classe Voiture ayant permis l'instanciation des objets smart et twingo, ainsi que pour la classe Robot qui permet celle des objets threepio et r2d2, ou encore la classe Point qui permet celle de points dans le plan, nous avons la représentation graphique suivante :

De la même façon, nous pouvons représenter nos objets à l'aide de la représentation graphique suivante. Les objets sont représentés par des rectangles comportant le nom de l'objet suivi par le séparateur ':', puis le nom de la classe. Le tout étant souligné. Les boites précisant les états de chaque objet sont des commentaires.

15/286

Figure 2: Représentation d'une classe.

Figure 3: Représentation des classes Voiture, Point et Robot.

Page 16: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

Remarque : un objet est dit « anonyme » lorsque son nom n'apparaît pas explicitement, comme par exemple :Robot.

1.3. Communication entre objets

La figure 1 nous a permis de constater que le robot threepio « parlait » au robot r2d2. Les objets sont donc capables d'échanger des informations par l'envoi de messages. Nous le représentons schématiquement de la manière suivante :

Les messages permettent de mettre en valeur une vue dynamique d’un modèle objet. Cette vue dynamique est précisées par deux types de diagrammes complémentaires et reliant les objets entres eux : les diagrammes de collaborations et les diagrammes de séquences.

1.3.1. Les diagrammes de collaborations

Ce sont des diagrammes qui permettent de représenter schématiquement l’envoi de messages entre les différents objets.

Exemple : Supposons la gestion d'un aéroport où un objet tourDeControle planifie le décollage et l'atterrissage d'avions. vol8 en approche de l'aéroport, demande à tourDeControle l'autorisation d'atterrir. Après vérification, tourDeControle envoie le message autoriser à l'objet vol8 qui en réation à ce dernier va exécuter sa méthode atterrir(). De la même façon, en accord avec les règles internationales de l'aviation, l'objet tourDeControle peut autoriser l'objet vol7 à décoller en lui envoyant le message autoriser, une fois seulement que vol8 sera posé. Celui-ci en réaction exécutera sa méthode decoller().

16/286

Figure 4: Représentation graphique des objets smart, p1 et threepio

Figure 5: Représentation de l'envoi d'un message entre deux objets.

Page 17: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

1.3.2. Les diagrammes de séquences

Les diagrammes de séquences permettent de représenter temporellement l’envoi de messages entre objets. Ils complètent en ce sens les diagrammes de collaborations.

Exemple : Dans le diagramme ci-dessous, monsieur dupont, un client de la banque creditX souhaite virer une somme d'argent montant de son compte bancaire c1 à son compte bancaire c2. Il transmet alors sa demande de virement au creditX en lui envoyant le message virer(montant,c1,c2). Le creditX va à partir de là, tout d'abord débiter la somme montant du compte c1 en lui envoyant le message retirer(montant), puis ensuite créditer le compte c2 en lui envoyant le message deposer(montant). L'envoi des messages respectent un ordre temporel dans lequel des contraintes de temps peuvent avoir été ajoutées.

1.4. Relations entre les classes

La vue dynamique du modèle objet qui nous permet de modéliser un système, doit être complétée par une vue statique de celui-ci. Nous allons pour y parvenir, étudier tout d'abord les relations qui peuvent exister entre les classes qui décrivent les objets.Il existe deux types de relations entre les classes : L'héritage et la délégation, cette dernière pouvant être une association, une agrégation ou encore une composition.

17/286

Figure 6: Exemple de diagramme de collaborations pour l'aéroport.

Figure 7: Exemple d'un diagramme de séquences pour le virement.

Page 18: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

1.4.1. L'héritage

Définition : L'héritage est une relation de transmission des propriétés d'une classe (ses attributs et méthodes) vers une sous-classe.

Une classe peut être spécialisée en d'autres classes, afin d'y ajouter des caractéristiques spécifiques ou d'en adapter certaines. Dans un des exemples précédents, les classes Airbus et Concorde peuvent avoir hérité d'une classe Avion. La représentation de l'héritage est la suivante :

On dit que B hérite ou dérive de A. On dit encore que B « est un » A.

La classe A est appelée super-classe ou classe mère de B.

La classe B, quant à elle, est appelée classe dérivée, classe fille ou encore sous-classe de A.

Plusieurs classes peuvent donc être généralisées en une classe qui les factorise, afin de regrouper les caractéristiques communes d'un ensemble de classes. Ainsi, l’héritage permet de définir une hiérarchie de classes.

Exemple : Hiérarchie de classes

18/286

Figure 8: Classe B héritant d'une classe A.

Page 19: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

Exemple : Illustration de la transmission des propriétés lors d'un héritage.

L'héritage peut être simple ou multiple :• simple : une classe dérive d’une seule classe ;

• multiple : une classe dérive de plusieurs classes et donc de leurs propriétés, comme c'est le cas sur la figure ci-contre, où la classe C hérite à la fois de A et de B.

Exemple : Hiérarchie des classes. La classe Omnivore hérite à la fois de la classe Herbivore et de la classe Carnivore.

19/286

Figure 9: Exemple de hiérarchie des classes.

Figure 10: Transmission des propriétés avec un héritage simple.

Figure 11: Représentation d'un héritage multiple.

Page 20: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

Exemple : Illustration de la transmission des propriétés lors d'un héritage multiple.

1.4.2. L'association

Définition : Une association est une relation simple entre deux classes sans notion de dépendance forte, ni lien de subordination entre les deux.

L'association est une relation symétrique pour laquelle on peut mentionner des cardinalités (le nombre d'objets).

A est associée à au moins un objet B (1..*) alors que tout objet B n'est associé qu'à un seul objet A.

Exemple : Un train peut ou non avoir des passagers (0..*) alors que le passager prend au moins un

20/286

Figure 12: Exemple d'héritage multiple. Les Omnivores sont Herbivores ET Carnivores.

Figure 13: Transmission des propriétés avec un héritage multiple.

Figure 14: Représentation d'une association.

Page 21: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

train.

1.4.3. L'agrégation

Définition : Une agrégation est une relation entre deux classes spécifiant que les objets d’une classe sont des éléments de l’autre classe. On parle d'objet agrégat et d'objet agrégé. La destruction d’un objet agrégé ne remet pas en cause le sens de l’objet agrégat et inversement.

L'agrégation est une relation antisymétrique pour laquelle on peut mentionner des cardinalités (à 1 par défaut).

Un objet A possède au plus un objet B.

Exemple : Un Train est un agrégat de Wagon (0..*) et un Wagon est agrégé à un Train. De la même manière, un Wagon est un agrégat d'au moins un Siege. Il est à remarquer qu'un Siege à toujours un sens même sans l'existence du Wagon ou du Train.

1.4.4. La composition

Définition : Une composition est une relation proche de la relation d’agrégation avec cependant une dépendance plus forte entre les deux objets. On parle alors d’objet composite et d’objet composant.

21/286

Figure 15: Exemple d'une association Train/Passager(s).

Figure 16: Représentation de l'agrégation. La classe B est agrégée à l'agrégat A.

Figure 17: Exemple d'agrégations Train/Wagon(s)/Siege(s).

Page 22: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

A la différence de l'agrégation, la destruction d’un objet composant remet en cause le sens de l’objet composite et inversement.

La composition est une relation antisymétrique pour laquelle on peut mentionner des cardinalités (à 1 par défaut).

Exemple : La molécule d'eau est composée d'un atome d'oxygène et de deux atomes d'hydrogène.

1.5. Le diagramme de classes

Le diagramme de classes représente les différentes relations entre les classes. Il reflète l’aspect statique du modèle objet.

Exemple :

22/286

Figure 18: Représentation d'une composition. La classe A est composée

d'au moins un B.

Figure 19: Composition de la Molécule d'eau.

Page 23: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

1.6. L'encapsulation des données

Définition : L'encapsulation des données est un mécanisme qui permet d’assurer l’intégrité des attributs de l’objet.

L'utilisateur de l'objet ne peut modifier directement les attributs de l’objet, car leur accès n'est rendu possible que par l'intermédiaire de l'interface de l’objet (ensemble de méthodes rendues accessibles à l’utilisateur). Celle-ci constitue la vue externe de l’objet.

L'encapsulation définit au moins 3 types d’accès aux membres (attributs + méthodes) : • membres privés : accès réservé aux méthodes de l’objet lui même ;• membres protégés : accès réservé aux objets issus de classes héritières ;• membres publics : accès autorisé pour tous les objets.

Exemple : L'accès au contenu des attributs x et y est impossible sans passer par les méthodes retournerX() et retournerY().

23/286

Figure 20: Exemple de diagramme de classes : le cas du Train.

Figure 21: Encapsulation des coordonnées x et y d'un Point.

Page 24: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

1.7. Le polymorphisme

Définition : Le polymorphisme est la faculté d'une méthode à pouvoir s'appliquer à des objets de classes héritées en prenant différentes formes.On parle de redéfinition de méthodes dans les sous-classes.

1.8. Évolution de l'état d'un objet

Nous pouvons représenter l'évolution de l'état d'un objet dans le temps par un diagramme d'états-transitions. Ce diagramme représente un automate à états finis permettant de décrire l'évolution de l'état. De la même manière, pour décrire l'évolution de la méthode d'un objet, nous pourrons utiliser un diagramme d'activité.

2. Les méthodes pour modéliser avec des objetsPlus de 50 méthodes objets ont été mises aux points entre 1970 et 1994. Les plus utilisées ont été OMT (Object Modeling Technique), BOOCH'93 et OOSE (Object-Oriented Software Engineering). En 1994, ces méthodes ont été mises en commun pour donner UML (Unified Modeling Language), que nous venons d'entrevoir dans les paragraphes précédents au travers des différents diagrammes.

Le principe général de développement en utilisant une approche orientée objet est le suivant :

1. Déterminer les objets présents dans le domaine qui nous intéresse, cela afin d’isoler leurs caractéristiques ainsi que les opérations qui les utilisent.

2. Modéliser les relations entre les objets ainsi que leurs interactions (aspects statique et dynamique).

24/286

Figure 22: Exemple de polymorphisme avec méthodeA().

Page 25: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

Nous renvoyons le lecteur aux ouvrages spécialisés sur UML (Muller [6] et Rumbaugh [8]), ainsi qu'à la mise en pratique à l'aide d'outils de modélisation comme l'Open Source ArgoUML (http://argouml.tigris.org/), distribué sous licence BSD.

3. ExercicesExercice 1.1Commenter en moins d’une ligne les mots : encapsulation, héritage et polymorphisme.

Exercice 1.2Traduire en un diagramme de classes l'ensemble des phrases suivantes :« un Ordinateur est une Machine » ; « un PDA est une Machine » ; « une Machine est composée d'au moins un Port » ; « un PortInfrarouge est un Port » ; « un Port a pour caractéristiques un numéro et une adresse (nombres entiers) » ; « un Ordinateur possède un PortInfrarouge » ; « un PDA possède lui aussi un port Infrarouge »; « un Ordinateur peut communiquer avec plusieurs PDA » ; « il est possible de lire et d'écrire sur un Port ».

Exercice 1.3 Le dessin ci-dessous représente des figures (triangles, carrés ou cercles) emboîtées. Les triangles contiennent une ou plusieurs figures. Les carrés ne contiennent rien. Les cercles contiennent exactement une figure. Les figures possèdent des « côtés ». On dira que les cercles ont un seul côté, les triangles trois côtés et les carrés quatre côtés. a) A partir du texte précédent déterminer les classes du domaine et dessiner le diagramme de classes général.b) Dessiner un diagramme d'instances (objets) correspondant au dessin sans dessiner les instances de la classe Cote.c) Dessiner un diagramme de classes correspondant à la figure. Le diagramme comprendra les

25/286

Figure 23: Principe de développement en Orienté Objet.

Page 26: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 1 - Introduction à la Programmation Orientée Objet

classes Figure, Cercle, Carre, Triangle et Cote et des relations à déterminer. Placer les ordres de multiplicité (cardinalités) sur ce diagramme.

26/286

Page 27: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

CHAPITRE 2 - INTRODUCTION AU LANGAGE JAVA

1. Historique du langageJava a officiellement vu le jour en août 1995, lorsque la société Netscape® acheta la première licence pour son navigateur. Les origines du langage remontent cependant à 1991 lorsqu'une équipe de la société Sun Microsystems, Inc., composée de James Gosling , Patrick Naughton et Mike Sheridan, décide de construire le prototype d’un logiciel qui permettrait de contrôler la téléphonie, l'électroménager, etc.. En 1992, tout était près pour envahir le marché avec cette nouvelle technologie, mais ce fut malheureusement un échec. Bill Joy, co-fondateur de Sun, sauva heureusement le projet. En effet, devant la montée en puissance d'Internet, il lui sembla intéressant de proposer un environnement complet et portable, avec l'objectif de palier à l'hétérogénéité des machines et des logiciels utilisés sur l'Internet. En 1995, le langage « Oak » développé par Gosling est rebaptisé « Java », puis il est alors soumis à la communauté Internet. Une machine virtuelle, un compilateur ainsi que de nombreuses spécifications sont fournis gratuitement, mais sous licence, et Java se lance alors dans une conquête fulgurante. Aujourd'hui, après de nombreuses modifications et améliorations, Java n'est plus uniquement une solution liée à Internet car de plus en plus de sociétés ou de particuliers, utilise ce langage pour leurs développements.

2. Se procurer et installer JavaLe kit de développement Java est disponible gratuitement sur le site officiel du langage : java.sun.com. Il se décline actuellement en trois versions pour les systèmes d'exploitation Ms-Windows, GNU/Linux, Mac OS X et Sun-Solaris :

• J2SE 5.0/JDK (Java 2 Standard Edition – Java Development Kit), la version orientée développement d'applications génériques que nous utiliserons (voir figure 2) ;

• J2EE (Java 2 Enterprise Edition), la version permettant la réalisation de servlets pour les serveurs d'entreprise ;

• J2ME (Java 2 Micro Edition), la version pour les systèmes embarqués.

27/286

Page 28: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

L'installation est facilitée sous Ms-Windows (fichier exécutable) ou sous GNU/Linux (fichiers rpm, deb, etc.) mais nécessite cependant la post-configuration suivante. En effet, les variables d’environnement PATH et CLASSPATH doivent être correctement définies. Par exemple :

• Sous Windows NT/2000/XP, le JDK étant supposé être installé sous C:\java\jdk1.5.0 : PATH=%PATH%;C:\java\jdk1.5.0\bin CLASSPATH=%CLASSPATH%;.

• Sous GNU/Linux, le JDK étant supposé être installé sous /usr/lib/jdk1.5.0 : export PATH=$PATH:/usr/lib/jdk1.5.0/bin export CLASSPATH=$CLASSPATH:.

3. Quelques généralités sur JavaConçu sur le modèle du C++ et reprenant des concepts de langages orientés objets plus anciens tels que Smalltalk, Java est un langage objet qui permet de réaliser des applications portables sur toutes les plates-formes et sur tous les systèmes d'exploitation pourvus d'un interpréteur appelé « machine virtuelle Java » (voir la figure suivante).Rappelons qu'un ordinateur ne sait exécuter que des programmes écrits en instructions machines, compréhensibles par son processeur central. Java, comme Pascal, Ada, C++, etc., fait partie de la famille des langages dits « évolués » ou encore de « hauts niveaux », qui ne sont pas compréhensibles immédiatement par le processeur de l'ordinateur. Il est donc nécessaire d'effectuer une « traduction » d'un programme écrit dans ce langage évolué afin que le processeur puisse l'exécuter. Les deux voies utilisées pour exécuter un programme évolué sont la compilation ou l'interprétation :

28/286

Figure 24: Structure du J2SE 5.0

Page 29: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

Un compilateur du langage L pour un processeur P, est un logiciel qui traduit un programme source écrit en L en un programme cible

écrit en instructions machines exécutables par le processeur P. Exemples de langages compilés : C, C++, Fortran, Ada, etc..

Un interpréteur du langage L pour le processeur P, est un logiciel qui ne produit pas de programme cible mais qui effectue lui-même immédiatement les opérations spécifiées par le programme source.Exemples de langages interprétés : Basic, Python, Perl, etc..

Un compromis assurant la portabilité d'un langage s'appelle une pseudo-machine.

Lorsque le processeur P n'est pas une machine physique, mais un logiciel qui la simule, on l'appelle alors pseudo-machine ou p-machine. Le programme source est traduit par le compilateur en instructions de la p-machine et se nomme pseudo-code ou p-code. La p-machine standard peut ainsi être implantée dans n'importe quel machine physique à travers un logiciel qui simule son comportement.

Un tel logiciel est appelé interpréteur de la p-machine.

Donc dans le cas d'une p-machine le programme source est bien compilé, mais le programme cible est lui interprété par la p-machine.

Beaucoup de langages possèdent, pour une plate-forme fixée, des interpréteurs ou des compilateurs. Moins possèdent une p-machine. Java est l'un de ces langages.

29/286

Figure 25: Portabilité du langage Java.

Page 30: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

3.1. Compilation native et bytecode

Compilation nativeLa compilation native consiste en la traduction du source Java en langage binaire, exécutable sur la plate-forme concernée. Ce genre de compilation est équivalent à n'importe quelle compilation d'un langage dépendant de la plate-forme, l'avantage est la rapidité d'exécution des instructions machines par le processeur central et l'inconvénient qui en découle est la perte de portabilité. BytecodeLa compilation en bytecode (ou pseudo-code) est réalisée par le compilateur javac, qui traduit le programme source XXX.java en un code intermédiaire, indépendant de toute machine physique et non exécutable directement. Le fichier obtenu se dénomme XXX.class. Seule une p-machine, appelée « Machine Virtuelle Java » ou encore JVM (Java Virtual Machine), est capable d'exécuter ce bytecode.

3.2. La machine virtuelle Java

Une fois le programme source traduit en bytecode, la JVM se charge de l'exécuter sur la machine physique à travers son système d'exploitation (Ms-Windows, GNU/Linux, Mac OS X, etc.). Remarque : Les navigateurs Internet modernes intègrent généralement une machine virtuelle qui est donc adaptée au système d'exploitation sur lequel ils s'exécutent.

Fonctionnement élémentaire de la JVM Une machine virtuelle Java contient 6 éléments principaux :

• un jeu d'instructions en pseudo-code ;• une pile d'exécution LIFO (Last In First Out) utilisée pour stocker les

paramètres des méthodes ainsi que leur valeur de retour ;• une pile d'opérandes pour stocker les paramètres et les résultats des instructions du p-code ;• un segment de mémoire appelé tas (heap en

anglais) dans lequel s'effectue l'allocation et la désallocation d'objets ;

• une zone de stockage des méthodes, contenant le p-code de chaque méthode ainsi que son environnement (tables des symboles, etc.) ;

• un ensemble de registres de 32 bits (comme dans un processeur physique) servant à mémoriser les différents états de la JVM ainsi que les informations utiles à l'exécution de l'instruction présente dans le registre « instruction bytecode en cours ». vars : pointe dans la pile vers la première variable locale de la méthode en cours d'exécution. pc : compteur ordinal indiquant l'adresse de l'instruction de p-code en cours d'exécution. optop : sommet de pile des opérandes. frame :

30/286

Figure 26: Compilation native pour une plate-forme déterminée.

Figure 27: JVM

Figure 28: Synoptique de la machine virtuelle Java.

Page 31: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

pointe sur le code et l'environnement de la méthode qui est en cours d'exécution.

Comme tout système informatique, la JVM est basée sur l'architecture de Von Neumann et, elle exécute les instructions séquentiellement.

Exemple : Addition de deux variables entières en Java et en bytecode.

Instructions Java Bytecode (mnémonique) Descriptionint i = 3, j;

j = i + 1;

0x06 (iconst_3)

0x3C (istore_1)

0x1B (iload_1)

0x04 (iconst_1)

0x60 (iadd)

0x3D (istore_2)

Empiler la constante 3 dans la pile d'opérandes.

Désempiler la pile d'opérandes dans la première variable.

Empiler la première variable dans la pile d'opérandes.

Empiler la constante 1 dans la pile d'opérandes.

Empiler la somme des premières valeurs de la pile d'opérandes.

Désempiler la pile d'exécution dans la deuxième variable.

3.3. Les premières notions pour programmer

Tout programme écrit en Java se fonde sur l'utilisation de classes.

Une classe est constituée : d'un ensemble d'attributs (des substantifs explicites) et d'un ensemble de méthodes (des verbes d'action), éventuellement aussi de classes

internes, permettant de définir la structure des objets que l’on souhaite manipuler.

Un programme construit en général des instances de classe.

31/286

Page 32: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

Une instance de classe est appelée objet.

Il est possible de ranger les classes selon des ensembles appelés paquetages.

Il existe des règles de visibilité entre les classes et entre les attributs et méthodes qu'elles contiennent. Ces règles dépendent de l'appartenance

ou non à un même paquetage.

Java est livré avec un ensemble de classes formant ce que l'on appelle l'API (Application Programming Interface). Cette dernière est indispensable et s'élargit continuellement ; la version 5.0 compte par exemple plus de 3000 classes. L'API version 5.0 est consultable en ligne sur le site : http://java.sun.com/j2se/1.5.0/docs.

On pourra entre autres, y trouver les paquetages suivants : • java.lang contient les classes de base du langage (Thread, ClassLoader, Math,

Throwable, etc.), et notamment la classe Object, qui est la super-classe de toutes les autres classes. Une classe qui ne déclare pas être héritée d'une autre, hérite par défaut de la classe Object ;

• java.util définit un certain nombre de classes utilitaires (Date, Hashtable, Properties, Vector, Stack, etc.) et est un complément de java.lang ;

• java.awt (AWT pour Abstract Window Toolkit) contient des classes pour fabriquer des interfaces graphiques portables (Frame, Panel, Button, CheckBox, Label, TextField, etc.) ;

• java.applet est utile pour réaliser des applets, applications utilisables à travers les pages HTML ;

• java.io contient les classes nécessaires à la gestion des flux d'entrées-sorties ;

• java.net fournit une infrastructure pour la programmation réseau ;

• java.sql offre des classes d'accès aux bases de données SQL.

• java.security contient les classes permettant de mettre en place des signatures numériques, des méthodes de cryptographie ou d'authentification ;

• java.rmi définit les classes de manipulation des objets distribués (Remote Method Invocation) ;

• java.beans fournit les classes de création de composants logiciels ;

• javax.swing contient des classes pour fabriquer des interfaces graphiques portables plus évoluées qu'AWT (JFrame, JPanel, JButton, JLabel, JTextField, etc.).

Tous les fichiers sources doivent avoir l'extension .java. Un fichier source peut éventuellement contenir plusieurs classes mais il doit

en contenir au plus une qui porte le modificateur de visibilité public.

32/286

Page 33: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

S'il contient une telle classe, alors le nom du fichier doit être le même que celui de la classe, suivi de l'extension .java.

Les quelques explications ci-dessous concernant les phases de compilation et d'exécution, conviennent lorsque le développeur ne range pas ses programmes en paquetages (voir le chapitre concerné) et, suppose que le JDK de SUN est utilisé. Pour l'utilisation des paquetages, des explications seront fournies plus loin.

Pour compiler, il faut : • se mettre dans le répertoire contenant le fichier source ; • utiliser la commande javac suivie du nom du fichier source : cela crée un fichier pour

chaque classe. Ces fichiers ont pour nom le nom de la classe correspondante, suivi de l'extension .class.

Pour exécuter, il faut : • avoir un fichier contenant une classe possédant une méthode main() ; • entrer la commande java suivi du nom, sans extension, de la classe contenant le

main().

4. Un premier programmeNotre premier exemple :

• se trouve dans le fichier : Essai.java ;• est compilé par la commande : javac Essai.java qui créé un fichier

Essai.class ;• est exécuté avec la commande : java Essai.

La méthode main() se présente toujours comme ci-dessous. Elle est obligatoirement définie à l'intérieur d'une classe spécifiée par le mot réservé class et précédée du mot réservé public. Mentionnons-le tout de suite, une classe est un substantif qui débute toujours par une majuscule !

/* * Fichier Essai.java */

/** * Cette classe se contente d'afficher sur la console le message : * "Mon premier programme Java !". * @author Alain Lebret */public class Essai { /** * Définition de la méthode statique main(), point d'entrée du * programme. */ public static void main ( String1 [] arguments ) {

1 il s'agit d'une classe définie dans le paquetage java.lang. La méthode main() a pour paramètre un tableau contenant les chaînes de caractères passées en arguments au programme.

33/286

Page 34: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

System2.out3.println4("Mon premier programme Java !"); // Affichage }}

On obtient à l'exécution : java Essai Mon premier programme Java !

En observant le code précédent, nous pouvons noter que certaines parties sont en italique et correspondent à des commentaires. Ceux-ci permettent de placer de l'information au sein d'un programme. Cette information n'est pas significative au niveau de l'exécution du programme, mais prend tout son sens lors de la compréhension de ce dernier.

Aussi est-il nécessaire de lire avec attention le paragraphe suivant !

5. Génération automatique de la documentationDans le cycle de vie normal d'un développement d'application, il n'y a pas que des phases de programmation. En effet, professionnellement parlant, il doit y avoir une phase de documentation du code produit. Cette phase est parfois négligée, ce qui peut poser des problèmes dans une équipe de développement. Aujourd'hui il est impératif de documenter le code durant la phase de conception, et non plus après. En Java, ce conseil est d'autant plus intéressant du fait que le JDK propose l'outil javadoc. Cet outil analyse le code source Java et en extrait des informations afin d'en générer une documentation au format HTML. Outre les informations purement liées au code lui-même, cet outil sait aussi récupérer des commentaires que l'on se doit d'ajouter à notre code. Cette pratique d'ajouter des commentaires au code source, à un double avantage : elle permet de compléter la documentation générée par javadoc et de donner en plus, une double formulation quant à la fonctionnalité ou à l'utilité de l'élément commenté, qu'il soit classe, attribut ou méthode. En effet, on obtient ainsi une formulation informelle grâce aux commentaires, ainsi qu'une formulation formelle grâce au code lui-même. La relecture des deux formulations permet alors de détecter des contradictions éventuelles. Afin de mieux comprendre les choses, nous allons commencer par tester les fonctionnalités de base de javadoc.

5.1. L'outil javadoc

Cet outil est fourni par défaut avec le JDK et permet de générer des fichiers de documentations à partir du code Java. Il attend qu'on lui spécifie un ensemble de fichiers Java à documenter, pour en générer une structure de navigation au travers de toutes les classes documentées. Le fichier de départ de cette navigation se nomme index.html.

5.1.1. Réalisation d'une documentation simple

Nous allons utiliser le code des classes du TP N°1 qui se trouve à la fin de ce chapitre. L'exemple qui suit montre comment générer la documentation en utilisant javadoc.

2 classe de java.lang qui fournit quelques fonctionnalités systèmes, indépendantes de la plate-forme. 3 instance de la classe java.io.PrintStream qui sera renseignée plus tard.4 on utilise ainsi une méthode de la classe PrintStream, qui affiche un message sur le flux de sortie standard (le terminal).

34/286

Page 35: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

javadoc AppliBouton.java Delegue.java Essai.java

Le point d'entrée de la documentation produite est le fichier index.html et en suivant ses liens, nous pouvons en consulter l'ensemble. Comme il est possible de le remarquer, cette documentation est structurée de la même manière que la documentation du JDK, qui elle aussi a été générée par javadoc. La documentation produite doit contenir à priori tout ce qui est nécessaire aux utilisateurs des classes.

5.1.2. Utilisation des commentaires javadoc

javadoc n'extrait du code Java d'une classe, qu'une partie des informations. Le reste est issu de commentaires pris en charge par l'outil et à destination des utilisateurs de la classe. Le programmeur peut souhaiter laisser des commentaires qui facilitent la compréhension du code mais sans forcément vouloir qu'ils apparaissent au niveau de l'aide. Pour régler le problème, les commentaires javadoc se différencient des commentaires traditionnels, étant entendu que pour le compilateur, il n'existe que deux types de commentaires (les commentaires javadoc étant dérivés des commentaires du langage C). L'exemple suivant montre les types de commentaires supportés :

/* Ceci est un commentaire classique hérité du langage C Il peut s'étendre sur plusieurs lignes.*/

// Ceci est un commentaire classique hérité de C++// Ce type de commentaire ne peut pas s'étendre sur plusieurs lignes

/** Et là nous avons un commentaire javadoc. Comme nous le * remarquons, il commence par les caractères /**. * Il peut s'étendre sur plusieurs lignes */

Les commentaires javadoc peuvent contenir des sections spéciales permettant d'ajouter des informations supplémentaires sur les paramètres des méthodes, les valeurs de retour, les exceptions, etc..

5.1.3. Profiter du format HTML

Une autre caractéristique de javadoc est qu'il génère du code HTML. Il est donc possible dans le texte des commentaires, d'insérer des balises HTML. Ainsi, une fois l'aide présentée dans le navigateur, les balises sont traitées. Des images, des liens, des tableaux, etc., peuvent donc être inclus pour améliorer la lisibilité. L'exemple suivant permet de mettre l'accent sur certains points en les affichant en gras (balises <b> et </b> en HTML).

/** * <p>Classe <b>héritant</b> de <code>JFrame</code>. Se compose d'une * fenêtre avec deux boutons ainsi que d'une zone de texte.</p> * * @see Delegue * @version 1.1 * @since 1.0 * @author Alain Lebret */

35/286

Page 36: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

5.2. Quelques instructions javadoc

Nous avons indiqué plus haut, qu'il était possible d'insérer des sections spéciales dans les commentaires javadoc. Il faut pour cela utiliser des instructions commençant toutes par le caractère @. Nous en présentons quelques unes ci-dessous.

5.2.1. Documentation de la classe

javadoc vous permet de définir plusieurs informations relatives à une classe :• l'instruction @see permet de faire référence à d'autres classes liées à la présente ;• l'instruction @since permet de dire à partir de quelle version du paquetage, la classe à été

définie ;• le numéro de version courante ainsi que l'auteur de la classe peuvent être indiqués via les

instructions @version et @author. Dans ce dernier cas, il faut rajouter des options lors de l'appel de la commande javadoc : javadoc -author -version fichiers...

5.2.2. Documentation des méthodes

Lors de la définition des méthodes, il est nécessaire d'être très généreux en commentaires en se mettant à la place du lecteur !

• la version du paquetage à partir de laquelle la méthode est supportée, peut être fournie grâce à l'instruction @since ;

• chacun des paramètres d'appel de la méthode peut être documenté avec l'instruction @param ;

• la valeur de retour de la méthode est documentée à l'aide de l'instruction @return. ;• la création de liens hypertextes est réalisable avec l'instruction @see. Cette dernière permet

la liaison à une autre classe, ou plus précisément à une des méthodes de cette autre classe. La syntaxe prend un paramètre qui correspond à l'élément lié, comme par exemple @see String#equals pour rattacher le commentaire à la méthode equals() de la classe String.

/** * Méthode d'<code>ActionListener</code> gérant les actions de * l'utilisateur. * @param e événements que le <b>délégué</b> doit prendre en compte. * Il est transmis par le système d'exploitation à l'interface * graphique du programme. * @return rien * @see ActionListener */

Deux autres points sont à connaître. Tout comme dans le JDK, les méthodes dépréciées5 ainsi que les exceptions déclenchées peuvent être documentées. Pour documenter une méthode dépréciée, il faut utiliser l'instruction @deprecated. Pour documenter une exception, il faut utiliser l'instruction @throws.

/** * Explications sur la méthode * @throws MonException pour faire un test * @deprecated Parce que c'est comme ça !

5 Se dit d'une méthode qui n'est plus utilisée mais reste utilisable pour garantir la compatibilité ascendante.

36/286

Page 37: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

*/public void operer() throws MonException { // . . .}

Le fait de documenter les programmes au fur et à mesure de leur élaboration, permet de générer une documentation conséquente. Il est vrai que certains autres outils en font de même, et ce, directement durant la phase de modélisation du logiciel (ArgoUML, Rational Rose, etc.).

6. ExercicesExercice 2.1Quel est le rôle de la variable d’environnement CLASSPATH que l’on définit dans AUTOEXEC.BAT (ou .bash_profile sur GNU/Linux) ?

(a) elle définit le chemin d’accès aux fichiers sources *.java utilisés ;(b) elle définit le chemin d’accès aux programmes exécutables (javac, java, jdb, etc.) ;(c) elle définit le chemin d’accès aux fichiers compilés *.class utilisés.

Exercice 2.2Le compilateur Java produit un code appelé bytecode (contenu dans un fichier .class) et exécuté par la machine virtuelle Java. Quel programme remplit le rôle de machine virtuelle, et à quelle catégorie appartient-il ?

(a) le programme est java et il appartient à la catégorie des traducteurs ;(b) le programme est java et il appartient à la catégorie des interpréteurs ;(c) le programme est javac et il appartient à la catégorie des interpréteurs.

Exercice 2.3 Quelle syntaxe permet l’exécution de l’application AppliBouton ?

(a) java AppliBouton.class(b) javac AppliBouton(c) java AppliBouton

7. Séance de TP N°1 : INSTALLATION ET CONFIGURATION DE LA PLATE-FORME JAVA

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

37/286

Page 38: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

Temps nécessaire 4h

Objectif : Mettre en oeuvre les outils du kit de développement Java.

7.1. Installation et configuration du JDK 5.0

1. Installer, si ce n'est déjà fait, à partir du CD-ROM ou d'Internet, le JDK en fonction du système d'exploitation utilisé.

2. Vérifier que le fichier .bash_profile (ou autoexec.bat sur Ms-Windows) contient les commandes de configuration du JDK.

3. Mettre en place une arborescence de travail, par exemple : ./java/tp1/, ./java/tp1/src, ./java/tp1/class et ./java/tp1/doc qui permettra le stockage des codes sources, codes compilés et documents HTML.

4. Tester l'existence des outils du JDK depuis la console (javac, java, javadoc, javac, javah, javap et jar).

7.2. Mise en oeuvre du JDK 5.0

1. Édition et Compilation • Récupérer le guide francophone des conventions de codage :

ftp://ftp-developpez.com/cyberzoide/java/JavaStyle.pdf • Éditer les fichiers sources AppliBouton.java, Delegue.java et Essai.java

dont le code source est fourni ci-après, puis les compiler. • Noter avec un éditeur hexadécimal (ghexedit par exemple) les premiers octets de

chacun des fichiers « .class ». • Relever la taille de chaque fichier. • Compiler les fichiers avec l'option de débogage et noter à nouveau leur taille. • Réitérer avec l'option d'optimisation. Comparer et commenter.

2. Exécution • Exécuter Essai.class. Qu’observe-t-on ? • Tester la portabilité des fichiers « .class » sur un autre OS.

3. Génération de documentation • Lancer le générateur javadoc avec comme paramètres les noms des fichiers

AppliBouton.java, Delegue.java et Essai.java. Visualiser la structure des documents produits.

• Supprimer tous les mots réservés public se trouvant avant les méthodes puis les remplacer par private. Régénérer la documentation. Que remarque-t-on ? Quelle option de l'outil permet une génération correcte ?

• Visualiser la structure des documentations après avoir enlevé les commentaires.

7.3. Travail sur les codes sources

1. Modification des paramètres de la fenêtre • Changer le nom de la fenêtre. Recompiler et tester. • Changer le nom et le label des deux boutons pour qu'ils correspondent mieux à leur

38/286

Page 39: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

utilisation. Recompiler et tester. • Changer la taille de la fenêtre. Recompiler et tester. • Changer la position de la fenêtre (méthode setLocation(newX, newY) appelée comme

setSize(..)). Recompiler et tester.

2. Passage de la bibliothèque SWING à AWT • Modifier JFrame en Frame et JButton en Button puis supprimer les occurrences de la

méthode getContentPane(). Recompiler et tester. Que remarque-t-on ? • Tester à nouveau la portabilité. Conclure.

7.4. Conditions

• JDK version 1.5.0_X et sa documentation en ligne; • Usage des outils javac, java, javadoc, jdb, jar, javap, javah ;• Codes sources : /* * AppliBouton.java * * Lycée Diderot * Département Informatique Industrielle * 61, rue David d'Angers * 75019 Paris * * TP - Session 2006/07 * Copyright (c) 1996/2006, A. Lebret */import java.awt.*;import javax.swing.*;import java.awt.event.*;

/** * <p>Classe <b>héritant</b> de <code>JFrame</code>. Se compose d'une * fenêtre avec deux boutons ainsi que d'une zone de texte.</p> * * @see javax.swing.JFrame * @see javax.swing.JButton * @see java.awt.FlowLayout * @version 1.0 * @author Alain Lebret */public class AppliBouton extends JFrame { /** Création d'un bouton de label OK */ JButton boutonOK = new JButton("OK");

/** Création d'un bouton de label Annuler */ JButton boutonAnnuler = new JButton("Annuler"); /** Constructeur par défaut */ public AppliBouton() { // Objet de gestion des événements Delegue delegue = new Delegue(this); // Mise en place des composants avec un FlowLayout this.getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER,5,5));

this.getContentPane().add(boutonOK); // Ajout du bouton OK this.getContentPane().add(boutonAnnuler); // Ajout du bouton Annuler boutonOK.addActionListener(delegue); // Ajout des boutons dans la liste boutonAnnuler.addActionListener(delegue); // des composants gérés par // le délégué; }

39/286

Page 40: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

} // Fin AppliBouton

/* * Delegue.java * * Lycée Diderot * Département Informatique Industrielle * 61, rue David d'Angers * 75019 Paris * * TP - Session 2006/07 * Copyright (c) 1996/2006, A. Lebret. */import java.awt.*;import java.awt.event.*;

/** * Classe implémentant l'interface ActionListener. Sert de Delegue * à la classe AppliBouton pour la gestion des actions sur les boutons. * Le délegué écoute les événements sur les objets dont il a la charge. * * @see java.awt.event.ActionListener * @see java.awt.Graphics * @version 1.0 * @author Alain Lebret */public class Delegue implements ActionListener { /** Instance de la classe AppliBouton */ AppliBouton application;

/** * Constructeur par défaut. * @param application Référence de l'objet uneApplication */ public Delegue(AppliBouton uneApplication) { this.application=uneApplication; }

/** * Méthode d'ActionListener gérant les actions utilisateur * @param e événements que le délégué doit prendre en compte. * Il est transmis par le système d'exploitation à l'interface * graphique de notre programme. * @return rien */ public void actionPerformed(ActionEvent e) { Graphics g = application.getGraphics();

if (e.getSource() == application.boutonOK) { g.setColor(Color.red); g.drawString("Vive la TSIRIS1 !", 60,100); } else if (e.getSource()==application.boutonAnnuler) { g.setColor(application.getBackground()); g.drawString("Vive la TSIRIS1 !", 60,100); } }} // Fin Delegue

/* * Essai.java * * Lycée Diderot

40/286

Page 41: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 2 - Introduction au Langage Java

* Département Informatique Industrielle * 61, rue David d'Angers * 75019 Paris * * TP - Session 2006/07 * Copyright (c) 1996/2006, A. Lebret */

/** * Classe de Test * @version 1.0 * @author Alain Lebret * */public class Essai { /** * Essai de la classe AppliBouton * @param arguments tableau de chaînes de caractères * @return rien */ public static void main(String[] arguments) { AppliBouton monCadre = new AppliBouton(); monCadre.setTitle("TP java N°1"); monCadre.setSize(200,200); monCadre.setVisible(true); }} // Fin Essai

41/286

Page 42: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 3 - Les types primitifs

CHAPITRE 3 - LES TYPES PRIMITIFS

Java possède un certain nombre de types de base, mais contrairement aux autres langages et pour en garantir la portabilité, il impose que la représentation interne de ces types occupe toujours un même espace mémoire et ce, quelle que soit la machine sur laquelle tourne le programme.

1. La déclaration des variablesRappelons qu'une variable est en quelques sorte la carte d'identité d'une donnée d'un programme. Cette carte d'identité est totalement définie par la connaissance des informations suivantes :

• le type de la donnée : Il correspond à l'ensemble des valeurs que pourra prendre une donnée qui aura été définie à partir de celui-ci. Par exemple, le type entier, définit un ensemble de valeurs numériques, non décimales, comprises entre une borne minimale et une borne maximale ;

• le nom de la variable : ce nom permet d'identifier la donnée dans le programme : on emploie aussi le terme étiquette ou label. En l'utilisant, on pourra alors affecter à la donnée mentionnée, une valeur bien entendu contenue dans l'ensemble des valeurs définies par le type. Le choix de ce nom est important quant à la lisibilité du code, et les substantifs utilisés doivent être pertinents ;

• une éventuelle valeur initiale : pour pouvoir correctement utiliser cette donnée, il pourra être judicieux de lui affecter une valeur initiale. Par la suite, la valeur de la variable pourra malgré tout changer, sauf dans quelques cas précis.

Bien entendu, il faut, pour que le compilateur comprenne ce qu'on veut lui dire, que la définition des variables respecte une certaine syntaxe :

• toute déclaration débute en mentionnant le type utilisé pour la (ou les) variable(s) considérée(s). Ensuite, on faire suivre le nom de chaque variable à définir en les séparant par des virgules s'il y en a plusieurs. Un point-virgule final permet de terminer la déclaration ;

• pour chaque variable, on peut mentionner la valeur initiale en utilisant l'opérateur d'affectation = suivi de la valeur. Le compilateur fera alors attention à ce que chaque valeur soit compatible avec le type utilisé.

Attention, car le langage fait la distinction entre minuscules et majuscules. La variable nommée indexDuTableau est

donc distincte de celle nommée IndexDuTableau.

Saisir l'exemple de code suivant dans un fichier nommé Variables.java. Le compiler, puis le tester afin de bien comprendre comment une variable est définie en Java. Le tester avec d'autres valeurs, comme par exemple en forçant l'affectation de la valeur 10.4 à la variable nombreDeFigures. Que peut-on en conclure ?

public class Variables {

public static void main( String params[] ){ // Définition de deux variables entières nombreDePages et

42/286

Page 43: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 3 - Les types primitifs

// nombreDeFigures. nombresDePages i sera de plus initialisée // avec la valeur 217. int nombreDePages = 217, nombreDeFigures;

// Définition de la chaîne de caractères avec // comme valeur initiale "Cours Java" String titreDuCours = "Cours Java";

// On donne une valeur à nombreDeFigures nombreDeFigures = 10;

// On utilise ces variables en les affichant System.out.println("Nombre de pages = " + nombreDePages + " et Nombre de figures = " + nombreDeFigures); System.out.print("Titre du cours : " + titreDuCours + "\n"); }}

Nous déclarons et initialisons dans ce programme deux variables int. Nous remarquons que :• les déclarations des deux variables sont séparées par une virgule ; • on utilise le signe d'affectation (=) pour initialiser aussitôt la première variable

nombreDePages avec la valeur 217. Notons que nous pouvons faire la déclaration de nombreDeFigures sur la première ligne et ne l'initialiser que par la suite ;

• la méthodes println() permet d'afficher une chaîne de caractères sur la sortie standard, puis d'effectuer automatiquement un passage à la ligne. Remarquons que nous affichons deux variables à l'intérieur de la même chaîne à l'aide de l'opérateur + permettant, entre autre, la concaténation de chaînes de caractères ;

• la méthode print(), n'effectue pas de retour à la ligne. Celui-ci est obtenu par la séquence dite d'échappement '\n' terminant la chaîne.

2. Portée d'une variableLa portée d'une variable est, par définition, la partie du code où cette variable est « visible » et donc utilisable. En Java une variable n'est visible qu'à l'intérieur du bloc entre accolades dans lequel elle est déclarée, ainsi que dans les blocs qui y sont imbriqués. Mais attention, si deux variables de même nom sont visibles en même temps pour une partie du code, seule la variable la plus interne sera utilisée.

Par exemple : int nombreDePages = 1; { int nombreDePages = 2; System.out.println(nombreDePages); // 2 s'affiche } System.out.println(nombreDePages); // 1 s'affiche

3. Les types numériquesLe langage propose un certain nombre de types numériques. On peut classer ces types en deux catégories : les types entiers et les types décimaux. Ce qui différencie les types d'une même catégorie, c'est la taille de mémoire utilisée pour contenir

43/286

Page 44: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 3 - Les types primitifs

une valeur. Plus cette taille est grande et plus les valeurs utilisables sont nombreuses. Il est donc nécessaire de choisir un type en fonction de l'étendue de valeurs que l'on souhaite pour une variable.

3.1. Les types numériques entiers

Il existe quatre types d'entiers en Java. La différence réside dans la taille nécessaire pour stocker la valeur.

a) Le type byte

La variable de type byte prend un octet de mémoire, soit huit bits. Le premier bit est appelé bit de poids fort, c'est lui qui représente le signe (+) quand il vaut 0 et le signe (-) lorsqu'il vaut 1. Les sept autres servent au codage de la valeur. La plus grande valeur que nous pourrons coder est composée du zéro, qui donne à l'entier une valeur positive, et de sept fois le 1 : 0111 1111 ⇔ 127 en décimal

Pour le convertir en décimal on multiplie chacun des sept 1 par une puissance croissante de 2, en commençant par 0 :

20+ 21+ 22+ 23+...+ 26 = 27 – 1 = 127 (comptons : il y en a 7)

0111 1111 ⇔ 127 = 20 +...+ 26 .... .... et ainsi jusqu'à0100 0000 ⇔ 64 = 26

.... ....0000 1000 ⇔ 8 = 23

.... ....0000 0001 ⇔ 1 = 20 puis0000 0000 ⇔ 0

Maintenant se pose le problème de la représentation des entiers négatifs. Nous avons choisi le système du complément à deux. Qu'est-ce à dire?

Prenons la valeur de 1 sur deux octets : 0000 0001Changeons les 1 en 0 et les 0 en 1 : 1111 1110Ajoutons la valeur binaire 1 : + 0000 0001Additionnons : = 1111 1111

Nous obtenons la valeur inverse de 1, c'est-à-dire -1.

1111 1111 ⇔ -1 et de même1111 1110 ⇔ -2 .... ....1111 1000 ⇔ -8 .... ....1000 0000 ⇔ -128

Nous avons pu ainsi représenter l'ensemble des entiers entre -128 et +127. C'est le domaine des byte.

domaine des byte { -128, 127 }

44/286

Page 45: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 3 - Les types primitifs

b) Le type short

La variable de type short prend deux octets de mémoire, soit 16 bits. Avec un espace de 16 bits, en gardant le bit de poids fort pour le signe, il est possible de représenter 215- 1 = 32767 valeurs positives et autant, plus une, valeurs négatives, ce qui nous donne :

domaine des short { -32768, 32767 }

c) Le type int

Avec un espace de 32 bits, en gardant le bit de poids fort pour le signe, il est possible de représenter 231- 1 = 2 147 483 647 valeurs positives et autant, plus une, valeurs négatives, ce qui nous donne :

domaine des int { -2 147 483 648, 2 147 483 647 }

d) Le type long

Avec un espace de 64 bits, en gardant le bit de poids fort pour le signe, il est possible de représenter 263- 1 = 9 223 372 036 854 775 807 valeurs positives et autant, plus une, valeurs négatives, ce qui nous donne:

domaine des long { -9 223 372 036 854 775 808, +9 223 372 036 854 775 807 }

3.2. Les types numériques décimaux

3.2.1. Pourquoi les machines calculent-elles toujours faux ?

Les nombres réels sont ceux que nous utilisons en algèbre. On les dit en « virgule flottante », puisqu'un nombre réel peut toujours s'écrire en représentation décimale, de façon unique sous la forme approchée :

0.xxxxxxxxx * 10y

Exemple :1024.000 = 0.1024 * 104 ou bien 0.001024 = 0.1024 * 10-3 la virgule « flotte ».

- y est l'exposant, ici y = -3 ;- la suite des x derrière le 0. est la mantisse : 1024 ; 0.1024 = 1 * 1/10 + 0 * 1/100 + 2 * 1/1000 + 4 * 1/10000- la mantisse sera formée, derrière le 0, des quatre premiers x : 1024. Chacun des chiffres de la mantisse est compris entre 0 et 9 dans la numérotation décimale. Il est associé à une puissance de 1/10.

Dans les machines, en représentation binaire ce nombre réel est converti en base 2 et devient:

45/286

Page 46: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 3 - Les types primitifs

0.zzzzzzzzz * 2t

- chacun des chiffres z est un 1 ou un 0 et il est associé à une puissance de 1/2 ;- l'exposant est t, une puissance négative de 2. nombre = (1er z) * 1/21 + (2ème z) * 1/22 + (3ème z) * 1/23 +...

Exemples :1 = 1/2 * 2 = (1/2)1 * 21 mantisse : le 0. et 1 fois 1/2 soit = 0.100000000exposant : la puissance de 2 qui est ici = 1

de la même façon:2 = 1/2 * 4 = (1/2)1 * 22

mantisse: 0.100000000exposant : 11 qui est la traduction de 2 en binaire

3.5 = 0.875 * 22 on décompose 0.875 en puissances de 1/2:0.875 = 0.50 + 0.25 + 0.125 = (1/2)1 + (1/2)2 + (1/2)3

mantisse en binaire : 0.1110 0000 0000 exposant : 11 qui traduit le 2 décimal.

Nous avons choisi pour nos exemples des cas simples. En général, la suite des puissances de 1/2 nécessaire pour recomposer la mantisse est infinie alors que la capacité des machines ne l'est pas !

Exemple :1/20 = 0.8 * 2-4 en binaire la mantisse est une suite infinie: 0.1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100...

Il faut donc tronquer cette suite et nécessairement introduire une erreur d'arrondi.

Si, dans une boucle de calcul, on refait 1000 fois une opération, l'erreur sera en général égale à 1000 fois l'arrondi. Il faut par exemple faire très attention lorsqu'on compare deux variables théoriquement égales car elles seront généralement différentes à cause des arrondis. En pratique on comparera leur différence à une valeur epsilon négligeable par rapport aux variables. Dans ce cas, epsilon est le « zéro numérique ».

3.2.2. Les deux types de réels : float et double

Les types numériques décimaux proposés par Java et au nombre de deux, respectent le standard IEEE 754. C'est-à-dire que sont définies deux valeurs pour le zéro ( ±0 ), deux valeurs pour l'infini ( ±∞ ) ainsi qu'une valeur appelé NaN (Not a Number) permettant de représenter le résultat d'opérations illégales telle qu'une division de zéro par zéro.

46/286

Page 47: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 3 - Les types primitifs

a) Le type float

Il occupe quatre octets de mémoire soit 32 bits (1 + 8 + 23). signe (1) exposant (8) mantisse 23

domaine des

float{ - 3.40* 1038, -1.21* 10-38 } + { 0 }

+ { 1.21* 10-38, 3.40* 1038 }

La longueur de la mantisse permet, dans des conditions normales, de garder six chiffres significatifs.

b) Le type double

Il occupe huit octets de mémoire soit 64 bits (1+ 11+ 52).signe (1) exposant (11) mantisse 52

domaine des double

{ -1.79*10308, -2.23*10-308 } + { 0 } + { 2.23*10-308, 1.79*10308 }

La longueur de la mantisse permet, dans des conditions normales, de garder quatorze chiffres significatifs.

4. Le type booléenOn introduit une variable de ce type avec le mot réservé boolean. Ce type accepte seulement deux états : l'un est nommé true et symbolise un état d'acceptation, l'autre, nommé false, symbolise un état de réfutation. Attention, contrairement au langage C, le type booléen n'est pas en Java, un sous-type numérique. En effet, en langage C, la valeur 0 est considérée comme fausse et les autres valeurs entières comme vraie.

5. Le type caractèreCe type, introduit par le mot réservé char, permet la gestion des caractères. Jusqu'à présent, la plupart des langages utilisaient à cet effet le codage ASCII ou l'un de ses dérivés. Le codage ASCII ne permet de représenter que 128 caractères : en effet à l'origine, seul les sept bits de poids faibles d'un octet servaient au codage du caractère, le huitième servait alors de bit de parité assurant le bon transport du caractère à travers un réseau. Par la suite, des couches de protocole de transport de données plus sûres ont rendu ce huitième bit utilisable. Différents systèmes dérivés d'ASCII ont fait leur apparition comme la table de codage ISO-Latin-1. Malheureusement, aucun de ces standards n'a su s'imposer de manière universelle, et l'échange de données entre différents systèmes reste encore aujourd'hui très problématique. N'avez-vous jamais reçu un courriel parasité aux niveaux des caractères accentués, car ces derniers n'appartiennent pas à la table ASCII ?Pour palier les problèmes engendrés, un nouveau standard de codage de caractères, universel, fut proposé, il s'agit du codage Unicode. L'Unicode utilise 16 bits pour représenter un caractère et permet donc potentiellement, de coder jusqu'à 65536 caractères et donc à fortiori, nos caractères accentués ainsi que d'autres caractères provenant d'autres alphabets (Cyrillique, Hébraïque, Arabe,

47/286

Page 48: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 3 - Les types primitifs

Chinois, Grec, etc.). Pour un aperçu de la table Unicode, il suffit de suivre ce lien.

Les concepteurs de Java, dans un souci de portabilité du code produit, ont donc trouvé judicieux d'utiliser le standard Unicode. Les exemples qui suivent indique la syntaxe à utiliser pour écrire un caractère en Java.

'a' '\t' pour une tabulation '\u06F3' le chiffre arabe ۳ (3)

6. Tableau récapitulatifLes types élémentaires ainsi que leurs caractéristiques sont résumés dans le tableau ci-dessous :

Type ContenuValeur initiale

Taille (bits) Intervalle de valeurs

boolean true ou false false 1 -

byte entier signé 0 8 -128 à 127

char caractère Unicode \u0000 16 \u0000 à \uFFFF

short entier signé 0 16 -32768 à 32767

int entier signé 0 32 -2147483648 à 2147483647

long entier signé 0 64-9223372036854775808 à 9223372036854775807

floatnombre décimal à virgule flottante en

simple précision0.0 32

+/- 3.40282347E+38 à +/- 1.40239846E-45

doublenombre décimal à virgule flottante en double précision

0.0 64+/- 1.79769313E+308 à +/-

4.94065645E-324

Remarque : Les types primitifs Java possèdent tous une classe enveloppe (wrapper) associée, permettant de travailler avec les concepts objets et de faciliter ainsi certaines opérations. Nous les rencontrerons partiellement dans les séances de TP.

Type Classe Enveloppante Type Classe Enveloppanteboolean Boolean int Integerbyte Byte long Longchar Character float Floatshort Short double Double

7. ExercicesExercice 3.1Donner le type et le nom des variables destinées à recevoir les informations suivantes :

(a) l'âge d'une personne ;(b) l'adresse d'un port d'entrée/sortie sur un PC ;(c) la surface d'un jardin ;(d) le nombre d'étoiles de notre galaxie ;

48/286

Page 49: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 3 - Les types primitifs

(e) la pluviométrie moyenne de janvier ;(f) la longueur de l'intestin grêle.

49/286

Page 50: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

CHAPITRE 4 - OPÉRATEURS ET EXPRESSIONS DU LANGAGE

Java manipule des expressions et donc des opérateurs de différentes natures. Nous allons au travers de ce chapitre, étudier l'ensemble des opérateurs du langage ainsi que les différentes natures d'expressions.

1. Les expressions numériques

1.1. Introduction

Les opérateurs numériques calculent tous un résultat numérique. Cependant, il faut bien noter qu'il existe plusieurs opérateurs utilisant un même symbole, par exemple +, mais qui s'appliquent sur des types différents. Par exemple :

4 + 3;

Ici, l'opérateur binaire + prend deux entiers représentés sur 32 bits comme opérandes, et rend un entier, lui aussi représenté sur 32 bits.

3.2 + 1.84;

Ici par contre, il prend deux opérandes de types décimaux représentés sur 64 bits, et retourne un décimal du même type.

En règle générale, il y a autant d'opérateurs + que de types numériques. Mais la question est alors de savoir ce qui doit se passer si l'on tente d'ajouter un entier à un décimal. Il est clair que le résultat doit aussi être décimal. En conséquence, c'est le deuxième cas donné en exemple qui s'applique, avec une phase implicite de conversion de la valeur entière en valeur décimale.

Une conversion d'un type vers un autre s'appelle un transtypage (cast).

Un transtypage est implicitement effectué s'il est nécessaire, et s'il n'y a pas de perte de précision sur la valeur

numérique. Le transtypage implicite d'un décimal vers un entier ne sera

donc jamais possible et il faudra alors explicitement le demander.

L'exemple qui suit indique la syntaxe à utiliser. Il suffit pour cela de préfixer la valeur par le type mis entre parenthèses.

4 + ( int )3.8;

50/286

Page 51: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

Dans ce cas, l'opérateur + prend deux entiers représentés sur 32 bits et rend un entier, lui aussi représenté sur 32 bits. Le résultat est 7 car 3.8 donne 3 après le transtypage.

1.2. Expressions

Une expression est constituée d'opérateurs et de sous-expressions, comme le montre l'exemple suivant.

3 * 6 + 2 * ( 9 / 7 );

1.3. L'opérateur d'affectation =

Une affectation est aussi une expression qui calcule ou non une valeur et l'affecte à une variable. La valeur d'une telle expression est celle calculée dans la partie droite de l'affectation (les opérateurs d'affectation étant binaires infixes). L'opérateur permettant de définir une affectation sera noté =.

Ce n'est surtout pas un symbole d'égalité !

test = 5; /* la valeur 5 est affecté à la variable test. */5 = test;///////// /* est illégal, on n'affecte pas une variable au nombre 5.*/

Le membre à droite doit être une valeur ou une expression calculée. Quant au membre à gauche, il doit être impérativement un élément modifiable comme par exemple une variable, on dit qu'il s'agit d'une « lvalue » (contraction de left value).

test + 5 = y - 2; /* est rejeté car le membre de gauche n'est pas une lvalue */

x = y = z = 0; /* est correct si les variables sont du même type. L'associativité se fait de droite à gauche. */

Mais on ne peut pas user de cette facilité dans une déclaration :double x = y = z = 3; /* est rejeté */

Exemple :

public class EssaiAffectation { public static void main(String args[]){ int valeur; System.out.println( "Premier = " + ( valeur = 10 ) * 2 + ";\t" + "Second = " + valeur + ";"); }}

Le résultat est : Premier = 20; Second = 10;

51/286

Page 52: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

1.4. Les opérateurs numériques

Nous trouvons notamment les opérateurs arithmétiques qui s'appliquent sur des opérandes numériques quelconques (entiers ou décimaux) :

Opérateur Description

+ addition

- soustraction

* multiplication

/ division

On trouve aussi des opérateurs de manipulation de bits qui ne s'appliquent uniquement qu'à des opérandes entiers :

Opérateur Description Remarque

% reste de la division entière (modulo)

& ET bit à bit

| OU inclusif bit à bit

^ OU exclusif bit à bit

<< décalage à gauche bit à bit Le décalage d'un bit à gauche est équivalent à une multiplication par 2.

>> décalage à droite bit à bit Le décalage d'un bit à droite est équivalent à une division par 2.

Enfin, nous trouvons aussi deux opérateurs permettant d'incrémenter (++) ou de décrémenter (--) d'une unité, une variable numérique (entière ou flottante). Il faut noter que ce sont deux opérateurs unaires qui ne sont utilisables qu'en préfixe ou en postfixe d'une variable.

Exemple 1 : Utilisation des opérateurs ++ et --public class EssaiOperateurs { public static void main( String args[] ){int resultat = 10;System.out.println("resultat = " + resultat + ";\t(++resultat*2) = " + ( ++resultat * 2 ) + ";\tresultat = " + resultat + ";");resultat = 10;System.out.println("resultat = " + resultat + ";\t(resultat++*2) = " + ( resultat++ * 2 ) + ";\tresultat = " + resultat + ";"); }}

Qui produit le résultat suivant : resultat = 10; (++resultat*2) = 22; resultat = 11; resultat = 10; (resultat++*2) = 20; resultat = 11;

Donnons quelques explications :

52/286

Page 53: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

• le programme débute par la définition d'une classe EssaiOperateurs dans laquelle nous spécifions la seule méthode qui doit servir de point de départ à l'application. Nous reviendrons ultérieurement sur la nécessité du mot réservé static, mais indiquons dès à présent qu'il est obligatoire pour la méthode main() ; idem pour le mot réservé public ;

• la ligne suivante permet de créer une variable nommée resultat à laquelle on affecte la valeur 10.

• ensuite, tout se complique : System est une classe de l'API Java qui permet d'avoir accès au système (comme son nom l'indique). Cette classe contient dans ses attributs un objet nommé out qui lui donne accès au flux de sortie standard de l'application (le terminal) ; nous y appliquons la méthode println() qui y place un message passé en paramètre suivi d'un retour chariot. Le message commence par une chaîne de caractères suivit de l'opérateur +. Il est évident qu'il ne s'agit donc pas de l'opérateur dont nous avons déjà parlé plus haut. Ici, celui-ci réalise la concaténation de deux chaînes de caractères. Et pourtant, ce qui suit n'est pas une chaîne de caractères ! Mais en Java, tout peut s'exprimer sous cette forme ! Comme il n'y a pas, à priori, de perte de données, le transtypage d'un nombre en une chaîne de caractères est accepté ! Nous trouvons ensuite une autre chaîne de caractères contenant les deux caractères \t : ils servent à placer une tabulation dans la chaîne. Ensuite nous trouvons l'opérateur ++. Lors de sa première apparition, il est utilisé en préfixe, tandis qu'il l'est en postfixe dans sa seconde. Les résultats affichés par l'appel à System.out.println() nous indiquent que la valeur calculée n'est pas la même, bien que l'incrémentation de la variable resultat soit faite. La raison en est que dans le premier cas, nous incrémentons la variable puis terminons le calcul, alors que dans le second, nous effectuons le calcul pour ensuite incrémenter la variable.

L'opérateur -- fonctionne de la même manière bien qu'il effectue une décrémentation.

Exemple2 : Mélange de types dans une même expression numérique

public class MelangeTypes { public static void main(String [] params) { int a = 3, b = 4, c, d; double t = 3.0, x, y;

c = b / a; x = b / a; y = b / t;

System.out.print(" c= " + c + "\t x= " + x + "\t y= " + y); x = 1.0 * b / a; y = b / a * 1.0; System.out.print("\n\n x= " + x + "\t y= " + y); d = ( int )(1.0 * b / a); System.out.print("\n\n d= " + d); return; }}

/*

c= 1 x= 1.0 y= 1.33333333333333333

x= 1.33333333333333333 y= 1.0 ( y= 1 * 1.0 = 1.0 )

d= 1 */

53/286

Page 54: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

c = b / a; a / b = 1.333333... tronqué à 1. Est-ce parce que c est un int? NON puisque dans :

x = b / a; x est un double et le résultat est tronqué à 1.000000. C'est donc le type des variables a et b qui déterminent celui de leur division. On confirme ce principe :

y = b / t; t est un double, le résultat de la division n'est pas tronqué : y = 1.333333

En pratique, pour ne pas avoir de problèmes, on utilise l'écriture suivante :

x = 1.0 * b / a; (et non pas x = b / a * 1.0; )Ainsi, la première opération est 1.0 * b dont le résultat est un réel et impose ce type à toute l'expression.

Nous faisons une chose très différente avec l'écriture: int d; d = ( int )(1.0 * b / a);

1.0 * b/a = 1.333333 nous l'avons vérifié plus haut, le résultat est 1 après transtypage qui force le résultat 1.

2. Les expressions booléennesDe même qu'il existe des expressions qui calculent des résultats numériques, il en existe qui calculent des résultats booléens, c'est-à-dire un résultat pouvant être interprété soit comme VRAI, soit comme FAUX. Il existe donc des opérateurs booléens nécessaires à la construction de ces expressions. Nous y trouvons notamment les opérateurs logiques :

Opérateur Description Remarque

== Comparaison

!= Différentiation

< <= Infériorité stricte ou large

> >= Supériorité stricte ou large

&& ET logique

|| OU logique

! Négation Opérateur unaire n'agissant que sur un seul opérande.

Exemple 1 :

boolean egal = (3 == 4); int a = 3, b = 4; boolean different = ( ( a < b ) || ( a > b ) );

La variable egal prend la valeur false et la variable different se verra affecter la valeur true

54/286

Page 55: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

soit si a est plus petit que b, soit s'il est plus grand. Sinon different se verra affecter la valeur false.

Exemple 2 :

boolean bool1 = ( 3 == 4 ); boolean bool2 = ( 3.0 == 4.0 );

Il est bien clair que dans ces deux cas, les opérateurs de comparaisons ne sont pas les mêmes. L'un prend deux opérandes entiers, l'autre deux opérandes décimaux.

3. Les expressions retournant des chaînes de caractèresComme nous l'avons dit précédemment, un opérateur agissant sur des chaînes de caractères, permet de constituer des expressions de type chaîne de caractères. Ainsi, l'opérateur + permet de concaténer deux chaînes pour en former une nouvelle.

Exemple :

public class EssaiChaines { public static void main( String args[] ) { String chaine1 = "Le début et ..."; String chaine2 = " la fin !!!"; String chaine3 = chaine1 + chaine2; System.out.println(chaine3); }}

Le résultat est l'affichage de la chaîne de caractères : Le début et ... la fin !!!.Remarque : En Java, comme toute valeur peut être affichée sous forme de chaîne de caractères, alors l'expression ("chaine1 " + 1) aura pour valeur "chaine1 1".

4. Les affectations et les opérateurs associésIssus du langage C, d'autres opérateurs d'affectation sont définis. Le principe reste quasiment le même, à la différence près que la valeur qui va être affectée à la variable, est calculée en fonction de l'état de cette variable. Observons le tableau suivant qui précise les équivalences entre ces opérateurs et l'opérateur d'affectation classique.

a += b; a = a + b; a -= b; a = a - b; a *= b; a = a * b; a /= b; a = a / b; a %= b; a = a % b; a &= b; a = a & b; a |= b; a = a | b; a ^= b; a = a ^ b; a >>= b; a = a >> b; a << = b; a = a << b;

55/286

Page 56: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

5. Les autres opérateurs du langageIl existe encore d'autres opérateurs dont une description sommaire est donnée dans le tableau suivant. Nous serons, par la suite, amenés à en reparler.

newOpérateur d'allocation mémoire. Il sera étudié plus en détail dans le chapitre consacré aux tableaux et à la programmation orientée objet. Exemple : int tableau [] = new int[10];

[]

Accès aux éléments d'un tableau. Attention, les indices d'un tableau commencent toujours à partir de zéro. Si un tableau contient dix éléments, les indices varieront donc entre 0 et 9. Exemple : b = tableau[indice];

.

Opérateur de traversé (on dit aussi d'accès), utilisé pour accéder aux champs (attributs et méthodes) des objets. Si vous avez fait du C, il ramène aux champs des structures. Exemple : int valeur = objet.champQuelconque;

?:

L'opérateur conditionnel : c'est le seul opérateur ternaire (qui prend trois opérandes) du langage. Il permet de calculer une expression plutôt qu'une autre selon la valeur booléenne d'une expression. L'expression qui sert à choisir quelle autre expression calculer, se place devant le point d'interrogation. Celle qui sera calculée si le test est vrai doit être mise entre le point d'interrogation et les deux points, la dernière, après les deux points. Exemple : boolean b = true; //... int valeur = b ? 3 * a + z : 3 * a - k;

,

Cet opérateur permet de constituer une expression à partir de deux sous-expressions. Ceci est utile dans les constructions du langage qui n'accepte qu'une unique expression à un endroit particulier.Exemple : while ((step = f(x), a -= step) > 0) g(a);

6. Priorité des opérateursIl est possible de se servir de parenthèses pour fixer l'ordre d'évaluation des sous-expressions formant une expression Java. Ainsi, les expression 3 * 2 + 4 et 3 * ( 2 + 4 ) n'auront pas les mêmes valeurs (10 et 18). Mais attention tout de même car l'utilisation abusive des parenthèses rend très rapidement un code illisible. Dans le but de ne pas arriver à de telles situations, il est utile de connaître les règles de priorités qui ont été définies sur les opérateurs. Le tableau suivant fixe cet ordre en allant du plus prioritaire jusqu'à l'opérateur de séquencement (,) qui est le moins prioritaire.

Opérateur Signification Priorité descendante

[] élément de tableau gauche->droite. opérateur d'accès

56/286

Page 57: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

Opérateur Signification Priorité descendante

++ post-incrémentation (lvalue++) droite -> gauche++ pré-incrémentation (++lvalue)-- post-décrémentation (lvalue--)-- pré-décrémentation (--lvalue)~ inversion de bits! NON logique- moins unaire+ plus unaire& adresse* indirection

( type ) conversion de type* multiplication gauche -> droite/ division% modulo+ addition- soustraction<< décalage à gauche>> décalage à droite< plus petit que<= plus petit ou égal> plus grand que>= plus grand ou égal== égalité!= inégalité& ET logique bit à bit^ OU logique exclusif bit à bit| OU logique bit à bit&& ET logique|| OU inclusif?: opérateur ternaire= affectation

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

57/286

Page 58: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

Opérateur Signification Priorité descendante

, opérateur séquentiel

7. Les erreurs de codage dans les expressionsNous allons introduire la notion d'erreur lors des calculs numériques et nous renvoyons le lecteur à l'ouvrage d'André Fortin [4] qui constitue une aide précieuse dans le domaine du calcul numérique.

Nous allons définir l'erreur absolue de l'approximation x' d'un nombre x comme étant : x = ∣x−x '∣

De la même manière, nous définirons l'erreur relative comme étant :

E R = x∣x∣

L'erreur absolue fournit une mesure quantitative de l'erreur alors que l'erreur relative en indique l'importance. Toutefois dans la pratique, la méconnaissance exacte de x empêche parfois le calcul de ces erreurs. Dans un système informatique, nous sommes limités par les spécificités de la représentation des nombres. Ainsi, quel que soit le nombre de bits utilisés pour la représentation, il existe un plus petit et un plus grand nombres positifs représentables et respectivement pour les nombres négatifs. De même, à l'intérieur de cet intervalle, seuls quelques nombres sont représentables exactement, les autres subissant une troncature par arrondi.

Montrons un exemple des dégâts causés par ces troncatures en additionnant un nombre très petit avec un nombre très grand :

float grand = 5.3247f, petit = 0.0005423f, resultat = 0;resultat = grand + petit;System.out.println(resultat);

Le résultat affiché est 5.325242 et donc grand a « avalé » une partie de petit. L'erreur absolue est x = ∣5.3252423−5.325242∣=3E-7 et l'erreur relative est de 5.63E-6 %.

Dans certains cas, si nos calculs sont organisés de manière à avaler tous les petits nombres, alors l'erreur risque de devenir importante comme nous le montre l'exemple suivant. Celui-ci présente un résultat encore plus affligeant, puisque lors du calcul d'y0, x0 est totalement avalé par x1, permettant ainsi une erreur relative de 100% :

Exemple :public class EssaiGrandsNombres {

public static void main(String [] params) { double x0 = 50, x1 = 1e30, x2 = -1e30, y0, y1; y0 = x1 + x0 + x2; System.out.println(" y0= " + y0); y1 = x1 + x2 + x0; System.out.println(" y1= " + y1); return;

58/286

Page 59: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

}}

/* Le programme nous affiche: y0= 0.0 y1= 50.0 */

Remarque : Notons au passage dans cet exemple, la manière d'écrire sous une forme condensée un nombre comme 1 suivi de trente zéros : 1e30 ou encore 1E30.

Nous venons de survoler l'ensemble des opérateurs ainsi que quelques expressions du langage, et effleurer les conséquences des erreurs commises lors de calculs numériques. Ils nous seront fort utiles pour calculer des valeurs numériques ou autres dans nos programmes.

8. ExercicesExercice 4.1Étant donné y = a.x3 + b.x2 + 7, laquelle des instructions suivantes représente cette équation ?

(a) y = (a * (x * x * (x + b)) * (x * x + 7))(b) y = a * (x * x) * x + (b * x * (x + 7))(c) y = (((a * x) * (x * x)) + b * x * x + 7)

Exercice 4.2Soient i=1, j=2, k=3 et m=2. Qu'affiche l'instruction suivante ?

System.out.print(k + m < j | 3 - j >= k); ?

Placer les parenthèses pour l'évaluation de l'instruction au niveau du résultat correct.

(a) 2 k + m < j | 3 - j >= k(b) true k + m < j | 3 - j >= k(c) false k + m < j | 3 - j >= k

Exercice 4.3Dans le morceau de code suivant, donnez les valeurs des variables après chaque instruction.

int e1 = 3;int e2 = e1++;int e3 = ++e1;e1 = e1++;e3++;

Exercice 4.4Quel problème se pose dans le morceau de code suivant ? Le résoudre.

short x = 5;short y = 15;

59/286

Page 60: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

x = x + y;

Exercice 4.5Qu'affiche le code suivant sur la sortie standard ?

byte a = ( byte ) 10;byte b = ( byte ) 120;a = ( byte )(a + b);System.out.println("a = " + a);

Exercice 4.6Le programme suivant comporte plusieurs erreurs. Lesquelles ? Les corriger. Qu'affiche ce programme ?

class Bidule { int truc; public static void main (String[] args) { for (int i = 0; i < 10; i++) truc = truc + 10; System.out.println(i); }}

Exercice 4.7En utilisant les opérateurs logiques, réaliser un programme permettant de vérifier les lois de de Morgan.

9. Séance de TP N°2 : DÉBOGUER UN PROGRAMME

TSIRIS, © Lycée Diderot, 2006

Champs Tests, mises au point et validation

Tâches T4.1

Temps nécessaire 4h

Objectif : Mettre en oeuvre le débogueur en ligne jdb

Le polynôme suivant permet le calcul de la variable réelle y en fonction de la variable réelle x.

60/286

Page 61: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

y=x−1

x

12⋅x−12

x2

13⋅x−13

x3

14⋅x−14

x4

15⋅x−15

x5

On peut simplifier ce calcul en posant : u= x−1/ x . Par exemple, pour x = 2,0 alors u vaut 0,5 et y = 0,688...

9.1. Travail demandé

1. Compilation du source Qu'observe t-on ? (Relevez les éventuelles erreurs).

2. Visualisation des variables en mode pas à pas Effectuer un relevé, sous forme de tableau, des valeurs prises par chaque variable aux différentes lignes de calculer(), et cela pour chacune des deux méthodes . Puis comparer la taille des fichiers .class obtenus avec une compilation utilisant l'option de débogage -g à celle de ceux obtenus avec le rajout des instructions System.out.println(). Méthode 1 : Utilisation du débogueur jdb

Placer des points d'arrêts et exécuter en mode pas à pas dans calculer(). Méthode 2 : Utilisation de System.out.println("")

Placer des appels de println() à chaque ligne de calculer().

3. Correction du source Déboguer le programme en comparant les valeurs attendues aux valeurs obtenues.

4. Compilation et test Compiler puis tester le programme corrigé. Réaliser un tableau comparatif des avantages et inconvénients de chaque méthode.

9.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne ; • Code source de Polynome.java :

Remarque : la numérotation des lignes de codes n'est là qu'à titre indicatif, et ne doit en aucun cas apparaître dans le code source.

1. /*2. * Polynome.java3. *4. * Lycée Diderot 5. * Département Informatique Industrielle6. * 61, rue David d'Angers7. * 75019 Paris8. *9. * TP - Session 2006/0710. * Copyright (c) 1996/2006, A. Lebret.11. */12.13. /**14. * Effectue un calcul polynomiale15. * @version 1.0 16. * @author A.Lebret17. */18. class Polynome {

61/286

Page 62: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 4 - Opérateurs et expressions du langage

19. 20. /**21. * Effectue le calcul polynomiale à partir de l'argument22. * @param x nombre23. * @return resultat du calcul24. * @see java.lang.Math25. */26. public double calculer(double x) {27. double u, y;28. u = x - 1 / x;29. y = u + Math.pow((u/2),2.) + Math.pow((u/3),3.) + Math.pow((u/4),4.) + Math.pow((u/5),5.); 30. return y;31. }32.33. /**34. * Point d'entree de l'application35. * @param arguments tableau de chaines de caractères36. * @return rien37. */38. public static void main(String[] arguments) { 39. Polynome p = new Polynome();40. System.out.println("x = " + arguments[0] + " et y = " + p.calculer(new Double(arguments[0]).doubleValue())); 41. }42. }

62/286

Page 63: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 5 - Les instructions du langage

CHAPITRE 5 - LES INSTRUCTIONS DU LANGAGE

1. Les instructions de contrôle Souvent dans un programme, une instruction ne devra être exécutée que si une certaine condition est vérifiée. C'est le rôle des structures de contrôle : l'instruction if...else et l'instruction switch.

1.1. L'instruction if...else

Le if permet une exécution conditionnelle : « une instruction, ou un bloc d'instructions6, ne sera exécutée que si une condition est vérifiée ».

Syntaxe :

if ( expression ) instruction;

ou encoreif ( expression ) { instruction 1; instruction 2; ... instruction n;}

Si l'expression est vérifiée, alors l'instruction ou le bloc d'instructions est exécuté.

Exemple :

public class EssaiIf { public static void main ( String args [] ) { int note = 5; if ( note >= 15 ) System.out.println("La note est bonne"); } }

En compilant ce programme une première fois avec note égale à 5 ; la phrase ne s'affiche pas. Une seconde compilation avec note égale à 15 l'affiche bien.

Le if peut être accompagné d'une clause else permettant d'exécuter certaines instructions si l'expression est fausse.

Syntaxe :

6Un bloc d'instructions est un ensemble d'instructions mis entre accolades et considéré par la logique du programme comme une instruction unique.

63/286

Page 64: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 5 - Les instructions du langage

if (expression) { instruction 1; instruction 2; ... instruction n;} else { instruction 1; instruction 2; ... instruction m;}

Exemple :

public class EssaiIfElse { public static void main(String args[ ]) { int note = 10; if ( note >= 15 ) System.out.println("La note est très bonne"); else System.out.println("La note est moyenne"); } }

En compilant cet exemple une première fois avec note égale à 5 et une deuxième fois avec note égale à 15, nous affichons l'un ou l'autre des messages.

Plusieurs instructions if...else peuvent être imbriquées les unes dans les autres selon les formes ci-dessous :

if else if else if else

if if if else else else

Un else se rapportant toujours au if le plus proche.

Remarque : Soyons attentif à l'imbrication des if...else, car elle peut induire des erreurs logiques difficilement détectables et utilisons les indentations après avoir placé correctement nos accolades de début et de fin.

1.2. L'instruction switch

Imaginons ce qui se passerait si une vingtaine de if...else imbriqués devaient exister dans notre programme. Il serait difficile à suivre et encombrant. C'est alors qu'intervient l'instruction switch.

Syntaxe :

switch ( variable_entiere ) { case const1: séquence instructions 1 case const2:

64/286

Page 65: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 5 - Les instructions du langage

séquence instructions 2 . . . case constM: séquence instructions M default: séquence instructions N }

Où variable_entiere est obligatoirement une variable entière, constX est une constante entière ou caractère (en fait son code Unicode) et la partie default est optionnelle mais fortement recommandée.

Le fonctionnement de l'instruction est le suivant : variable est comparée à chaque constante depuis la première jusqu'à la dernière. Lorsqu'une égalité est assurée, les instructions correspondantes à la constante concernée sont exécutées ainsi que celles correspondantes aux constantes suivantes et à la partie default : c'est l'effet de chute (fall-through en anglais). Si aucune égalité n'est assurée, les instructions de la partie default, si elle existe, sont exécutées.

Ainsi l'instruction switch est équivalente à la forme suivante : if (variable == const1) { séqu. instr. 1 } else if (variable == const2) { séqu. instr. 2, séqu. instr. 3, …} else if (variable == const3) { séqu. instr. 3, séqu. instr. 4, …} . . . else { séqu. instr. N }

Exemple :

public class EssaiSwitch { public static void main( String args[ ] ) { char operateur = '-'; switch ( operateur ) { case '+' : System.out.println("+"); case '-' : System.out.println("-"); case '*' : System.out.println("*"); default : System.out.println("autre"); } } }

Le résultat est : - * autre

Le plus souvent, l'effet de chute est indésirable ; Il suffit alors d'ajouter à la fin de chaque séquence

65/286

Page 66: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 5 - Les instructions du langage

d'instructions, l'instruction break.

2. Les instructions de boucle Dans la plupart des algorithmes que nous avons à concevoir, il est nécessaire d'exécuter des répétitions. L'expression de ces algorithmes par un langage de programmation nécessite que celui-ci soit muni d'instructions de boucle, et Java en fournit trois : l'instruction for, l'instruction while et l'instruction do...while. Nous allons les passer en revue dans les sections suivantes.

2.1. L'instruction for

L'instruction for est souvent utile lorsque l'on connaît au préalable le nombre de fois qu'une séquence d'instructions devra être exécutée.

Syntaxe :

for ( expr1 ; expr2 ; expr3) { // séquence instructions}

Où expr1 est une expression permettant l'initialisation de la boucle, expr2 est une expression booléenne imposant une condition sur sa continuation et expr3 est une expression permettant sa progression. Les trois expressions sont utilisées pour contrôler l'exécution de l'instruction de la façon suivante :

1) évaluer expr1 ; 2) tester la condition dans expr2 et si elle est fausse, sortir de la boucle ;3) exécuter la séquence d'instructions ; 4) évaluer expr3 ; 5) tester à nouveau la condition dans expr2, si elle est vérifiée revenir à 3) sinon sortir

de la boucle. La partie s'étendant de 3) à 5) s'appelle une itération de la boucle. L'étape 1) est l'initialisation, 4) est la mise à jour et 2) est la condition de continuation.

Dans la plupart des cas, une variable entière est utilisée pour assurer la coordination entre les expressions de la boucle et assurer son bon fonctionnement. La structure la plus commune est donc :

for ( int i = valeurInitiale ; i < valeurFinale ; i++) { // séquence instructions}

i s'appelle l'index de boucle.

Exemple : public class EssaiBoucleFor { public static void main ( String args [] ) { for ( int i = 0 ; i < 5 ; i++ ) System.out.println("Itération de la boucle"); } }

66/286

Page 67: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 5 - Les instructions du langage

La sortie de la boucle est alors : Itération de la boucle Itération de la boucle Itération de la boucle Itération de la boucle Itération de la boucle

On voit que la boucle s'est exécutée 5 fois.

2.2. L'instruction while

L'instruction for convient lorsque le nombre d'itérations est connu à l'avance et lorsqu'une variable d'index sert à contrôler la boucle. Par contre, lorsque le nombre d'itérations n'est pas connu à l'avance, il est préférable d'utiliser l'instruction while. Cette dernière permet d'itérer un bloc d'instructions tant qu'une condition est vérifiée. Elle présente de plus, l'avantage de tester la condition avant la première exécution.

Syntaxe :

while ( condition ) { // séquence instructions}

Exemple :

public class EssaiBoucleWhile { public static void main ( String args[] ) { int m = 1, n = 3, o = 100; while ( ( n * m / o ) < 100 ) { n++; m++; o--; } System.out.println(m); } }

L'exécution de ce programme affiche 62. Pouvait-on déduire d'une façon simple que ce programme allait s'exécuter ce nombre de fois ?

2.3. L'instruction do...while

L'instruction do...while est une instruction while dont la condition de continuation a été placée à la fin de la boucle. Ainsi contrairement au while, la boucle est exécutée au moins une fois avant que la condition ne soit évaluée. Elle correspond au repeat d'autres langages.

Syntaxe :

do { // séquence instructions

67/286

Page 68: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 5 - Les instructions du langage

} while ( condition );

2.4. Les instructions break et continue

L'instruction break permet de quitter un bloc d'instructions en cours comme nous l'avons vu précédemment. L'instruction continue quant à elle, permet d’arrêter l'itération en cours et de reprendre à celle d'après.

instruction22;for (int i = 0; i < 10; i++) { instruction1; instruction2; if (i == x) break; instruction 3;}instruction23;

instruction22;for (int i = 0; i < 10; i++) { instruction1; instruction2; if (i == x) continue; instruction 3;}instruction23;

3. ExercicesExercice 5.1Une seule des instructions suivantes est exacte. Laquelle ?

(a) while (c<=5) { produit *= c c = c + 1; }(b) if (genre==1) type = "Homme"; else; type = "Femme";(c) for (a=8; a>3; a--);

Exercice 5.2Pourquoi le code suivant est-il bogué ? for (int compteur = 0; compteur < 10; compteur ++); System.out.println("Compteur = " + compteur);

Exercice 5.3Écrire la boucle de comptage de 100 à 200 et de deux en deux.

(a) en utilisant une boucle for ;(b) en utilisant une boucle while ;(c) en utilisant une boucle do...while.

4. Séance de TP N°3 : FAIRE DES CHOIX

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

68/286

Page 69: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 5 - Les instructions du langage

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 4h

Objectif : Apprendre à faire des choix avec if..else et switch..case.

4.1. Travail demandé

1. Choisir avec if...elseOn se propose d'écrire un programme nous permettant de calculer les racines d'une équation du second degré : aX² + bX + c = 0, où a, b, et c sont des nombres réels simples précisions.

1. Rappeler l'algorithme de résolution d'une équation du second degré 2. Construire le fichier source Equation.java en employant des structures imbriquées de

if..else. 3. Compiler et tester le bon fonctionnement de la procédure avec l'équation X² - 3000.001 + 3

= 0. Calculer les erreurs absolue et relatives. Conclusion ? Le passage en double précision résout-il le problème ?

4. Réitérer avec des équations permettant de valider tous les cas de figure.5. Générer la documentation.

2. Choisir avec switch...caseÉcrire un programme qui simule une calculette (fichier Calculette.java) et dont les opérations sur des nombres entiers soient l'addition (+), la soustraction (-), la multiplication (*), la division (/) et le modulo (%).

1. En cours d'exécution, le programme demande à l'utilisateur d'entrer deux valeurs numériques caractère correspondant à l'opération à effectuer. Suivant le caractère entré (+, -, *, /, %) le programme affiche l'opération effectuée suivie du caractère '=' ainsi que du résultat.

2. Après avoir écrit et exécuté le programme avec différentes valeurs, saisir dans cet ordre les valeurs suivantes : '2', '0' puis '/'. Que se passe-t-il ? Pourquoi ?

3. Modifier le programme de façon à ne plus rencontrer cette situation en cours d'exécution. 4. Transformer la calculette pour qu'elle opère sur des nombres réels. 5. Générer la documentation.

4.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne ;• Pour vos travaux à venir en mode console, la classe Clavier vous est fournie ci-après :/* * Clavier.java * * Lycée Diderot * Département Informatique Industrielle * 61, rue David d'Angers * 75019 Paris * * TP - Session 2006/07 * Copyright (c) 1996/2006, A. Lebret. */import java.io.*;import java.util.Scanner; // existe depuis la version 1.5.0 de Java

69/286

Page 70: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 5 - Les instructions du langage

/** * <p>Classe de lecture au clavier.<br /> * Les fonctions étant statiques, leur appel se fait comme suit :</p> * <pre> String chaine = Clavier.S(); // pour récupérer une chaine * byte octet = Clavier.b(); // pour récupérer un octet au clavier * short eCourt = Clavier.s(); // pour récupérer un entier court au clavier * int entier = Clavier.i(); // pour récupérer un entier au clavier * long eLong = Clavier.l(); // pour récupérer un entier long au clavier * float reelF = Clavier.f(); // pour récupérer un réel simple précision * double reelD = Clavier.d(); // pour récupérer un réel double précision * char car = Clavier.c(); // pour récupérer un caractère</pre> * @version 1.0 * @since Java 1.5.0 * @see java.util.Scanner * @author Alain Lebret */public class Clavier { /** Lecture d'une chaîne de caractères au clavier */ public static String S() { return ( new Scanner(System.in) ).nextLine(); }

/** Lecture d'un octet au clavier */ public static byte b() { return ( new Scanner(System.in) ).nextByte(); }

/** Lecture d'un entier court (2 octets) au clavier */ public static short s() { return ( new Scanner(System.in) ).nextShort(); }

/** Lecture d'un entier (4 octets) au clavier */ public static int i() { return ( new Scanner(System.in) ).nextInt(); }

/** Lecture d'un entier long (8 octets) au clavier */ public static long l() { return ( new Scanner(System.in) ).nextLong(); }

/** Lecture d'un réel double précision au clavier */ public static double d() { return ( new Scanner(System.in) ).nextDouble();}

/** Lecture d'un réel simple précision au clavier */ public static float f() { return ( new Scanner(System.in) ).nextFloat(); }

/** Lecture d'un caractère unicode au clavier */ public static char c() { return ( new Scanner(System.in) ).nextLine().charAt(0); }}

5. Séance de TP N°4 : FAIRE DES RÉPÉTITIONS

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 4h

Objectif : Apprendre à faire des répétitions en Java avec les boucles for, while et do..while.

70/286

Page 71: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 5 - Les instructions du langage

5.1. Travail demandé

1. Exploiter les répétitions avec do...whileÉcrire une classe PGCD possédant une méthode main() qui permet de lire deux nombres entiers entrés au clavier, puis de calculer leur PGCD (Plus Grand Commun Diviseur).

2. Exploiter les répétitions avec whileÉcrire une classe ExerciceWhile possédant une méthode main() qui permet d'acquérir au clavier des nombres entiers positifs et d'afficher le plus petit, le plus grand et la moyenne. L'entrée d'un nombre nul permet la sortie du programme.

3. Exploiter les répétitions avec forEn utilisant une boucle for, écrire une classe Alphabet possédant une méthode main() qui affiche l'alphabet en lettres majuscules, d'abord à l'endroit, puis à l'envers, après un passage à la ligne. Réitérer en n'affichant plus qu'une lettre sur deux, ces dernières devant conserver leur position par rapport à l'affichage initial. Voici une capture de l'affichage souhaité :

A B C D E F G H I J K L M N O P Q R S T U V W X Y ZZ Y X W V U T S R Q P O N M L K J I H G F E D C B AA C E G I K M O Q S U W Y B D F H J L N P R T V X ZZ X V T R P N L J H F D B Y W U S Q O M K I G E C A

5.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

71/286

Page 72: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

CHAPITRE 6 - LE MODÈLE OBJET DE JAVA

1. Définition d'une classe en JavaNous pouvons remarquer en observant les programmes précédents, qu'à chaque fois, le code est placé dans une structure introduite par le mot réservé class. Il s'agit de la définition d'une classe. class doit être suivi du nom de la classe qui débute obligatoirement par une majuscule, puis d'une description de ses champs (attributs et méthodes) mise entre accolades.

class Cercle { // Définition des champs}

1.1. Les attributs de la classe

Dans les chapitres précédents, nous avons vu comment définir des variables à l'intérieur d'une méthode (rappelez-vous les différents exemples avec la méthode main()). Pour définir un attribut dans une classe, on réalise la même démarche. En effet, un attribut peut être vu comme une variable. La différence essentielle réside dans le fait que le domaine de visibilité est plus grand dans le cas des attributs de la classe. Par domaine de visibilité, on entend l'étendue des parties du code du programme sur lesquelles l'attribut peut être accédé, ne serait-ce déjà que par toutes les méthodes de la classe. Une variable, elle, ne peut être utilisée uniquement qu'au niveau du bloc d'instructions dans lequel elle est définie.

Un attribut est généralement désignée par un substantif décrivant au mieux la propriété qu'il renseigne.

La notation en « dos de chameau7 » est généralement utilisée. Par exemple : nomArtiste, portEntreeSortie, estVraie, nombreTumeurs, etc..

La classe suivante donne la description d'une classe Cercle associée à la notion de cercle : elle devra donc contenir un point pour le centre (de type Point2D) et un rayon (valeur réelle en double précision). La classe Point2D contiendra quant à elle, deux valeurs réelles en double précision permettant de définir les coordonnées d'un point dans le plan.

class Cercle { private Point2D centre; private double rayon;}

class Point2D { private double x, y;}

7 minuscule au début, puis majuscule à chaque nouveau mot le composant.

72/286

Page 73: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

1.2. Les méthodes

Maintenant, nous désirons définir des comportement liés à cette classe graphique qu'est le cercle. Nous devons donc définir des méthodes à cet effet. Supposons que dans le cadre du programme que nous sommes en train d'écrire nous ayons besoin de pouvoir déplacer un cercle selon un vecteur donné : il nous faut donc écrire une méthode qui prend un vecteur en paramètre. Nous devons donc, au passage, définir une classe Vecteur2D (cette classe possédera deux attributs réels en double précision.

Au stade actuel de l'écriture du programme, nous pourrions penser qu'il serait judicieux de ne pas créer la classe Vecteur2D, pour utiliser à sa place la classe Point2D qui contient la même information. Ceci est une mauvaise idée, car la sémantique d'un point du plan n'est pas la même que celle d'un vecteur. En effet, du point de vue mathématique, ils représentent deux concepts différents avec lesquels ont obtient des traitements différents. Il est donc fortement conseillé de séparer en codes distincts, deux concepts différents.

Une méthode est généralement désignée par un verbe d'action à l'infinitif.

Deux exceptions : les méthodes retournant des booléens (ex: boolean estCirculaire(), etc.), ainsi que les méthodes accesseurs getXXX() et setXXX() permettant d'accéder aux attributs (nous avions parlé d'interface

dans la toute première partie de ce chapitre).

De la même manière que pour les attributs, la notation dite en « dos de chameau » est utilisée.

Nous obtenons donc les classes suivantes : class Cercle { private Point2D centre; private double rayon;

public void deplacer(Vecteur2D vecteur) { centre.x += vecteur.x; centre.y += vecteur.y; }}

class Vecteur2D { private double x; private double y;}

Nous pouvons remarquer l'utilisation du mot réservé void. Celui-ci sert à définir un type de retour nul, c'est-à-dire sans valeur, pour la méthode. Si une méthode doit retourner une valeur non nulle, alors il faut spécifier son type à cet endroit précis de la définition de la méthode.

73/286

Page 74: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

1.3. La surcharge de méthodes

Si maintenant, nous souhaitons définir un déplacement de cercle, non pas en spécifiant un vecteur, mais bien des valeurs en x et en y, il faut alors écrire une autre méthode :

class Cercle { private Point2D centre; private double rayon;

public void deplacer(Vecteur2D vecteur) { centre.x += vecteur.x; centre.y += vecteur.y; }

public void deplacer(double x, double y) { centre.x += x; centre.y += y; }}

Nous pouvons remarquer que l'on a utilisé le même nom pour les deux méthodes : cette possibilité s'appelle la surcharge. Cela est réalisable car lorsque nous utiliserons une des deux méthodes, nous spécifierons soit un argument de type Point2D, soit deux arguments de type double. De manière plus formelle, deux méthodes ne doivent pas avoir le même prototype (nous entendons par là, le type des paramètres et celui de retour de la méthode). Donc, si nous voulons définir deux méthodes de déplacement, une horizontalement et une verticalement, nous ne pouvons pas les nommer de la même manière, sans quoi on ne pourrait savoir laquelle choisir lors d'un appel. Voici donc une application possible, agrémentée d'une méthode d'affichage textuelle.

class Cercle { private Point2D centre; private double rayon;

public void deplacer(Vecteur2D vecteur) { centre.x += vecteur.x; centre.y += vecteur.y; }

public void deplacer(double x, double y) { centre.x += x; centre.y += y; }

public void deplacerH(double x) { centre.x += x; }

public void deplacerV(double y) { centre.y += y; }

public void afficher() { System.out.print("Objet Cercle :\n\tcentre : "); centre.print(); System.out.println("\n\trayon : " + rayon); }}

class Point2D { private double x; private double y;

74/286

Page 75: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

public void afficher(){ System.out.print("[" + x + ", " + y + "]"); }}

Remarque : Il aurait été à priori plus judicieux de fournir à la place des méthodes d'affichage, des méthodes retournant uniquement la description textuelle de l'objet (et si possible nommées toString()).

2. Instanciation d'objetRegardons à présent comment définir des objets à partir d'une classe. Cette création d'objet, à partir d'une classe, est appelée instanciation. On instancie donc un objet à partir d'une classe en appliquant l'opérateur new sur l'un des constructeurs de la classe. Précisons un peu les choses.

2.1. L'allocation de mémoire

Pour qu'un objet puisse réellement exister au sein d'un processus, il faut qu'il puisse stocker son état dans une zone de la mémoire. Or deux objets définis à partir de deux classes différentes n'ont, à priori, pas forcément besoin de la même taille d'espace mémoire, car ils n'ont pas les mêmes définitions d'attributs. L'opérateur new est donc là pour nous simplifier l'existence. Par l'intermédiaire d'une méthode un peu particulière appelée constructeur, cet opérateur déterminera sans problème la taille de l'espace mémoire requis.

2.2. Les constructeurs

De manière simple, on peut dire qu'un constructeur est une méthode d'une classe donnée, servant à créer des objets du type de la classe.

Toute classe possède au moins un constructeur !

Un constructeur est une méthode qui est appelée et exécutée au moment de la création, par un new, d'une instance de la classe.

Remarque :

Un constructeur ne retourne pas de valeur et ne mentionne pas void au début de sa déclaration.

Par ailleurs, un constructeur doit posséder le même nom que celui de sa classe. Il peut de plus posséder des arguments qui seront initialisés de façon

classique.

De même que les méthodes acceptent la surcharge, les constructeurs l'admettent aussi. L'exemple suivant propose donc quelques constructeurs pour nos classes déjà étudiées.

75/286

Page 76: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

class Cercle { private Point2D centre; private double rayon;

public Cercle() { centre = new Point2D(); rayon = 1; } public Cercle(Point2D centre, double rayon) { this.centre = centre; this.rayon = rayon; }

public void deplacer(Vecteur2D vecteur) { centre.x += vecteur.x; centre.y += vecteur.y; }

public void deplacer(double x, double y) { centre.x += x; centre.y += y; }

public void deplacerH(double x) { centre.x += x; }

public void deplacerV(double y) { centre.y += y; }

public void afficher() { System.out.print("Objet Cercle :\n\tcentre : "); centre.afficher(); System.out.println("\n\trayon : " + rayon); }}

class Point2D { private double x, y;

public Point2D(){ x = y = 0; } public Point2D(double x, double y) { this.x = x; this.y = y; } public void afficher(){ System.out.print("[" + x + ", " + y + "]"); }}

class Vecteur2D { private double x, y; public Vecteur2D() { x = y = 0; } public Vecteur2D(double x, double y) { this.x = x; this.y = y; }}

Nous remarquons deux choses en regardant ce programme : Un constructeur peut créer des objets qu'il utilise et son rôle est principalement de définir l'état initial des objets instanciés. Sans un tel mécanisme il serait difficile de connaître l'état initial des objets et donc quasiment impossible de déterminer l'évolution du programme.

Petit exercice : déterminer l'affichage résultant du programme suivant.

public class Essai {

76/286

Page 77: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

public static void main(String args[]){ Cercle c1 = new Cercle(); Cercle c2 = new Cercle(new Point2D(5,4),3); c1.afficher(); c2.afficher(); } }

Deux règles sur les constructeurs :

Si aucun constructeur n'est spécifié dans la définition de la classe, un constructeur par défaut est obligatoirement fourni, celui-ci n'admettant aucun paramètre.

Si au moins un constructeur est défini, alors le constructeur par défaut, celui qui n'admet aucun paramètre, n'est plus fourni. Si son utilité se fait sentir, alors il faudra le définir explicitement.

2.3. Les finaliseurs

Nous venons de voir que des constructeurs pouvaient être fournis pour permettre la création d'objets. Parallèlement, un finaliseur et un seul (un peu l'équivalent du destructeur en langage C++) peut être défini pour être utilisé lors de la destruction de l'objet. Celui-ci doit forcément se nommer finalize(), il ne prend aucun paramètre et ne renvoie aucun type (void). Cette méthode doit, de plus, être qualifiée de public, sans quoi le compilateur nous rappellera à l'ordre.

En voici un exemple : class Point2D { // ... public void finalize(){ System.out.print("Objet Point2D détruit"); }}

Mais comment un objet est-il réellement détruit ? C'est le rôle du ramasse-miettes (Garbage Collector) et nous allons étudier son principe dans la section suivante.

3. Le ramasse-miettesUn programme Java a besoin de mémoire pour pouvoir s'exécuter et en règle général, plus il en a, mieux c'est. Comme nous l'avons déjà vu, l'opérateur new se charge d'allouer de la mémoire à la demande. Une conséquence évidente est que, si on ne libère pas la mémoire des objets devenus inutiles, on peut rapidement arriver à saturation. Le ramasse-miettes se charge de repérer ces objets inutiles et de libérer cette mémoire inaccessible. Il opère de façon totalement automatisée et dans la quasi totalité des cas, il n'est pas nécessaire de s'en soucier. N'oublions pas qu'il existe la possibilité de définir, par l'intermédiaire des finaliseurs, les actions à effectuer en cas de destruction d'objets. Pour les plus curieux et avant le chapitre sur les threads, notons que le ramasse-miettes fonctionne en permanence dans un thread de faible priorité.

77/286

Page 78: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

4. Méthodes et attributs statiquesNous pouvons résumer ce qui vient d'être vu ainsi : Nous définissons des classes, à partir desquelles nous pouvons instancier des objets, qui eux sont manipulables. Or si nous reprenons le tout premier exemple, nous pouvons nous apercevoir que tout fonctionne malgré tout, sans qu'il y ait eut aucune instanciation. La seule différence réside dans la présence du mot réservé static.

4.1. main() : le point d'entrée du programme

Comme nous l'avons déjà dit, nous lançons l'exécution d'un programme Java en démarrant une machine virtuelle Java avec, en paramètre, le nom de la classe de démarrage, laquelle doit forcément contenir une méthode main(). Une fois la JVM chargée en mémoire et initialisée, elle lance le programme proprement dit, par la première instruction de la méthode main(), et ce, sans instancier d'objet à partir de la classe de démarrage. Cela fonctionne, car la méthode est déclarée static : c'est-à-dire qu'elle existe sans qu'il y ait eut instanciation. La tâche principale de cette méthode est alors d'instancier des objets sur différentes classes afin que le programme puisse travailler. Comme la méthode main() existe indépendamment de toute classe, si elle doit utiliser des attributs ou des méthode de la classe, il faut alors que ses champs soient eux aussi déclarés static, sans quoi ils n'existent pas. Plus précisément, les méthodes déclarées statiques sur une classe, ne peuvent en manipuler que des champs statiques. Notons enfin que la méthode main() admet en paramètre un tableau de chaînes de caractères (String args[]). Celui-ci contient les éventuelles options spécifiées sur la ligne de commande, lors du lancement de la JVM.

Exemple :public class Essai { static int a = 3;

static public void main(String args[]) { a += 5; System.out.println("a^2 = " + calculerCarre(a)); }

static int calculerCarre(int value){ return value*value; }}

4.2. Partage d'informations

Une conséquence logique d'une définition statique est la suivante : les champs statique d'une classe sont partagés par toutes les instances de cette classe. En effet, comme tout champ statique existe indépendamment de toute instanciation d'objet, il existe aussi après une quelconque instanciation. Exemple :

public class Essai { static int a = 3;

static public void main(String args[]) {

78/286

Page 79: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

Essai s1 = new Essai(), s2 = new Essai(); s1.a++; s2.a++; System.out.println("a = " + Essai.a); }}

donne le résultat : 5

• Le programme se lance en exécutant la méthode main() statique. Celle-ci instancie deux objets à partir de la classe Essai elle-même. Nous pourrions, dans le main(), si nous le voulions, écrire s1.main() qui instancierait encore deux objets, mais nous aurions très rapidement un souci de mémoire. Ensuite, sur chacun des deux objets, nous incrémentons la variable a. Mais comme celle-ci est partagée, nous incrémentons finalement la variable de deux unités. Finalement, nous affichons le résultat : 5. Notons que nous accédons ici à la variable en utilisant le nom de la classe.

Une méthode est dite de classe si dans sa déclaration figure le mot-clé static.

Le main() est une méthode de classe !

Les attributs et méthodes d'une classe peuvent être déclarés static (comme la méthode main()) ou non.

Dans le premier cas, on dira qu'il s'agit d'un attribut ou d'une méthode de classe.

Dans le second cas, on dira qu'il s'agit d'un attribut ou d'une méthode d'instance.

N'ayant rien de plus à ajouter sur les champs statiques, nous vous proposons donc, dans la section suivante, d'étudier un des concepts clés de la programmation orientée objet : l'héritage.

5. L'héritageNous allons tout d'abord considérer une extension du programme manipulant nos cercles sur un espace à deux dimensions. Il en résultera une simplification du problème.

5.1. Ce qu'il ne faut surtout pas faire

L'extension va consister en l'introduction de classes liées à d'autres types de figures géométriques dans le plan. En effet, nous aimerions, maintenant, pouvoir manipuler outre des cercles, des carrés, des rectangles, des triangles, ou encore toute autre figure géométrique de votre choix. L'idée la plus simple se résume en la définition d'une classe par type de figures. Voici donc un autre exemple de classe.

class Carre { private Point2D centre; private double longueur;

79/286

Page 80: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

public Carre() { centre = new Point2D(); longueur = 1; } public Carre(Point2D c, double l) { centre = c; longueur = l; }

public void deplacer(Vector2D vecteur) { centre.x += vecteur.x; centre.y += vecteur.y; }

public void deplacer(double x,double y) { centre.x += x; centre.y += y; }

public void deplacerH(double x) { centre.x += x; } public void deplacerV(double y) { centre.y += y; }

public void afficher(){ System.out.print("Objet Carre :\n\tcentre : "); centre.afficher(); System.out.println("\n\tlongueur : " + longueur); }}

Jusqu'ici, rien de bien nouveau, et l'ensemble des classes fonctionne correctement. Malgré cela, ce n'est pas réellement ce que nous pouvons faire de mieux. En effet, si nous regardons le code de la classe Cercle et celui de la classe Carre, ils sont identiques, aux noms des variables près. Si nous avions un moyen de regrouper les parties de code identiques (nous dirions « factoriser le code »), cela serait bien mieux. C'est ce que nous propose le concept d'héritage.

5.2. Ce qu'il faut faire

L'idée principale consiste à définir une classe à partir d'une autre. Une remarque importante peut déjà être faite :

Une classe fille dérive d'une unique classe mère car l'héritage multiple n'est pas supporté par le langage Java.

(nous verrons par la suite un moyen de simuler l'héritage multiple avec le concept d'interface)

Une fois que l'héritage est spécifié, la classe fille possède aussi l'ensemble des attributs et des méthodes de sa classe mère.

La principale difficulté, avec l'héritage, est de définir ce qui est propre à la classe mère et ce qui l'est pour sa classe héritière. Dans tous les cas, cela est fortement lié au problème considéré.

Revenons donc à nos classes Cercle et Carre. Nous remarquons que dans les deux cas, nous avons besoin de connaître la position du centre de la figure géométrique. De même, nous définissons dans les deux cas, les mêmes méthodes liées au déplacement de la figure. En réfléchissant encore un peu, nous pouvons alors pressentir qu'il en sera de même pour toutes les classes associées à la notion de figures géométriques. Il pourrait donc être judicieux de définir une classe FigureGeometrique de

80/286

Page 81: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

laquelle toutes les classes associées à une figure géométrique, pourraient hériter. Quant à la syntaxe à utiliser pour définir un lien d'héritage, il suffit d'ajouter le mot réservé extends suivi du nom de la classe mère, de suite après le nom de la classe fille, ce dans la définition de cette dernière.

class Cercle extends FigureGeometrique

Un point important et souvent source d'erreur est à éclaircir. Il n'y a en aucun cas héritage des constructeurs. Si l'on ne spécifie pas explicitement un constructeur particulier, nous ne pourrons pas l'utiliser et ce, même s'il en existe un qui est défini dans la classe mère. Par contre, des règles existent sur l'utilisation des constructeurs de la classe mère dans les constructeurs d'une classe fille quelconque. Avant de voir ces règles en détail, quelques précisions sont à apporter sur deux mots réservés du langage : super et this. Le premier permet d'accéder depuis la classe considérée aux définitions de la classe parente (ces définitions pouvant être soient des méthodes, soient des constructeurs) et le second sert à accéder à l'objet courant.

Règle 1 Si nous invoquons super(), cela signifie que le constructeur en cours d'exécution passe la main au constructeur de la classe parente pour commencer à initialiser les attributs définis dans cette dernière. Ensuite le premier constructeur continuera son exécution.

Règle 2Un appel de constructeur de la classe mère ne peut uniquement se faire qu'en première instruction d'une définition de constructeur. Une conséquence évidente est qu'on ne peut utiliser qu'un seul appel au constructeur de la classe mère.

Règle 3Si la première instruction d'un constructeur ne commence pas par le mot réservé super, alors le constructeur par défaut de la classe mère est appelé. Autrement dit, l'appel à super() est implicite. Dans ce cas, il faut faire bien attention à ce qu'un constructeur sans paramètres soit défini au sein de la classe parente.

Règle 4Si nous invoquons this(), le constructeur considéré passe la main à un autre constructeur de la classe considérée. Encore une fois, cela doit être la première instruction du bloc.

Afin de mieux comprendre les choses regardons bien attentivement les exemples qui suivent : ils permettent, notamment, de valider les règles que nous venons de présenter.

class Classe1 {

81/286

Page 82: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

public Classe1(){ System.out.println("Classe1"); } public Classe1(int val){ this(); System.out.println(val); }}

etclass Classe2 extends Classe1 { public Classe2() { super(5); System.out.println("Classe2"); } public Classe2(int val) { System.out.println(val); }}

produisent comme résultats :Instructions Affichage

new Classe1(); Classe1

new Classe1(3); Classe 1

new Classe2(); Classe1 5 Classe2

new Classe2(2); Classe1

Nous pouvons à présent revenir à notre exemple des figures géométriques. Notons que celui-ci reprend, en grande partie, tout ce qui a déjà été observé dans ce chapitre.

class FigureGeometrique { protected Point2D centre; // Idéal pour l'héritage

public FigureGeometrique() { centre = new Point2D(); } public FigureGeometrique(Point2D c) { centre = c; }

public void deplacer(Vector2D vecteur) { centre.x += vecteur.x; centre.y += vecteur.y; }

public void deplacer(double x,double y) { centre.x += x; centre.y += y; }

public void deplacerH(double x) { centre.x += x; } public void deplacerV(double y) { centre.y += y; }

public void afficher(String nature){ System.out.print("Objet "+nature+" :\n\tcentre : "); centre.afficher(); }}

puis :class Cercle extends FigureGeometrique { private double rayon;

public Cercle() { rayon = 1; } public Cercle(Point2D centre, double rayon) { super(centre); this.rayon = rayon; }

82/286

Page 83: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

public void afficher() { super.afficher("Cercle"); System.out.println("\n\trayon : " + rayon); }}

et enfin :class Carre { private double longueur;

public Carre() { longueur = 1; } public Carre(Point2D c, double l) { super(c); longueur = l; }

public void afficher(){ super.afficher("Carre"); System.out.println("\n\tlongueur : " + longueur); }}

5.3. L'intérêt

Le premier point important qu'il faut absolument assimiler, est que l'héritage supprime, en grande partie, les redondances dans le code. En effet, une fois la hiérarchie de classes bien établie, on localise en un point unique les sections de code, celles-ci restant à tout moment accessibles grâce au mot réservé super. Seconde chose importante, et ce à condition que la hiérarchie de classes ait été bien pensée, on peut très facilement rajouter, après coup, une classe, et ce à moindre coût, étant donné que l'on peut réutiliser le code des classes parentes. Dernier point, si l'on n'a pas encore modélisé un comportement dans une classe donnée et que nous voulons le rajouter, une fois l'opération terminée, ce comportement sera alors directement utilisable dans l'ensemble des sous-classes de celle considérée.

6. Le polymorphisme

6.1. Définition

Un langage orienté objet est dit polymorphique, s'il offre la possibilité de pouvoir percevoir un objet en tant qu'instance de classes variées. Le langage Java est polymorphique. Pour mieux comprendre, reconsidérons notre hiérarchie de classes associée aux figures géométriques. Au niveau des figures, il est clair qu'un carré est une figure géométrique. Au point de vue du programme Java, il en va de même : un objet instancié sur une classe donnée peut être utilisé en tant qu'instance de toutes les classes parentes de la classe considérée. Comme nous le montre l'exemple suivant, deux solutions permettent d'atteindre ce résultat : le polymorphisme ou le transtypage.

class A { // ... }

class B extends A {

83/286

Page 84: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

// ... }

// ... B b = new B(); A a1 = b; // Utilisation du polymorphisme A a2 = ( A )b; // Utilisation du transtypage

Remarque : En Java, si l'on ne spécifie pas de lien d'héritage, la classe en cours de définition hérite alors de la classe Object. Cette classe de l'API Java est la classe de plus haut niveau de laquelle toutes les autres dérivent. Nous verrons plus tard que la classe Object fournie un certain nombre de fonctionnalités qui sont donc applicables sur n'importe quel objet Java. Mais attention, tout n'est pas aussi simple qu'il y paraît. Pour nous en convaincre, essayons de répondre au petit exercice suivant. Des explications seront fournies dans la section suivante.

Exercice : Quel est le résultat affiché par l'exécution de Polymorphe ?

class A { public void operer() { System.out.println("Mere"); }}

class B extends A { public void operer() { System.out.println("Fille"); }}

public class Polymorphe { public static void main(String args[]) { B b = new B(); A a = b; a.operer(); // Utilisation du polymorphisme }}

6.2. La liaison dynamique (dynamic binding)

L'exemple précédent met en valeur la notion de liaison dynamique (en anglais dynamic binding). Ainsi, lors de l'exécution, c'est la méthode la plus spécifique qui est utilisée : celle de la classe de création si elle existe. L'exemple qui suit donne le code des classes de figures géométriques avec une méthode calculerSurface() supplémentaire, ainsi qu'un exemple d'utilisation.

class FigureGeometrique { protected Point2D centre;

public FigureGeometrique() { centre = new Point2D(); } public FigureGeometrique(Point2D c) { centre = c; }

public void deplacer(Vector2D vecteur) { centre.x += vecteur.x; centre.y += vecteur.y; }

public void deplacer(double x,double y) {

84/286

Page 85: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

centre.x += x; centre.y += y; }

public void deplacerH(double x) { centre.x += x; } public void deplacerV(double y) { centre.y += y; }

public double calculerSurface() { return 0; }

public void afficher(String nature){ System.out.print("Objet "+nature+" :\n\tcentre : "); centre.afficher(); } public void afficherNL(){ afficher("Figure"); System.out.println(""); }}

etclass Cercle extends FigureGeometrique { private double rayon;

public Cercle() { rayon = 1; } public Cercle(Point2D centre, double rayon) { super(centre); this.rayon = rayon; } public double calculerSurface { return 2*Math.PI*rayon*rayon; } public void afficher() { super.afficher("Cercle"); System.out.println("\n\trayon : " + rayon); }}

puisclass Carre { private double longueur;

public Carre() { longueur = 1; } public Carre(Point2D c, double l) { super(c); longueur = l; } public double calculerSurface() { return longueur*longueur; } public void afficher(){ super.afficher("Carre"); System.out.println("\n\tlongueur : " + longueur); }}

et enfin,public class EssaiFigures { static double calculerSurface(FigureGeometrique f) { return f.calculerSurface(); }

static public void main(String args[]){ FigureGeometrique f1 = new FigureGeometrique();

85/286

Page 86: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

Carre f2 = new Carre(new Point2D(),2);

System.out.println(calculerSurface(f1)); System.out.println(calculerSurface(f2)); }}

A priori, une figure, sans autre précision sur sa nature n'a pas de surface. Or une méthode double calculerSurface() de la classe FigureGeometrique est nécessaire pour pouvoir utiliser le polymorphisme. Le concept d'abstraction permet alors de résoudre ce genre de problème.

7. Utiliser l'abstractionIl peut donc, dans certains cas, être utile de définir une méthode sans en préciser le code. Les seules informations alors données sont donc, le nom de la méthode, les types des paramètres ainsi que le type de retour de cette dernière : nous appelons cela, le prototype de la méthode. En Java, nous disons que nous définissons une méthode abstraite. Pour réaliser une telle déclaration, il suffit de mettre en tête du prototype le mot réservé abstract. Dans ce cas, nous ne définissons plus le corps de la méthode et nous supprimons ses accolades que nous remplaçons par un point-virgule. Voici un exemple de définition d'une méthode abstraite :

public abstract double calculerSurface();

Mais attention : il n'est pas possible de définir une méthode abstraite n'importe où. Cela ne peut se faire que dans une définition de classe abstraite.

7.1. Définir une classe abstraite

Une classe abstraite est une classe qui peut contenir des méthodes abstraites, mais pas uniquement. Une chose importante est à signaler : comme une classe abstraite, possédant des méthodes dont le code est inconnu est incomplète, on ne peut en aucun cas instancier d'objets de cette classe. Il faut impérativement définir des classes filles, lesquelles fournirons les définitions manquantes, sans quoi elles seraient aussi abstraites. Ces classes filles peuvent par contre, servir à instancier des objets.

Dans le cas de nos classes de figures géométriques, cela se traduit par le fait que si la classe FigureGeometrique est abstraite, nous ne pouvons plus instancier d'objet de cette nature par une instruction telle que new FigureGeometrique(). Le polymorphisme peut par contre être encore utilisé avec des instructions du type : FigureGeometrique f = new Cercle().

Du point de vue de la syntaxe, nous définissons une classe abstraite en rajoutant devant le mot réservé abstract. Le code de la classe FigureGeometrique devient donc le suivant.

abstract class FigureGeometrique { protected Point2D centre;

public FigureGeometrique() { centre = new Point2D(); } public FigureGeometrique(Point2D c) { centre = c; }

public void deplacer(Vector2D vecteur) { centre.x += vecteur.x; centre.y += vecteur.y;

86/286

Page 87: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

}

public void deplacer(double x,double y) { centre.x += x; centre.y += y; }

public void deplacerH(double x) { centre.x += x; } public void deplacerV(double y) { centre.y += y; }

public abstract double calculerSurface();

public void afficher(String nature){ System.out.print("Objet "+nature+" :\n\tcentre : "); centre.afficher(); } public void afficherNL(){ afficher("Figure"); System.out.println(""); }}

Nous ne pouvons alors plus créer d'objets de type FigureGeometrique. Dans la réalité, il n'existe pas de « figures », mais seulement des cercles, des carrés ainsi que d'autres formes, que nous regroupons sous le terme générique de « figures géométriques ». Donc, notre modèle est parfaitement conforme à la spécification de notre problème. Il existe une autre technique pour introduire de l'abstraction dans un programme, par le biais des interfaces. Voyons cela de plus près.

7.2. Définir une interface

En fait, ce mécanisme n'est qu'une généralisation du concept de classe abstraite. Plus précisément :

Une interface est une classe dont toutes les méthodes sont abstraites.

Nous n'avons donc plus besoin de spécifier que les méthodes sont abstraites (signalées par le mot réservé abstract), car elles doivent forcément l'être. Au niveau de la syntaxe, nous introduisons une interface non plus par le mot réservé class mais par celui d'interface. Et d'ailleurs, pour les distinguer des classes, nous dirons qu'on implémente une interface et non pas qu'on en hérite. Le fait d'implémenter une interface se réalise grâce au mot réservé implements.

Exemple : interface I1 { public void opererI1();}

abstract class C1 { public void opererC1();}

87/286

Page 88: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

class C2 extends C1 implements I1 { public void opererI1() { // Le code de opererI1 }

public void opererC()1 { // Le code de opererC1 }}

interface I2 extends I1 { public void opererI2();}

abstract C3 implements I2 { public void opererI1() { // Le code de opererI1 }}

La différence essentielle entre une classe abstraite (dont toute les méthodes seraient abstraites) et une interface réside dans le fait qu'il

n'est possible d'hériter que d'une seule classe (héritage simple), alors qu'il est possible d'implémenter plusieurs interfaces.

C'est une solution pour simuler l'héritage multiple.

class MaClass extends ClasseMere implements Interface1, Interface2 { // ... }

8. Attributs et méthodes constantsToujours dans le but d'augmenter la puissance d'expression du langage, les concepteur y ont introduit le mot réservé final. Bien que sa sémantique diffère selon les cas d'utilisation, nous pouvons dire que globalement, il permet d'introduire de l'invariance dans les programmes, soit dans un but lié à la modélisation des problèmes, soit dans le cadre d'optimisations. Afin de mieux appréhender cela, étudions chacun des cas d'utilisation de ce mot réservé.

8.1. Attributs constants

Il est possible de préfixer une déclaration de variable, ou d'attribut, par le mot réservé final. Dans ce cas, la variable, respectivement l'attribut, sont dès lors considérés comme constants : leurs valeurs ne pourront plus être modifiées. Ce qui implique que l'on est obligé d'initialiser une variable constante lors de sa définition !

Exemple :class Mathematiques { final double PI = 3.141592654; // Attribut de classe public void donnerExemple() { final int valeur = 2; // Variable

88/286

Page 89: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

}}

Une petite entorse à la règle précédente : dans le cas d'un attribut, il est possible de ne pas l'initialiser lors de la déclaration, mais il est alors obligatoire de le faire au moins dans les constructeurs de la classe. Cette possibilité, introduite dans le langage à partir de la version 1.1 du JDK, permet d'avoir différents objets avec leurs propres valeurs constantes. Sans cette possibilité nous aurions eu des valeurs constantes identiques pour tous les objets de la classe considérée.

class Mathematiques { final double PI ; public Mathemathiques(double delta) { PI = 3.141592654 + delta; }}

Attention tout de même au piège suivant. Dans le cas où, une variable constante ou un attribut, contiennent un objet ou un tableau, ce qui est constant n'est pas l'état de cet objet, c'est-à-dire ses attributs, mais sa référence, c'est-à-dire son pointeur. Autrement dit, c'est la zone mémoire qui reste invariante, mais son contenu peut changer. Ceci est normal, car malgré le fait que le langage masque la gestions des références, elles restent bien présentes : en Java, quand nous définissons une variable contenant un objet, nous définissons en réalité un pointeur.

Pour qui ne se souviendrait plus de ce qu'est un pointeur (une référence), on peut dire qu'il s'agit d'un identificateur de zone de la mémoire, permettant de situer un ensemble de données, dans notre cas un objet.

public class Mathematiques { double PI = 3.141592654; static public void main(String args[]){ final Mathematiques m = new Mathematiques();

m = new Mathematiques(); // n'est plus autorisé m.PI = 6.28; // est autorisé }}

8.2. Méthodes finales

De manière analogue, il est possible de déclarer une méthode en la préfixant avec le mot réservé final. Mais la sémantique d'une telle déclaration n'est plus la même que précédemment : en effet, une méthode ne change pas au cours du temps. Par contre, elle peut changer selon le niveau dans lequel on se trouve dans une hiérarchie de classes donnée. C'est justement ce changement que l'on interdit en utilisant ce mot réservé. Autrement dit, dès lors que nous déclarons, dans une classe donnée, une méthode finale, il nous sera alors impossible de la redéfinir dans les sous-classes.

Il existe deux intérêts à ce genre de manipulation : • cela peut être plus réaliste au niveau de la modélisation d'un problème, de figer un

comportement à un niveau de l'arborescence de classes ;• on gagne en terme de rapidité d'exécution.

89/286

Page 90: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

Le premier point ne demande aucun commentaires. Le second, lui, peut être plus difficile à comprendre : nous allons donc nous attarder un peu dessus. Nous avons vu qu'avec la liaison dynamique et le polymorphisme, la JVM, lors d'un appel de méthode, se charge de lancer la méthode la plus spécifique dans une hiérarchie de classes, ce en fonction du type réel de l'objet sur lequel l'appel est réalisé. Pour ce faire, l'interpréteur récupère la classe de l'objet (liaison dynamique) et y recherche la méthode. S'il ne la trouve pas, il prend alors la classe mère (polymorphisme) puis continue sa recherche, et ainsi de suite. Dès que la méthode est trouvée (elle existe forcément car le compilateur a validé le code), son corps est exécuté. Il est clair que cette recherche coûte du temps. Si une méthode est déclarée final, tout ceci n'a plus lieu : le compilateur sait déterminer que si l'on appelle une méthode sur un objet d'une classe donnée, et que cette méthode est déclarée final dans une des classes parentes, alors c'est elle qui doit être exécutée. Dans ce cas, la localisation de la méthode est écrite directement dans le bytecode produit par le compilateur.

8.3. Paramètres constants

Une dernière possibilité d'utilisation du mot réservé final permet de rendre un paramètre d'une méthode, constant : il ne pourra donc jamais changer de valeur durant l'exécution du corps de la méthode. Exemple :

class ClasseQuelconque { public void faireQuelqueChose(final int x){ // On affiche la valeur du paramètre System.out.print(x);

// Ceci marche très bien int y = 2 * x;

// Ceci ne marche pas du tout !!! x += 2; }}

9. Les classes internes Nous allons dans cette section introduire la notion de classes internes (inner classes en anglais). Cette possibilité de pouvoir définir une classe à l'intérieur d'une autre, a été définie à partir de la version 1.1 du JDK. L'exemple suivant montre comment créer une classe interne.

public class Englobante {

class Interne { int attribut = 0; }

public static void main(String args[]){ Englobante tl = new Englobante(); Interne i = tl.new Interne(); }}

90/286

Page 91: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

Nous pouvons tout de suite remarquer qu'il n'est possible de créer un objet instancié sur une classe interne, qu'à partir d'un objet instancié sur la classe englobante. Si l'on ne mentionne pas d'objet, pour appliquer l'opérateur new, c'est la classe courante qui est prise en considération, ce qui permet d'écrire des programmes similaires au suivant.

public class Englobante { class Interne { int attribut = 0; }

Interne appelerInterne(){ return new Interne(); }

public static void main(String args[]){ Englobante tl = new Englobante(); Interne i = tl.appelerInterne(); }}

10. Les paquetagesUn paquetage (package en anglais) est un regroupement de classes, travaillant conjointement sur le même domaine : par exemple toutes les classes qui travaillent sur les flux d'entrée/sortie sont placées dans le paquetage java.io. L'ensemble des classes du langage se trouvent rangées dans une série de paquetages appelée API Java, chacun ayant un domaine d'action bien déterminé, comme par exemple, les connexions réseaux, les composants graphiques, etc.. Il nous est aussi possible de définir nos propres paquetages afin de les utiliser dans nos programmes. Nous allons donc voir comment un paquetage est défini, comment il est utilisé et quels en sont les intérêts.

10.1. Création d'un paquetage

Pour créer un paquetage, deux étapes sont nécessaire. La première consiste à déterminer pour chaque fichier, à quel paquetage il doit appartenir. La seconde place les fichiers de bytecode générés à une position bien précise de l'arborescence du système de fichiers. Reprenons en détail chacun de ces deux points.

10.1.1. Le mot réservé package

Nous devons en premier lieu indiquer pour chaque fichier, à quel paquetage il appartient. Si nous ne spécifions pas de paquetage, le fichier appartient au paquetage par défaut : par conséquent, tout fichier Java, et donc toute classe, appartient à un paquetage, même si cela n'est pas spécifié explicitement. Au niveau de la syntaxe, il suffit d'ajouter en première instruction dans le fichier, le mot réservé package suivit du nom du paquetage auquel le fichier doit appartenir puis d'un point-virgule.

package diderot.tsiris.tp8;

10.1.2. Placer les fichiers d'un paquetage

Mais cela ne suffit pas à définir un paquetage Java. Il faut aussi correctement situer les fichiers sources et bytecode en respectant une règle très précise. En effet, le nom du paquetage correspond à un emplacement sur le système de fichiers. Par exemple, le paquetage de nom

91/286

Page 92: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

diderot.tsiris.tp8 doit être placé dans un répertoire ./diderot/tsiris/tp8/. Les points, opérateurs d'accès, sont remplacés par un slash ou un anti-slash au niveau de l'arborescence. Pour placer les fichiers de bytecode, deux méthodes sont applicables : soit nous le faisons manuellement, soit nous le faisons lorsque nous compilons le fichier. Dans ce dernier cas, il suffit d'entrer la commande : javac -d repertoire file.java. L'option -d permet de spécifier le répertoire dans lequel sera stocké le fichier .class. L'exemple suivant intègre les classes de figures géométriques dans un paquetage diderot.tsiris.outils.

mkdir diderot mkdir diderot/tsiris mkdir diderot/tsiris/outils javac -d diderot/tsiris/outils/ Cercle.java ...

Remarque : Si nous utilisons le paquetage par défaut, le répertoire dans lequel doivent se trouver les fichiers sources et bytecode est le répertoire courant.

10.2. Importer les classes d'un paquetage

Comme nous allons le voir, il est possible d'utiliser tout ou partie d'un paquetage dans un programme grâce au mot réservé import. Mais pour que cela fonctionne, il faut avoir correctement positionné la variable d'environnement CLASSPATH.

10.2.1. Positionnement de la variable CLASSPATH

Comme nous l'avons déjà mentionné plus haut, la variable d'environnement CLASSPATH permet de localiser l'ensemble des fichiers .class (ceux qui contiennent le bytecode), afin que la JVM puisse, lors de l'exécution d'un programme, charger le code nécessaire. En effet, la JVM ne charge pas directement tout le code de l'application, mais seulement la partie dont elle a besoin à un moment donné. Si par la suite, une classe est utilisée sans que son code ait déjà été chargé, alors la JVM se charge de le faire, en utilisant la variable CLASSPATH. En fait, lorsque nous définissons un paquetage, nous regroupons un certain nombre de fichiers bytecode dans un répertoire particulier. Il faut donc pour que ce code soit utilisable, ajouter la racine de ce paquetage dans la variable CLASSPATH. Notons que ce doit être la racine et non le répertoire que nous devons ajouter.

10.2.2. Le mot réservé import

Nous allons voir qu'il existe plusieurs façons d'utiliser un paquetage. En effet, nous pouvons soit spécifier que nous utilisons un paquetage complet, soit nous déterminons un par un les fichiers du paquetage que nous souhaitons utiliser. Nous pourrions être amenés à penser qu'il vaut mieux importer deux fichiers d'un paquetage plutôt que le paquetage complet, surtout si celui-ci comporte de nombreux autres fichiers, mais il n'en est rien. En effet, les fichiers ne sont pas tous chargés dès le début de l'exécution du programme, mais dynamiquement, et seulement si le besoin s'en fait sentir. En fait, lorsque nous spécifions que nous importons un paquetage, nous indiquons uniquement la localisation des fichiers le constituant. Il est donc préférable d'importer un paquetage complet, plutôt que d'importer quelques fichiers d'un même paquetage. Pour des raisons de lisibilité, il peut par contre être judicieux de faire le contraire. Pour importer une classe d'un paquetage, il faut utiliser le mot réservé import suivi du nom du paquetage, d'un point, du nom de la classe et d'un point-virgule. Dès lors que notre programme

92/286

Page 93: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

contient une telle déclaration d'importation, nous pouvons y utiliser la classe. Si nous voulons importer directement le paquetage complet, il suffit de remplacer le nom de la classe par une *. Ce mécanisme est celui du joker dans les systèmes de fichiers (voir le cours GNU/Linux), ce qui est d'ailleurs logique, quand on sait qu'à une classe est associé un fichier portant le même nom.

Exemple :import java.awt.Button; // On importe la classe Buttonimport java.shape2D.*; // On importe toutes les classes

public class Exemple { public static void main(String args[]){ Circle c1 = new Circle(); ... }}

10.2.3. Accéder directement à une classe d'un paquetage

Mais il n'est pas obligatoire d'importer un paquetage pour pouvoir en utiliser une partie seulement des fonctionnalités. En effet, nous pouvons accéder à une classe en spécifiant sa localisation complète, directement dans le code.

Exemple : class Exemple { static void main(String args[]){ java.shape2D.Circle c1 = new java.shape2D.Circle(); ... }}

10.3. Compresser un paquetage

Comme nous le verrons plus tard, les programmes Java peuvent être amenés à circuler sur un réseau informatique et comme le transfert de données n'y est pas forcément très rapide, il a fallut minimiser la taille des données qui allaient y transiter. Dans ce but, la notion d'archive Java a été introduite. Ces archives (d'extension .jar) au format de compression ZIP contiennent les fichiers bytecode (d'extension .class), et donc les paquetages. Les archives Java permettent aussi une diffusion plus efficace des programmes : en effet, nous n'avons plus qu'à transférer un unique fichier en lieu et place de tous les .class qui constituent le programme.

10.3.1. L'utilitaire jar

Afin de pouvoir facilement créer des archives Java, l'utilitaire jar est fourni avec le JDK. Son utilisation est simple et similaire à celle d'autres outils d'archivage que l'on peut notamment trouver sur les stations Unix. Voici quelques informations sur l'utilisation de cet utilitaire. Usage : jar {ctx}[vf0] [jar-file] fichiers...Options :

c crée une nouvelle archive t liste le contenu d'une archive x restitue les fichiers nommés (tous par défaut) d'une archive v génère un affichage sur la sortie standard d'erreur

93/286

Page 94: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

f spécifie le nom de l'archive 0 annule la compression de l'archive

Exemple : Pour archiver deux fichiers (f1.class et f2.class) dans une nouvelle archive (a1.jar), il faut procéder de la manière suivante :

jar cvf a1.jar f1.class f2.class

Remarques : Tout d'abord, si un nom de fichier correspond à un répertoire, alors se dernier sera archivé dans son intégralité. Ensuite, l'option 0 permet de pouvoir accéder à l'archive directement à partir de la variable d'environnement CLASSPATH, mais regardons cela de plus près.

10.3.2. L'archivage et CLASSPATH

Une fois le paquetage archivé il est possible de l'utiliser à condition que la variable d'environnement CLASSPATH contienne le chemin d'accès de l'archive, y compris le nom du fichier de l'archive. Cependant une restriction doit être observée : l'archive ne doit pas être compressée. Cela est obtenu à l'aide de l'option O lors de la création de cette dernière.

10.3.3. Une petite subtilité

La JVM charge une classe lors de sa première utilisation et non pas au commencement du programme, ce qui est appréciable lorsque l'on utilise un réseau informatique lent. Mais lorsque le code de la classe se trouve dans une archive, c'est l'archive complète qui est transférée et par conséquent toutes les autres classes. Par contre, une fois l'archive transférée sur la machine cliente, la JVM n'en extraira que le fichier requis.

11. Les droits d'accèsNous allons dans ce paragraphe, approfondir le concept d'encapsulation des membres (ou champs) d'une classe.

11.1. L'encapsulation

L'encapsulation permet de ne montrer d'une classe que ce qui est nécessaire à son utilisation. Nous nommerons cet ensemble interface publique de la classe, en faisant attention à ne pas confondre le mécanisme d'interface Java avec l'interface publique d'une classe. Cette interface publique contient l'ensemble des attributs et des méthodes utilisables de l'extérieur sur des objets instanciés. Pour mettre en oeuvre ce mécanisme, il existe plusieurs niveaux de visibilité utilisables sur les champs d'une classe.

11.1.1. L'accès public

Cette accès est le plus général et le moins contraignant. En effet, si le champ d'une classe est déclaré comme étant publique, alors tout objet ayant une référence sur un objet de cette même classe pourra accéder à ce champ et donc, éventuellement le modifier.

Exemple :class A { public int a = 0;}

94/286

Page 95: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

public class Main { public static void main(String args[]){ A a = new A(); a.a++; }}

11.1.2. L'accès private

En définissant un champ privé dans une classe, nous interdisons tous les accès extérieurs. Seules les autres méthodes de la classe pourront utiliser ce champ. Attention, même les méthodes d'une classe fille ne peuvent accéder au champ privé d'une classe.

Exemple :class A { public int a = 0; static private b = 0;}

public class Main extends A { public static void main(String argv[]){ A a = new A(); a.a++; // Marche très bien

// a.b++; // Ne marche pas // b++; // Ne marche pas non plus }}

Il existe deux principaux avantages à utiliser ce genre d'accès. Tout d'abord, il est des situations où les champs d'une classe n'existent que pour le fonctionnement de celle-ci et, dont nous souhaitons empêcher l'accès par un utilisateur. Il suffit alors de fixer ces champs comme étant private. Le deuxième avantage relève de la robustesse du code. En effet, nous pouvons imaginer des situations où un attribut d'une classe ne puisse pas prendre certaines valeurs, comme par exemple l'attribut denominateur d'une classe NombreRationnel qui ne peut en aucun cas valoir 0. Dans cette optique, laisser l'attribut public peut devenir dangereux pour une bonne exécution du code, car l'utilisateur peut à tout moment lui affecter une valeur fatale. Une solution consiste à positionner de tels attributs en accès private et à fournir, pour chaque champ, une méthode de lecture et une d'écriture. Dès lors, la méthode de modification peut contrôler la validité de la valeur que l'on souhaite affecter.

L'exemple suivant montre ce que pourrait être une classe NombreRationnel robuste. class NombreRationnel { private int numerateur = 0; private int denominateur = 1;

public int getNumerateur() { return numerateur; } public void setNumerateur(int valeur) { numerateur = valeur; } public int getDenominateur() { return denominateur; } public void setDenominateur(int valeur) { if (valeur == 0) { System.err.println("Mauvais dénominateur : " + valeur); System.exit(-1);

95/286

Page 96: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

} denominateur = valeur; }}

Dans cette manière de programmer, l'utilisateur de la classe peut faire abstraction de la manière dont le problème est résolu en interne, enfin et c'est peut-être moins visible, la maintenance de ce genre de programme est plus aisée.

11.1.3. L'accès protected

L'accès protégé (protected en anglais) est en fait un hybride des deux accès précédents. Un champ ainsi déclaré pourra, selon les situations, tantôt être considéré comme public, tantôt comme privé. Plus exactement, un champ protégé sera considéré comme public pour toutes les méthodes de la classe, ainsi que pour toutes celles de ses sous-classes. Ce même champ sera de plus considéré public par toutes les méthodes du paquetage contenant la classe. Dans tous les autres cas d'accès, un champ protégé sera considéré comme privé et donc non accessible.

11.1.4. L'accès friendly

Il s'agit de l'accès par défaut. Dans ce cas-là, le champ sera public vis-à-vis du paquetage, et privé pour le reste. Rappelons-nous que toute classe doit appartenir au moins à un paquetage et qu'il existe aussi un paquetage par défaut.

11.1.5. Quelques conventions

Nous allons à présent tenter d'établir un certain nombre de règles de programmation qui assurent un style « propre ».

• La première règle impose que tout attribut de la classe doit être de préférence déclaré private. Cela implique que nous ne pouvons y accéder que par l'intermédiaire de méthodes accesseurs. Une conséquence évidente, est que si des valeurs de cet attribut peuvent nuire à l'application, alors les méthodes de modification pourront vérifier que nous n'affectons pas ces valeurs.

• La méthode accesseur d'affectation de l'attribut doit respecter le prototype suivant, en supposant que l'attribut se nomme attr, et soit de type type. void setAttr(type valeur);

• La méthode accesseur de consultation doit elle aussi respecter le prototype suivant :

type getAttr();

En conclusion, il y a tout intérêt à utiliser l'encapsulation. En effet, il en résulte trois avantages principaux :

– l'utilisation des objets en est simplifiée ;– le programme est rendu plus robuste ;– la maintenance de la classe en est plus aisée.

Nous en avons donc fini avec ce chapitre consacré à la programmation orientée objet. En fait certains points liés à cette méthode de programmation manquent dans ce chapitre, mais ils sont déplacés dans les autres chapitres de ce cours, ce pour des raisons diverses.

96/286

Page 97: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

12. ExercicesExercice 6.1FenetreApplication doit hériter de la classe JFrame. Quelle syntaxe est correcte ?

(a) public FenetreApplication extends JFrame(b) class FenetreApplication extends JFrame(c) public class FenêtreApplication extends JFrame

Exercice 6.2boutonAnnuler instancie la classe JButton. Quelle syntaxe permet cette création ?

(a) JButton boutonAnnuler = new JButton("Annuler");(b) JButton boutonAnnuler = JButton("Annuler");(c) JButton boutonAnnuler = new JButton;

Exercice 6.3a) Quel est l'affichage produit par le programme suivant ?Dans le fichier principal ProgPrincipal.java :

class Etudiant { String prenom; String nom; private int age; static int nbEtudiants = 0;

public Etudiant(String p, String n, int a){ prenom = p; nom = n; age = a; nbEtudiants++; }

public void afficher(){ System.out.println(prenom+" "+nom+" a "+age+" ans"); }}

public class ProgPrincipal { public static void main(String[] args){ Etudiant p1= new Personne("Jean", "Durand", 25); Etudiant p2= new Personne("Patrick", "Martin", 30); p1.afficher(); p2.afficher(); System.out.println(Etudiant.nbEtudiants); System.out.println(p1.prenom); }}

b) Si ce programme ci-dessus comporte des erreurs, les corriger afin d'obtenir l'affichage suivant avec le main() ci-dessous :

JeanDurand25

public class ProgPrincipal { public static void main (String[] args){ Etudiant p1= new Etudiant("Jean","Durand", 25); System.out.println(p1.prenom); System.out.println(p1.nom); System.out.println(p1.age);

97/286

Page 98: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

}}

Exercice 6.4Nous disposons de la classe Point suivante :

public class Point { public double x,y; // attributs de la classe

public Point(double abscisse, double ordonnee) { // le constructeur x = abscisse; y = ordonnee; }

// calcule la distance du point a au point b public static double calculerDistance(Point a, Point b) { double xdiff, ydiff; xdiff = a.x – b.x; ydiff = a.y – b.y; // Math.sqrt retourne un double return Math.sqrt(xdiff*xdiff + ydiff*ydiff); } // calcule la distance du point receveur au point a public double calculerDistance(Point a) { double xdiff, ydiff; xdiff = x – a.x; ydiff = y - a.y; return Math.sqrt(xdiff*xdiff + ydiff*ydiff); }

// rend un nombre compris entre inf et sup public static double alea(double inf, double sup) { double r= Math.random(); // Math.random retourne un double return(inf + r*(sup - inf)); }

// affiche le point public void afficher() { System.out.println("l'abscisse est " + x); System.out.println("l'ordonnée est " + y); } public static void main(String[] args) { Point a1 = new Point(alea(0.0,2.0),alea(-1.0,0.0)); a1.afficher(); Point a2 = new Point(alea(-1.0,1.0),alea(-2.0,1.0)); a2.afficher(); Point a3 = new Point(alea(0.0,-1.0),alea(-9.0,-8.0)); a3.afficher(); System.out.println(Point.calculerDistance(a1,a2)); System.out.println(a1.calculerDistance(a2)); }}

a) Critiquer la conception de cette classe. La corriger.b) N'y-a-t-il pas redondance dans le code des méthodes calculerDistance(). Quelle solution peut-on envisager ?c) Que doit-on faire pour pouvoir tester l'égalité stricte entre deux points ?d) Comment faire pour introduire la notion de précision dans l'égalité entre deux points ? Comment

98/286

Page 99: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

s'assurer que la précision soit la même pour tous les points ?e) Un lecteur curieux pourrait faire remarquer, qu'il existe déjà une classe Point dans l'API standard. Comment être certain de celle qui est utilisée ?f) Le même curieux pourrait faire remarquer que la classe existante propose a peu près les mêmes services. Il serait en effet préférable de l'utiliser, mais qu'est-ce qu'il nous manque et comment le rajouter ?

13. Séance de TP N°5 : UTILISER DES CLASSES ET OBJETS – CRÉATION D'UNE CLASSE CALCULETTE

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 4h

Objectif : Apprendre à manipuler classes et objets en mettant en avant l'encapsulation des données et l'écriture de méthodes en langage Java.

13.1. Travail demandé

On se propose de réaliser une calculette décrite par une classe Calculette qui permet de réaliser les quatre opérations de base (addition, soustraction, multiplication et division) sur des nombres décimaux.

13.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne ;• Voici un extrait de la documentation générée par javadoc qui servira de base pour réaliser

cette classe :

public class Calculette extends java.lang.ObjectPermet d'effectuer des calculs sur des nombres réels à l'aide des quatre opérations de base. Ces calculs sont de la forme operande1 operateur operande2. Elle dispose d'un menu, d'une gestion sommaire des erreurs de frappe et permet d'effectuer plusieurs calculs tant que l'utilisateur n'a pas demandé à quitter. Les données principales sont encapsulées. La classe Clavier est utilisée pour la lecture des éléments.Version: 1.0 Author: Alain Lebret

99/286

Page 100: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 6 - Le modèle Objet de Java

Field Summary

private double

operande1 opérande 1 de la calculette

private double

operande2 opérande 2 de la calculette

private char

operateur opération effectuée sur la calculette

private double

resultat résultat de l'opération

Constructor Summary

Calculette() Constructeur par défaut.

Method Summary

void afficherCalcul() Effectue un affichage du calcul sur la console. 'q' permet de quitter le programme.

void calculer() Effectue le calcul souhaité : operande1 operateur operande2.

char getOperateur() Méthode accesseur pour la récupération de l'operateur en cours.

static void main(java.lang.String[] argument) Point d'entrée dans le programme. Crée un nouvel objet de type Calculette et lance son menu.

void afficherMenu() Effectue un affichage du menu sur la console. Il permet de demander à l'utilisateur d'entrer séquentiellement les différents champs de celui-ci. operande1, puis operande2 et enfin operateur. Une entrée sur 'q' permet de quitter le programme.

void setOperande1(double operande1) Méthode accesseur de mise à jour de l'attribut operande1

void setOperande2(double operande2) Méthode accesseur de mse à jour de l'attribut operande2

100/286

Page 101: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 7 - Les chaînes de caractères

CHAPITRE 7 - LES CHAÎNES DE CARACTÈRES

Nous traitons ici de l'utilisation de la classe java.lang.String. Et nous reprenons plusieurs points déjà rencontrés dans les chapitres précédents. Les points importants à retenir sont :– la chaîne contenue par un objet String ne peut pas être modifiée. Si l'on veut traiter des chaînes

de caractères modifiables, il est nécessaire d'utiliser la classe java.lang.StringBuffer ;– lorsque le compilateur rencontre une série de caractères entre guillemets, il crée

automatiquement un objet String, sauf si cette chaîne a déjà été rencontrée, auquel cas un même objet String peut servir pour les deux chaînes ;

– le langage définit par ailleurs un opérateur capable de travailler avec des objets String, l'opérateur + de concaténation ;

– lorsque l'opérateur de concaténation a un opérande de type chaîne de caractères et l'autre non, le second opérande est convertie grâce à la méthode toString() qui est définie dans Object. Comme toutes les classes sont des sous-classes de Object, toutes les classes connaissent toString(), et d'ailleurs beaucoup de classes de l'API la redéfinissent.

1. Création et utilisation d'objets de la classe StringCet exemple montre les avantages de l'opérateur + et de la méthode toString().

public class ManipulateurDeChaine {

public void ecrire() { String chaine = 3 + " ans"; System.out.println("Classe utilisee : " + getClass()8); System.out.println(chaine + " de longueur "+ chaine.length()9)); }

public void ecrire(String s)10 { System.out.println(s); }

public static void main( String[] arguments ) { ManipulateurDeChaine ecrivain = new ManipulateurDeChaine(); ecrivain.ecrire(); ecrivain.ecrire("Utilisation d'une surcharge " + "de la methode ecrire"); }}

On obtient à l'exécution : Classe utilisee : class ManipulateurDeChaine

8 la méthode getClass() de la classe Object retourne la classe de l'objet qui l'a invoquée. 9 la méthode length() de la classe String retourne le nombre de caractères d'une chaîne.10 on utilise ici le principe de la surcharge. Il n'y aura pas confusion entre les méthodes ecrire() car elles diffèrent par le jeu de

leurs paramètres.

101/286

Page 102: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 7 - Les chaînes de caractères

3 ans a pour longueur 5Utilisation d'une surcharge de la méthode ecrire

2. Quelques méthodes de la classe StringCet exemple illustre les méthodes equals() et compareTo(), méthodes d'instance définies dans la classe String.

public class ComparaisonChaine { public static void main(String[] arguments) { String s = "essai"; String t = "essai"; String r = "essais";

System.out.println("(" + s + " identique avec essai): "+ s.equals("essai")); System.out.println("(" + s + " identique avec essais) : " + s.equals("essais")); System.out.println("(" + s + " identique avec " + t + ") : " + (s==t))11; System.out.println("(" + s + " identique avec " + r + ") : " + (s==r)); System.out.println(s.compareTo("essai")); System.out.println(s.compareTo("essais")); System.out.println("essais".compareTo(s)); }}

On obtient à l'exécution : (essai identique avec essai) : true(essai identique avec essais) : false(essai identique avec essai) : true(essai identique avec essais) : false0-11

Il existe de nombreuses autres méthodes (voir la documentation en ligne) dont par exemple regionMatches() qui recherche l'existence d'une chaîne passée en paramètre comme sous-chaîne de la chaîne considérée. Ainsi, l'exemple suivant permet la recherche du mot « chaussettes » dans une phrase et introduit la méthode toLowerCase() qui permet de transformer toute chaîne en minuscules. Il est à noter que le prototype :

boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len);

aurait avec son premier paramètre à true permis d'éviter l'utilisation de la méthode toLowerCase().

public class RechercheChaussettes { public static void main(String[] args) { String phrase = "Les chauSSettes de l'ArchiDuchesse";

11 System.out.println("(" + s + " identique avec " + t + ") : " + (s==t)); : l'exécution de cette instruction montre que les références s et t sont égales. Il est néanmoins plus sûr d'utiliser la méthode equals() pour tester l'égalité de deux chaînes.

102/286

Page 103: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 7 - Les chaînes de caractères

String mot = "chaussettEs"; int longueurPhrase = phrase.length(); int longueurMot = mot.length(); boolean aTrouve = false; phrase.toLowerCase(); mot.toLowerCase(); for (int i = 0; i <= (longueurPhrase - longueurMot); i++) { if (phrase.regionMatches(i, mot, 0, longueurMot)) { aTrouve = true; System.out.println(phrase.substring(i, i + longueurMot)); break; } } if (!aTrouve) System.out.println("Aucune occurrence trouvée."); }}

3. ExercicesExercice 7.1« Et la marine va venir à Malte », « Élu par cette crapule », « Ésope reste ici et se repose », « Tu l'as trop écrasé, César, ce Port-Salut », etc., sont des palindromes. Écrire une classe Palindrome qui sera utilisée pour savoir si une phrase est un palindrome. Cette classe comportera plusieurs méthodes qui indiqueront si une chaîne de caractères (String) passée en paramètre est un palindrome ou pas : La méthode boolean estUnPalindrome1(String) prend en paramètre une chaîne constituée uniquement de lettres minuscules ou majuscules (pas d'espaces ni de lettres accentuées). Elle renvoie vrai si le paramètre est un palindrome. On tient compte de la casse des lettres (abBA n'est pas un palindrome). Vous comparerez les lettres une à une.

Exercice 7.2La deuxième version, nommée estUnPalindrome2() utilise la méthode reverse() de la classe StringBuilder (ou StringBuffer).

Exercice 7.3La méthode estUnPalindrome3() généralise estUnPalindrome1() : le paramètre peut comporter autre chose que des lettres (des signes de ponctuation par exemple) mais on n'en tient pas compte.

Exercice 7.4La méthode estUnPalindrome4() généralise estUnPalindrome3() : un deuxième paramètre indique si on tient compte de la casse des lettres. Ce deuxième paramètre devra être optionnel. Par défaut, les majuscules et minuscules ne seront pas considérées comme équivalentes.

Exercice 7.5Généraliser estUnPalindrome4() en estUnPalindrome5() : en plus, les lettres accentuées sont considérées équivalentes aux lettres non accentuées ; par exemple, e, ê, è et é sont équivalentes. Elle a aussi un paramètre optionnel pour ignorer la casse des lettres. Dans le cas où elle est ignorée, E, È, Ê et É sont équivalentes à e, è, ê et é.

103/286

Page 104: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 7 - Les chaînes de caractères

4. Séance de TP N°6 : UTILISER LA CLASSE String – AMÉLIORATION DE LA CALCULETTE [1]

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 8h

Objectif : Apprendre à manipuler classes et objets en utilisant les classes String et StringTokenizer en langage Java.

4.1. Travail demandé

On se propose cette fois-ci de réaliser une calculette syntaxique. Plutôt que de proposer à l'utilisateur de décomposer lui-même son calcul comme nous l'avons fait dans le TP précédent, nous allons modifier la classe Calculette de manière à ce qu'elle permette l'analyse d'une chaîne de caractères représentant le calcul à réaliser, puis d'effectuer celui-ci lors d'un appel à la méthode calculer(). Les différents éléments du calcul : les deux opérandes, l'opérateur ainsi que le résultat sont stockés par la calculette. Les chaînes de caractères passées à la méthode calculer() doivent respecter la forme suivante :

opérande1 opérateur opérande2

Voici quelques exemples de chaînes de caractères convenables : 4 - 27 62.5 / 0 7 * 9 0.64128 * 3.14159

La chaîne de caractères fin permet de quitter l'application grâce à l'instruction System.exit(0).

4.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

5. Séance de TP N°7 : ALLER PLUS LOIN DANS LES CONCEPTS OBJETS – AMÉLIORATION DE LA CALCULETTE [2]

TSIRIS, © Lycée Diderot, 2006

104/286

Page 105: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 7 - Les chaînes de caractères

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 8h

Objectif : Apprendre à affiner les notions d'encapsulation, communication entre objets et héritage par amélioration de la Calculette.

5.1. Travail demandé

On se propose de reprendre la calculette du TP précédent en dégageant la notion d'opération et plus particulièrement d'opération mathématique. Nous souhaitons réaliser une classe Calculette utilisant une classe OperationMathematique chargée de la gestion des opérations, en particulier de leur analyse. La super-classe Operation est pour sa part chargée du stockage des opérations, qu'elles soient mathématiques ou non. En utilisant le diagramme de classes ci-après, ainsi que de la documentation générée pour les classes Operation, OperationMathematique et Calculette, réaliser une calculette fonctionnellement identique à celle du TP précédent.

Remarque : La gestion des calculs ainsi que celle de la sortie du programme sera faite depuis une classe Essai comportant le main() et chargée d'instancier la Calculette.

5.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne ;• Extraits des documentations :

• Classe Operation :public class Operation extends java.lang.ObjectDéfinit une opération sous forme de chaîne de caractères. Permet d'introduire la notion d'héritage ainsi que d'encapsulation. Elle utilise : String : Représente une chaîne de caractères dont le contenu ne peut être modifié.

105/286

Figure 29: Diagramme de classes pour la Calculette [2].

Page 106: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 7 - Les chaînes de caractères

Version: 1.0 Author: Alain Lebret

Field Summary

private java.lang.String

operation chaîne de caractères représentant l'opération à effectuer.

Constructor Summary

Operation() Constructeur par défaut.

Method Summary

java.lang.String getOperation() Récupération de l'opération souhaitée.

void setOperation(java.lang.String operation) Mise à jour de l'opération souhaitée

• Classe OperationMathematique :public class OperationMathematique extends OperationDéfinie une opération mathématique de la forme operande1 operation operande2. Elle utilise : Double : Encapsule le type primitif double. String : Représente une chaîne de caractères dont le contenu ne peut être modifié. StringTokenizer : Permet de découper une chaîne de caractères en éléments de base base suivant des séparateurs précis.Version: 1.0 Author: Alain Lebret See Also: Operation

Field Summary

private double

resultat résultat de l'opération

Fields inherited from class Operation

operation

Constructor Summary

OperationMathematique() Constructeur par défaut.

Method Summary

void resoudre(java.lang.String operation) Résolution de l'Opération souhaitée : operande1 <operation> operande2.

java.lang.String toString() Redéfinition de la méthode toString permettant de retourner des informations sur l'objet sous la forme d'une chaîne de caractères.

• Classe Calculette :public class Calculette extends java.lang.ObjectAmélioration de la calculette du TP précédent avec utilisation d'une super-classe Operation ainsi que d'une classe fille OperationMathematique chargée de la gestion des chaînes de caractères définissant les opérations mathématiques à résoudre.

106/286

Page 107: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 7 - Les chaînes de caractères

Elle utilise : String : Représente une chaîne de caractères dont le contenu ne peut être modifié. System : Contient plusieurs attributs et méthodes utiles au dialogue avec le système d'exploitation. Version: 1.2 Author: Alain Lebret

Field Summary

private OperationMathematique

operation opération à effectuer

Constructor Summary

Calculette() Constructeur par défaut.

Method Summary

void calculer(java.lang.String uneOperation) Effectue le calcul souhaité en faisant appel à l'objet operation.

java.lang.String toString() Redéfinition de la méthode toString permettant de retourner des informations sur l'objet sous la forme d'une chaîne de caractères.

107/286

Page 108: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 8 - Les tableaux en Java

CHAPITRE 8 - LES TABLEAUX EN JAVA

Les tableaux ne sont ni des objets, ni des types primitifs. Un tableau se rapproche néanmoins d'un objet par le fait :

• qu'il est manipulé par référence ;• qu'il nécessite en général un new pour être défini.

1. Définition d'un tableauOn peut voir ici une première façon de déclarer et d'allouer un tableau.

char [ ] tableau;

déclare le tableau tableau, et aurait d'ailleurs aussi pu s'écrire comme en C : char tableau [ ];

Par ailleurs, tableau étant déjà défini : tableau = new char[10];

alloue un tableau pour dix variables de type char.

On pourra remarquer l'utilisation de la méthode println() pour un tableau de caractères. public class TableauA { public static void main ( String [] arguments ) { char [] tableau;

tableau = new char[2]; tableau[0] = 'P'; tableau[1] = 'I'; System.out.println(tableau); }}

On obtient à l'exécution : PI

2. Une autre façon de définir un tableauVoici une façon de déclarer et d'allouer un tableau pour trois variables de type boolean. Notons à nouveau que la concaténation entre une chaîne de caractères et une variable booléenne, effectuée grâce à l'opérateur + est possible.

public class TableauB { public static void main( String[] arguments ) { boolean tableau [] = { true, false, true }; System.out.println("Deuxième élément de tableau : " + tableau[1]); }}

108/286

Page 109: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 8 - Les tableaux en Java

On obtient à l'exécution : Deuxième élément de tableau : false

3. Longueur d'un tableau et dépassement des bornesIl est possible de récupérer la longueur d'un tableau référencé par la variable tableau en utilisant :

tableau.length;

length est en quelque sorte un attribut du tableau, attribut que l'on peut d'ailleurs uniquement lire.

On illustre aussi les exceptions. Ce sujet sera développé plus tard et il ne s'agit ici que d'une introduction.

public class TableauC { public static void main(String[] arguments) { int tableau [] = new int[3];

System.out.println("Taille du tableau : " + tableau.length); for (int i = 0; i < tableau.length; i++) tableau[i] = i; System.out.println("Deuxième élément de tableau : "+tableau[1]12); try13 { System.out.println(tableau[tableau.length])14; } catch(ArrayIndexOutOfBoundsException15 e16) { System.out.println(e + ", bien intercepte!"); } }}

On obtient à l'exécution : Somme des entiers : 3Taille du tableau : 3Deuxieme element de tableau : 1java.lang.ArrayIndexOutOfBoundsException: 3, bien intercepte!

4. Manipuler un tableau d'objetsL'exemple suivant a pour but de montrer comment manipuler un tableau d'objets, ici un tableau d'Integer. A part pour cela, il n'est évidemment pas utile, pour additionner quelques entiers, d'utiliser la classe Integer. 12 remarquez qu'un int peut servir d'opérande dans la concaténation de chaînes de caractères ; il est automatiquement converti en

son écriture décimale. 13 lorsqu'une ArrayIndexOutOfBoundsException est lancée dans un bloc précédé du mot réservé try et suivi de

catch(ArrayIndexOutOfBoundsException e) suivi à son tour d'un bloc, les instructions qui se trouvent dans ce dernier bloc sont exécutées et le programme se poursuit alors normalement, ici pour se terminer.

14 l'indice tableau.length déborde du tableau puisque le plus grand indice de ce tableau est nécessairement tableau.length - 1. En conséquence, une exception du type ArrayIndexOutOfBoundsException est levée. Si celle-ci n'était pas attrapée, elle se propagerait jusqu'à la fin du programme qui se terminerait donc directement avec un message d'erreur. Ici, le mécanisme utilisant les mots réservés catch et try permet d'intercepter l'exception.

15 la classe java.lang.ArrayIndexOutOfBoundsException hérite de la classe java.lang.Exception. 16 observez, dans la sortie du programme, l'utilisation d'une méthode toString() de la classe

ArrayIndexOutOfBoundsException.

109/286

Page 110: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 8 - Les tableaux en Java

public class TableauD { public static void main( String[] arguments ) { Integer17 tableau[] = new Integer[4]; int somme = 0, i;

for (i = 0; i < tableau.length; i++) tableau[i] = new Integer(i)18; for (i = 0; i < tableau.length; i++) somme += tableau[i].intValue()19; System.out.println("Somme des entiers : " + somme); }}

On obtient à l'exécution : Somme des entiers : 6

L'exemple suivant propose une classe TableauDynamique qui gère un tableau d'objets et peut être redimensionné en cas de manque de place. La classe java.util.Vector offre les mêmes fonctionnalités.

public class TableauDynamique { Object [] tableau; int nbElements;

public TableauDynamique(int taille) { tableau = new Object[taille]; nbElements = 0; }

private void agrandir() { Object [] tampon = new Object[tableau.length];

// Sauvegarde des objets du tableau dans un tableau temporaire for (int i = 0; i < tableau.length; i++) tampon[i] = tableau[i]; int taille = tampon.length+10; // +10 objets par défaut

// La référence du nouveau tableau pointe sur une zone mémoire // augmentée de 10 objets. tableau = new Object[taille];

// Recopie des éléments du tableau temporaire dans le tableau. for (int i = 0; i < tampon.length; i++) tableau[i] = tampon[i]; }

17 la classe java.lang.Integer, à consulter comme beaucoup d'autre classes de l'API directement dans la documentation du JDK, contient beaucoup de méthodes statiques ou non, pour gérer le type int; à titre d'exemple, la méthode public static int parseInt(String s) convertit la chaîne s en un int. Le constructeur public Integer(int value) crée une instance de la classe Integer ayant pour attribut privé, un int de valeur value.

18 il faut noter que déclarer un tableau de trois Integer ne dispense pas de construire une instance d'Integer pour chaque composante du tableau.

19 la méthode utilisée ici est une méthode d'instance de Integer ; elle retourne l'attribut de type int contenu dans l'instance d'Integer invoquée, ici tableau[i].

110/286

Page 111: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 8 - Les tableaux en Java

public void ajouter(Object unObjet) { if (nbElements >= tableau.length) this.agrandir(); tableau[nbElements] = unObjet; nbElements++; }

public void vider() { tableau = null; nbElements = 0; }

5. Un tableau à deux dimensionsCet exemple montre comment il est possible de définir un tableau d’entiers à deux dimensions et de lui allouer de la place en mémoire. Deux méthodes sont proposées.

• dans la première méthode, l'allocation est réalisée en une seule fois, en précisant les deux dimensions. Le tableau obtenu alors est rectangulaire ;

• dans la deuxième méthode, on alloue d'abord un tableau à une dimension avec des références vers des tableaux d’entiers à une dimension ; on a ainsi allouer un tableau destiné à recevoir les références des lignes. Puis on alloue une par une les lignes du tableau. Les lignes ne doivent pas nécessairement avoir toutes la même longueur.

public class TableauDouble { public static void main(String[] arguments) { int [][] image, i, j;

image = new int[2][3]; for (i = 0; i < image.length; i++) for (j = 0; j < image[i].length; j++) image[i][j]=i+j; System.out.println(image[0][1]); image = new int[2][]20; for (i = 0; i < image.length; i++) { image[i] = new int[i+2]21; for (j = 0; j < image[i].length; j++) image[i][j] = -(i+j); } System.out.println(image[0][1]); }}

On obtient à l'exécution : 1-1

20 image = new int[2][] : on réaffecte image ; un ramasse-miettes va libérer l'espace-mémoire alloué au précédent tableau. Le nouveau tableau défini est destiné à recevoir deux références de tableaux d'entiers.

21 image[i]=new int[i+2] : le nouveau tableau à deux dimensions ne sera pas rectangulaire.

111/286

Page 112: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 8 - Les tableaux en Java

6. La classe ArraysLa classe java.util.Arrays offrent de nombreuses méthodes statiques pour manipuler les tableaux : remplissage (fill()), copie (copyOff() et copyOfRange()), tri (sort()), recherche (binarySearch()), comparaison (equals()), etc.. Elles sont décrites dans la documentation et nous n'en fournissons qu'un extrait. Exemple : Remplir des tableaux avec des valeurs uniques

boolean[] echiquier = new boolean[64];boolean estPasseSurLaCase = false;Arrays.fill(echiquier, estPasseSurLaCase); byte[] image = new byte[40000];byte niveauDeGris = ( byte )0xFF;Arrays.fill(image, niveauDeGris); double[] resultats = new double[10000];double resultatParDefaut = -1;Arrays.fill(resultats, resultatParDefaut);

7. ExercicesExercice 8.1 (Deitel [3])Identifier et corriger les erreurs de chacune des propositions suivantes :

(a) final int TAILLE_TABLEAU = 10; TAILLE_TABLEAU = 15;

(b) Sachant que : int [] a = new int[10]; for (int i=0; i<a.length; i++) a[i] = 1;

(c) Sachant que : int [][] b = { { 1, 2}, {3, 4}};b[1, 1] = 5;

Exercice 8.2Qu'affiche le programme suivant ?

public class Test { public static void echanger(int i, int j) { int aux = i; i = j; j = aux; } public static void main(String[] args) { int a = 1, b = 2; echanger(a, b); System.out.println("a = " + a); System.out.println("b = " + b); int[] tab = new int[2]; tab[0] = 1; tab[1] = 2; echanger(tab[0],tab[1]); System.out.println(tab[0]+ " " + tab[1]); }}

112/286

Page 113: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 8 - Les tableaux en Java

8. Séance de TP N°8 : COLLECTIONNER UN NOMBRE FIXE D'OBJETS – AMÉLIORATION DE LA CALCULETTE [3]

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 8h

Objectif : Apprendre à utiliser les tableaux en Java.

8.1. Travail demandé

On se propose de reprendre la calculette du TP précédent. On souhaite que cette dernière conserve en mémoire la liste des 5 dernières opérations effectuées, et que l'affichage des résultats fasse apparaître ces opérations. Les objets OperationMathematique utilisés par Calculette seront stockés dans un tableau tel que les documents fournis le proposent.

Facultatif : Réitérer en remplaçant le tableau par un tableau dynamique (classe java.util.Vector) afin de réaliser un stockage dynamique des opérations.

8.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne ;• Extraits des documentations :

• Classe Calculette :public class Calculette extends java.lang.ObjectAmélioration de la calculette du TP précédent avec gestion d'un tableau des N dernières opérations effectuées. Elle utilise : String : Représente une chaîne de caractères dont le contenu ne peut être modifié. System : Contient plusieurs attributs et méthodes utiles au dialogue avec le système d'exploitation.

113/286

Figure 30: Diagramme de classes pour la Calculette [3].

Page 114: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 8 - Les tableaux en Java

Version: 1.3 Author: Alain Lebret

Field Summary

protected int compteur compteur d'opérations effectuées. Permet de se positionner dans le tableau.

protected OperationMathematique[]

operations tableau contenant les opérations à effectuer

Constructor Summary

Calculette(int uneTaille) Création d'une calculette permettant de stocker un nombre uneTaille d'opérations mathématiques.

Method Summary

void calculer(java.lang.String uneOperation) Effectue le calcul souhaité

java.lang.String toString() Redéfinition de la méthode toString permettant de retourner des informations sur l'objet sous la forme d'une chaîne de caractères.

114/286

Page 115: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 9 - Les Exceptions

CHAPITRE 9 - LES EXCEPTIONS

Une exception correspond à un événement anormal ou inattendu. Les exceptions sont des instances de sous-classes des classes java.lang.Error, pour des erreurs graves qui devront généralement conduire à l'arrêt du programme et, java.lang.Exception, pour des événements inattendus qui seront souvent traités sans que cela provoque l'arrêt du programme. Un grand nombre d'exceptions sont définies dans l'API Java et sont « lancées » par des méthodes de l'API. Comme nous allons le voir, deux mécanismes principaux permettent d'acquitter une exception. Soit on la traite, soit on la relaye à la méthode appelante.

1. Traitement de l'exceptionSi l'on décide de traiter une exception, il nous faut alors définir un certain nombre de blocs d'instructions. Pour ce faire, trois mots réservés sont à notre disposition : try, catch et finally que nous allons étudier dans les paragraphes suivants.

1.1. Le mot réservé try

Il permet de spécifier une section de code sur laquelle on s'attend à ce qu'une exception soit « levée ». La syntaxe est simple :

try { // Section de code pouvant entraîner une exception. }

1.2. Le mot réservé catch

Celui-ci, sert à spécifier le code à exécuter pour une exception (ou une catégorie) donnée. La syntaxe est la suivante :

catch (XXXException e) { // Code exécuté dans le cas où l'exception a été levée. }

1.3. Le mot réservé finally

Enfin, le mot réservé finally permet d'introduire un code de traitement d'exception, qui sera de toute manière exécuté.

Exemple complet :try { // des lignes de code susceptibles de lever une exception}catch (XXXException e) { // traitement de l'exception e}finally {

115/286

Page 116: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 9 - Les Exceptions

// traitement plus général}

1.4. Quelques exceptions

Le tableau suivant propose quelques exemples d'exceptions que l'on peut avoir à traiter. ExceptionIOException

NullPointerExceptionOutOfMemoryExceptionArithmeticExceptionClassCastException

ArrayIndexOutOfBoundsExceptionnegativeArraySizeException

Les méthodes String toString() et void printStackTrace() sont applicables sur des objets dérivés de la classe Exception.

2. Relayer l'exception avec throwsMaintenant, le problème est de savoir ce que l'on doit faire d'une exception que l'on n'aurait pas su traiter. Il faut dans ce cas la relayer à la méthode appelante. Plus généralement, si une méthode est susceptible de lever une exception, et si celle-ci ne peut la traiter, elle se doit de prévenir le système qu'elle relaye ce traitement. Pour ce faire nous utilisons le mot réservé throws qui permet d'avertir le système qu'une certaine catégorie d'exceptions ne sera pas traitée localement.

Dans l'exemple suivant, il s'agit de l'ensemble des exceptions liées aux entrées/sorties. void operer() throws IOException { // ...}

Le problème est de savoir ce qui se passe, si une exception est levée et qu'elle n'est traitée par aucune méthode. Dans ce cas, c'est la JVM qui la traite et affiche sa représentation sous forme d'une chaîne de caractères (la méthode toString() est appelée) avant de redonner la main.

3. Définir ses propres exceptionsIl est possible de définir ses propres exceptions en créant des classes qui devrons dériver de la classe Exception. Au point de vue de la conception d'une application, il est souvent nécessaire d'y incorporer des exceptions. L'exemple suivant définit un exception qui sera levée dans le cas où une note d'examen serait invalide, comme 21 si les notes doivent être comprises entre 0 et 20.

public class MonException extends Exception { public String toString() { return ("Aucune note n'est valide"); }}

116/286

Page 117: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 9 - Les Exceptions

4. Lever une exception avec throwLa question que l'on peut alors se poser, c'est comment fait-on pour déclencher une exception ? La réponse est simple : on utilise le mot réservé throw qui permet de lever une exception en instanciant un objet sur une classe dérivée de la classe Exception. Dans l'exemple suivant, le but est de calculer une moyenne de notes entières envoyées en arguments par la ligne de commande. Les arguments non entiers, donc erronés, doivent être éliminés. On utilise l'exception MonException précédente.

public class ExceptionThrow { public static int calculerMoyenne(String[] liste) throws MonException22 { int somme=0, entier, nbNotes=0; int i; for (i = 0; i < liste.length; i++) { try { entier = Integer.parseInt(liste[i]); somme += entier; nbNotes++; } catch (NumberFormatException e) { System.out.println("La "+(i+1)+" eme note n'est "+ "pas entiere"); } } if (nbNotes == 0) throw new MonException()23; return somme/nbNotes; }

public static void main(String[] arguments) { try { System.out.println("La moyenne est "+ calculerMoyenne(arguments)); } catch (MonException e) { System.out.println(e); } }}

Pour : java ExceptionThrow ha 15.5, on obtient : La 1 eme note n'est pas entiereLa 2 eme note n'est pas entiereAucune note n'est valide

22 la méthode calculerMoyenne() indique ainsi qu'il est possible qu'une de ses instructions envoie une exception de type MonException sans que celle-ci soit attrapée par un mécanisme try-catch. Il est obligatoire d'indiquer ainsi un éventuel lancement d'exception non attrapée, sauf pour les exceptions les plus courantes de l'API. Si vous oubliez de signaler par la clause throws l'éventualité d'un tel lancement d'exception, le compilateur vous le rappellera.

23 on demande ainsi le lancement d'une instance de MonException. Une fois lancée, l'exception se propagera. Ici, il y aura sortie de la méthode calculerMoyenne(), on se retrouve alors à l'appel de la méthode dans le main(), appel qui se trouve dans un bloc try suivi d'un bloc catch qui attrape les MonException(s) : l'instruction de ce bloc catch est effectuée. Le programme reprend alors son cours normal pour se terminer.

117/286

Page 118: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 9 - Les Exceptions

5. Séance de TP N°9 : GÉRER LES EXCEPTIONS – AMÉLIORATION DE LA CALCULETTE [4]

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 4h

Objectif : Apprendre à utiliser les exceptions en Java.

5.1. Travail demandé

On se propose de reprendre la calculette du TP précédent. On souhaite que cette dernière soit dotée d'une gestion des exceptions quant au format des chaînes de caractères qui spécifient les opérations à effectuer. Vous nommerez la classe de gestion des exceptions MauvaisFormatOperationException.

5.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

118/286

Page 119: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

CHAPITRE 10 - RÉALISER UNE INTERFACE GRAPHIQUE

Nous allons, dans ce chapitre, nous intéresser à la mise en oeuvre d'interfaces graphiques en Java (nous emploierons par la suite le terme IHM pour Interface Homme-Machine). Comme nous allons le voir, il est possible de créer toutes sortes d'IHM, et d'y inclure tous types de composants : des conteneurs, des boutons, des case à cocher, des zones de saisie de texte, des menus, des arbres déroulant, des boîtes de dialogue pour choisir des fichiers, des couleurs, etc.. Mais avant d'aller plus loin il nous faut être capable de faire un choix. En effet, deux API de définition d'IHM sont proposées : AWT (Abstract Window Toolkit) et Swing.

1. Introduction

1.1. Conteneurs et composants

Tout d'abord précisons un point de vocabulaire concernant ce que sont les composants graphiques et les conteneurs vis-à-vis d'une IHM :

• un composant (Component en anglais) est un objet qui peut être affiché sur l'écran et réagir aux actions de l'utilisateur. Pour illustrer, notez que le bouton de fermeture d'un application Ms-Windows en haut à droite est un composant ainsi que les boutons réguliers de l'application, les cases à cocher, les barres de défilement, etc.. Les composants ont des propriétés spéciales comme la couleur de fond, la police de caractères, etc. ;

• un conteneur (Container en anglais) est un composant pouvant contenir d'autres composants. Ainsi une fenêtre est un conteneur tandis qu'un bouton ne l'est pas. Un conteneur possède des propriétés spéciales comme l'utilisation d'un gestionnaire de mise en page (Layout Manager en anglais). Les conteneurs sont :

• la fenêtre java.awt.Window / javax.swing.JWindow : conteneur possédant certaines propriétés spéciales comme une boîte à outils pour créer des composants, le traitement des messages d'erreurs etc. ;

• le cadre java.awt.Frame / javax.swing.JFrame : conteneur possédant un barre de titre, une barre de menus, une bordure, un curseur et une icône. Le cadre est le conteneur le plus utilisé pour y placer des composants. Bien qu'un cadre soit une fenêtre spécialisée et soit l'élément utilisé dans nos exemples, nous le désignerons pourtant par le nom de fenêtre ;

• le panneau java.awt.Panel / javax.swing.JPanel : conteneur qui n'a pas d'existence en dehors d'un cadre mais permet la spécialisation de l'IHM par la création de zones internes au cadre ;

• la boite de dialogue java.awt.Dialog / javax.swing.JDialog : conteneur associé à un cadre et permettant d'interagir avec l'utilisateur ;

• l'applet Applet / JApplet : conteneur proche du panneau qui n'a d'existence que dans une page HTML affichée par un navigateur Web.

119/286

Page 120: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

1.2. L'API AWT

AWT est historiquement la première API graphique qui fut proposée. La contrainte d'AWT, ou son avantage, est que Java fait appel au système d'exploitation pour en afficher les composants graphiques. Pour cette raison, l'affichage de l'interface utilisateur d'un programme peut diverger sensiblement d'un OS à l'autre, car chacun dessine à sa manière un bouton. Attention, AWT garantit que la fonctionnalité recherchée sera dans tous les cas fournie, mais présentée différemment. AWT présente toutefois un avantage indéniable : en utilisant les objets graphiques de l'OS, l'affichage est plus rapide.Cette API est fournie dans les paquetages dont le nom commence par java.awt.

1.3. L'API Swing

Swing fut donc mis en place pour assurer une portabilité totale : un même pixel doit avoir la même couleur. Pour assurer cette portabilité, un bouton, ou tout autre composant graphique, est dessiné non plus par l'OS, mais par Java, ce qui en terme de temps d'exécution à un prix. En fait, bien que Swing soit présentée comme étant écrite uniquement en Java, ce dernier doit au moins être capable d'interagir avec l'OS pour pouvoir tracer un point, or c'est ce que font les classes de base d'AWT telles que Component, Container, etc.. En conséquence :

Tous les composants de Swing dérive d'une classe d'AWT et comportent (en général) un J majuscule comme préfixe du nom

de la classe AWT dérivée !

De même, les mécanismes de gestion d'événements et de positionnement des composants d'AWT restent utilisable avec Swing. Donc, certaines des classes déjà utilisées avec AWT sont toujours d'actualité. En terme d'importation de paquetages, cela implique qu'un programme Swing doit malgré tout importer certains des paquetages d'AWT.Enfin, Swing est basé sur un modèle appelé MVC (Modèle Vue Contrôleur) qui permet d’avoir plusieurs vues d’un ensemble de données :

• le Modèle contient les données ;• les Vues montrent à l’utilisateur tout ou partie des données du Modèle ;• le Contrôleur traite les événements reçus par le composant graphique.

1.4. Un exemple

Pour fixer les différences entre l'écriture d'une IHM sous AWT et sous Swing, voici l'exemple du plus petit programme de dessin libre en Java. Vous pouvez aussi vous reporter aux classes du premier TP de ce cours.

1.4.1. Version AWT

import java.awt.*;import java.awt.event.*;

/** * Application de dessin libre sous AWT.

120/286

Page 121: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

*/public class PeintreAwt extends Frame implements MouseMotionListener { private int x = -10; private int y = -10; private String nom;

/** * Construit une fenêtre AWT de titre <code>unNom</code> et * de dimensions 300x150. * @param unNom nom que l'on souhaite donner à la fenêtre. */ public PeintreAwt(String unNom) { nom = unNom; // La fenêtre écoute les événements des déplacements souris. this.addMouseMotionListener(this); this.setTitle(nom); // Titre de la fenêtre this.setSize(300,150); // Taille de la fenêtre this.setVisible(true); // Affichage de la fenêtre }

/** * Fonction de dessin des conteneurs (Window, Frame, * JFrame, Panel, JPanel, Applet, JApplet, Canvas, ...). * Cette dernière doit être redéfinie pour réaliser des dessins. * Ici on dessine un disque (à l'aide d'un oval) de centre (x, y) * et de rayon 4, rempli de la couleur par défaut, c'est-à dire * le noir. * @param g Contexte graphique dans lequel on souhaite dessiner. * Chaque Interface comporte un ou plusieurs contextes graphiques * (mémorisation de la couleur de fond, la couleur de dessin, la * police, etc.). */ public void paint(Graphics g) { g.fillOval(x, y, 4, 4); }

/** * Fonction de l'interface MouseMotionListener qui doit être * redéfinie. Elle permet de gérer les actions à réaliser lors du * déplacement d'une souris avec le bouton gauche maintenu * enfoncé. Ici on récupère les coordonnées de cette dernière afin * de les stocker dans nos deux attributs x et y. La fonction * paint est alors appelée avec en paramètre le contexte * graphique par défaut. * @param e Evénement de type MouseEvent ayant été détecté. */ public void mouseDragged(MouseEvent e) { x = e.getX(); y = e.getY(); this.paint(this.getGraphics()); System.out.println(nom + ": " + x + ", " + y); }

/** * Fonction de l'interface MouseMotionListener qui doit être * redéfinie. Elle permet de gérer les actions à réaliser une fois * qu'une souris a été déplacée. Ici, pour notre programme * de dessin, on ne la redéfinit pas. * @param e Evénement de type MouseEvent ayant été détecté.

121/286

Page 122: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

*/ public void mouseMoved(MouseEvent e) { }

public static void main(String [] arg) { PeintreAwt matisse = new PeintreAwt("Matisse"); PeintreAwt picasso = new PeintreAwt("Picasso"); }}

1.4.2. Version SWING

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

/** * Application de dessin libre sous Swing. */ public class PeintreSwing extends JFrame implements MouseMotionListener { private int x = -10; private int y = -10; private String nom;

/** * Construit une fenêtre Swing de titre <code>unNom</code> et * de dimensions 300x150. * @param unNom nom que l'on souhaite donner à la fenêtre. */ public PeintreSwing(String unNom) { nom = unNom; // La fenêtre écoute les événements de déplacements souris. this.addMouseMotionListener(this); this.setTitle(nom); // Titre de la fenêtre this.setSize(300,150); // Taille de la fenêtre this.setVisible(true); // Affichage de la fenêtre }

/** * Fonction de dessin des conteneurs (Window, Frame, JFrame, * Panel, JPanel, Applet, JApplet, Canvas, ...). * Cette dernière doit être redéfinie pour réaliser des dessins. * Ici on dessine un disque (à l'aide d'un oval) de centre (x, y) * et de rayon 4, rempli de la couleur par défaut, c'est-à-dire le * noir. * @param g Contexte graphique dans lequel on souhaite dessiner. * Chaque Interface comporte un ou plusieurs contextes graphiques * (mémorisation de la couleur de fond, la couleur de dessin, * la police, etc.). */ public void paint(Graphics g) { g.fillOval(x, y, 4, 4); }

/** * Fonction de l'interface MouseMotionListener qui doit être * redéfinie. Elle permet de gérer les actions à réaliser lors du

122/286

Page 123: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

* déplacement d'une souris avec le bouton gauche maintenu * enfoncé. Ici on récupère les coordonnées de cette dernière afin * de les stockées dans nos deux attributs x et y. La fonction * paint est alors appelée avec en paramètre le contexte * graphique par défaut. * @param e Evénement de type MouseEvent ayant été détecté. */ public void mouseDragged(MouseEvent e) { x = e.getX(); y = e.getY(); this.paint(this.getGraphics()); System.out.println(nom + ": " + x + ", " + y); }

/** * Fonction de l'interface MouseMotionListener qui doit être * redéfinie. Elle permet de gérer les actions à réaliser une * fois qu'une souris a été déplacée. Ici, pour notre programme * de dessin, on ne la redéfinit pas. * @param e Evénement de type MouseEvent ayant été détecté. */ public void mouseMoved(MouseEvent e) { }

public static void main(String [] arg) { PeintreSwing matisse = new PeintreSwing("Matisse"); PeintreSwing picasso = new PeintreSwing("Picasso"); }}

1.5. Les sujets abordés par ce chapitre

Pour appréhender au mieux ce vaste sujet qu'est la conception d'interface graphique en Java, je vous propose dans une premier temps d'étudier deux concepts fondamentaux utilisés aussi bien en AWT qu'en Swing : la gestion des événements en Java et les stratégies de positionnement des composants graphiques. Nous terminerons avec un tour d'horizon des différents composants.

2. La gestion des événements en JavaLe langage Java propose un mécanisme clairement établit pour le traitement d'événements. Ce mécanisme repose sur la notion d' « auditeurs » (Listener en anglais).En fait, ce mécanisme n'a pas été le premier proposé par le langage. En effet, la première version du JDK proposait un tout autre mécanisme de gestion des événements qui s'est avéré problématique à utiliser dans le cas de « grosses » applications. Dès la version 1.1 du JDK, le second mécanisme fut donc proposé. Dans ce cours nous ne traiterons que de ce dernier modèle (le premier étant maintenant largement déprécié) !Ce qui faut aussi savoir, c'est que ce mécanisme de gestion d'événements n'est en fait pas directement rattaché à une quelconque API d'interfaces graphiques. Au contraire, il se veut indépendant de tout contexte. On peut donc traiter les événements Java en réponse à des clics souris, mais aussi en réponse à tout autre activité se déroulant au sein de la JVM.Bien entendu, comme en Java tout est objet, les événements le sont aussi. D'où deux classes particulières : java.util.EventObject et java.awt.AWTEvent. La première correspond à la classe d'événements la plus générique et dérive directement de java.lang.Object. De la seconde

123/286

Page 124: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

dérive tous les événements d'AWT et de Swing : c'est donc la classe mère des événements dans les IHM.La classe java.util.EventObject possède une méthode fort intéressante : getSource(). Elle permet de récupérer l'objet sur lequel l'événement est initialement intervenu. En conséquence, lorsque l'on traite un événement, nous pouvons toujours savoir quel est l'objet initiateur du traitement.Pour traiter les événements, il sera toujours nécessaire d'importer au moins les deux paquetages java.awt.* et java.awt.event.*.

2.1. Traitement d'événements simples

Nous allons maintenant regarder comment savoir qu'un événement a eu lieu, et comment nous pouvons y répondre. Pour cela, il nous faut définir un auditeur, puis enregistrer ce dernier sur un objet susceptible de déclencher l'événement. Ainsi, si l'événement apparaît, il pourra être traité par l'auditeur.

2.1.1. Définition d'un auditeur

Un auditeur est un objet qui possède au moins une méthode qui pourra être invoquée si l'événement attendu apparaît. En fait, un auditeur doit remplir un contrat bien particulier. Dans ce but, il se doit d'implémenter un comportement, la plupart du temps en implémentant une interface, ou bien en dérivant de certaines classes.Il existe plusieurs types d'auditeurs, donc plusieurs types d'interfaces. Toutes ces interfaces ont un nom se terminant par Listener (par exemple ActionListener, WindowListener, etc.). Selon l'interface, il y a une ou plusieurs méthodes à implémenter.A titre d'exemple, considérons l'événement ActionEvent : cet événement est par exemple déclenché dès lors que l'on actionne un bouton soit en cliquant dessus avec la souris, soit en utilisant le clavier. Pour être capable d'écouter et de traiter un tel événement, un objet auditeur se doit d'implémenter une interface nommée ActionListener. Si vous êtes curieux et que vous étudiez l'interface ActionListener, vous remarquerez qu'elle ne contient qu'une unique méthode à implémenter : actionPerformed(). Cette méthode doit donc contenir le code à exécuter si jamais l'on clique sur un bouton.

Exemple :import java.awt.*;import java.awt.event.*;

public class PousseMoi implements ActionListener { public static void main(String argv[]) { new PousseMoi(); }

/** Le constructeur de la classe PousseMoi */ public PousseMoi() { Frame f = new Frame("Ma fenêtre"); Button b = new Button("Pousse Moi");

f.add(b); // On ajoute le bouton dans la fenêtre f.pack(); // On optimise la taille des composants f.setVisible(true); // On affiche la fenêtre }

/** gestionnaire d'événements défini dans ActionListener */

124/286

Page 125: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

public void actionPerformed(ActionEvent event) { System.out.println("Bouton " + event.getSource() + " cliqué ! "); }}

Cependant, lorsque l'on exécute ce code, rien ne se passe. Pourquoi ?

2.1.2. Enregistrement d'un auditeur

Nous avons défini comme auditeur, l'objet de classe PousseMoi susceptible de répondre à un clic sur le bouton. Mais qui a informé le bouton qu'il fallait avertir l'auditeur si on lui cliquait dessus ? Absolument personne ! Le problème est que chaque auditeur doit être enregistré auprès des objets qu'il est censé écouter : Un auditeur peut écouter plusieurs sources d'événements et une source d'événement peut alerter plusieurs auditeurs. L'exemple suivant enregistre l'auditeur à une source d'événements.

import java.awt.*;import java.awt.event.*;

public class PousseMoi implements ActionListener { public static void main(String argv[]) { new PousseMoi(); }

/** Le constructeur de la classe PousseMoi */ public PousseMoi() { Frame f = new Frame("Ma fenêtre"); Button b = new Button("Pousse Moi"); b.addActionListener(this);

f.add(b); // On ajoute le bouton dans la fenêtre f.pack(); // On optimise la taille des composants f.setVisible(true); // On affiche la fenêtre }

/** gestionnaire d'événements défini dans ActionListener */ public void actionPerformed(ActionEvent event) { System.out.println("Bouton " + event.getSource() + " cliqué ! "); }}

Maintenant, il est possible de cliquer sur le bouton. Un message est affiché en confirmation sur la console. Notons que l'objet source de l'événement est aussi affiché sur la console. Qu'a permis de faire la méthode addActionListener() ? En fait, elle a simplement permis de rajouter un auditeur au bouton. A chaque fois que le bouton est actionné, il cherche maintenant à invoquer la méthode actionPerformed() sur chacun de ses auditeurs. ActionListener pouvant être utilisé comme un type, le compilateur ne dit rien et tout fonctionne. Cependant, l'exemple du code précédent peut se ré-écrire différemment : en effet un objet d'écoute peut être créé exclusivement dans ce but. Comme les générateurs d'interfaces graphiques utilisent ces mécanismes, regardons-les de plus près.

125/286

Page 126: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

2.1.3. Définition de classes privées

Une première solution consiste à définir des classes privées simplement dédiées au traitement des événements. Deux façons de coder peuvent alors être retenues.

import java.awt.*;import java.awt.event.*;

public class PousseMoi implements ActionListener { public static void main(String argv[]) { new PousseMoi(); }

/** Le constructeur de la classe PousseMoi */ public PousseMoi() { Frame f = new Frame("Ma fenêtre"); Button b = new Button("Pousse Moi"); b.addActionListener(new GestionnaireEvenements());

f.add(b); // On ajoute le bouton dans la fenêtre f.pack(); // On optimise la taille des composants f.setVisible(true); // On affiche la fenêtre }}

class GestionnaireEvenements { /** gestionnaire d'événements défini dans ActionListener */ public void actionPerformed(ActionEvent event) { System.out.println("Bouton " + event.getSource() + " cliqué ! "); }}

ou encore :import java.awt.*;import java.awt.event.*;

public class PousseMoi implements ActionListener { public static void main(String argv[]) { new PousseMoi(); }

/** Le constructeur de la classe PousseMoi */ public PousseMoi() { Frame f = new Frame("Ma fenêtre"); Button b = new Button("Pousse Moi"); b.addActionListener(new GestionnaireEvenements());

f.add(b); // On ajoute le bouton dans la fenêtre f.pack(); // On optimise la taille des composants f.setVisible(true); // On affiche la fenêtre }

private class GestionnaireEvenements { /** gestionnaire d'événements défini dans ActionListener */ public void actionPerformed(ActionEvent event) { System.out.println("Bouton " + event.getSource() + " cliqué ! "); }

126/286

Page 127: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

}}

2.1.4. Définition de classes anonymes

Cette quatrième méthode, déroutante à première vue, permet malgré tout une écriture simplifiée du programme. Il est conseillé de bien l'assimiler puisqu'elle est fréquemment utilisée. En fait, la méthode addActionListener() à besoin d'un objet de type ActionListener. On en dérive donc une classe anonyme qui redéfinit quelques méthodes, ici une, et qui est directement utilisée pour créer un unique objet d'écoute.

import java.awt.*;import java.awt.event.*;

public class PousseMoi implements ActionListener { public static void main(String argv[]) { new PousseMoi(); }

/** Le constructeur de la classe PousseMoi */ public PousseMoi() { Frame f = new Frame("Ma fenêtre"); Button b = new Button("Pousse Moi"); b.addActionListener(new GestionnaireEvenements(){ /** gestionnaire d'événements défini dans ActionListener */ public void actionPerformed(ActionEvent event) { System.out.println("Bouton " + event.getSource() + " cliqué ! "); } });

f.add(b); // On ajoute le bouton dans la fenêtre f.pack(); // On optimise la taille des composants f.setVisible(true); // On affiche la fenêtre }}

Notons qu'une classe anonyme ne peut servir qu'à générer un unique objet. Dans ce cas, on associe un auditeur à une unique source d'événements.

2.1.5. Les classes d'adaptation

Dans les exemples précédents, les choses étaient simples : l'interface utilisée ne définie qu'une seule méthode. Mais en est-il de même pour les autres types d'événements ? Eh bien non. Les événements de fenêtre en sont un bon exemple : en effet une seule interface est définie pour tout ce qui peut arriver sur une fenêtre (ouverture de fenêtre, fermeture, iconification, etc.). Au total, ce sont sept méthodes qui y sont définies. Or, n'oublions pas qu'en implémentant une interface, il est nécessaire d'implémenter chacune de ses méthodes, sans quoi la classe est abstraite, et ce même si certaines ne sont pas utiles. Une solution intéressante est de coupler les mécanismes de classe anonyme et de classe d'adaptation. Une classe d'adaptation est une classe qui implémente une interface en fournissant, pour chaque méthode, un corps vide. L'intérêt est évident : si nous avons besoin de fournir du code pour une unique méthode de l'interface, nous ne devrons donner, dans la définition de la classe anonyme, que le code de cette méthode. Pour les autres, elles sont déjà implémentées, même si leur code est inexistant : le corps est vide.

127/286

Page 128: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

Une remarque : toutes les classes d'adaptation ont sensiblement le même nom que leur interface associée. Il suffit de remplacer le mot Listener par Adapter. Ainsi l'interface WindowListener possède une classe associée, nommée WindowAdapter. Autre remarque, si une interface du JDK ne possède qu'une seule méthode abstraite, aucune classe d'adaptation n'y est rattachée. L'exemple suivant illustre cette notion en proposant une méthode chargée de fermer l'application lorsqu'on clique sur le bouton de fermeture de la fenêtre.

import java.awt.*;import java.awt.event.*;

public class PousseMoi implements ActionListener { public static void main(String argv[]) { new PousseMoi(); }

/** Le constructeur de la classe PousseMoi */ public PousseMoi() { Frame f = new Frame("Ma fenêtre"); Button b = new Button("Pousse Moi"); b.addActionListener(new GestionnaireEvenements(){ /** gestionnaire d'événements */ public void actionPerformed(ActionEvent event) { System.out.println("Bouton " + event.getSource() + " cliqué ! "); } }); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent event) { System.exit(0); } }); f.add(b); // On ajoute le bouton dans la fenêtre f.pack(); // On optimise la taille des composants f.setVisible(true); // On affiche la fenêtre }}

2.2. Types d'événements supportés par AWT et Swing

Pour clore ce paragraphe, nous allons présenter quelques autres types d'événements qu'il est possible de traiter : la liste n'est bien entendue pas exhaustive !

2.2.1. Gestion du clavier

Commençons par la gestion du clavier : à tout composant, objet de la classe Component, il est possible d'enregistrer un KeyListener. Toute classe implémentant cette interface se doit de fournir le code de trois méthodes : keyPressed(), keyReleased() et keyTyped(). Ces trois dernières doivent prendre en paramètre un objet de classe KeyEvent.

a) Intercepter des frappes de touches

128/286

Page 129: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

Voici le code d'une application faisant apparaître la fenêtre ci-dessus. Observons l'utilisation de l'instruction requestFocus(), sans laquelle le point de mire (focus) serait probablement sur le bouton, ce qui empêcherait les frappes des touches d'être interceptées.

import java.awt.*;import java.awt.event.*;

public class EssaiTouche extends Frame implements KeyListener24, ActionListener { Button effacer = new Button("effacer");

public EssaiTouche(String titre) { super(titre); addKeyListener(this); addWindowListener(new WindowAdapter()25 {26

public void windowClosing(WindowEvent e)27 { System.exit(0); } }); add("South",effacer); effacer.addActionListener(this); setSize(300,300); setVisible(true); requestFocus()28; }

public void keyPressed(KeyEvent evt){}

24 l'interface java.awt.event.KeyListener permet, à condition de ne pas oublier d'utiliser la méthode addKeyListener(KeyListener) définie dans java.awt.Component, « d'entendre les actions sur les touches » lorsque le point de mire est dans la fenêtre. Cette interface déclare les méthodes ;

public void keyPressed(KeyEvent evt); public void keyReleased(KeyEvent evt); public void keyTyped(KeyEvent evt); qu'il faut donc définir dans notre classe. 25 on utilise une instance d'une classe héritée de la classe WindowAdapter(). La classe WindowAdapter hérite directement de

la classe Object et implémente l'interface WindowListener. Elle redéfinit en conséquence les nombreuses méthodes de cette interface, mais attribue des corps sans instruction à chacune d'entre elles.

26 on va à partir de cette accolade définir la classe héritée de WindowAdapter utilisée ici. 27 on redéfinit cette méthode ; c'est cette version de windowClosing() qui sera exécutée quand un événement de type

WindowEvent se produira. 28 cette instruction demande à ce que le point de mire (focus) soit dans la fenêtre principale, et est nécessaire pour que les frappes

des touches soient interceptées. Elle deviendrait inutile si on supprimait le bouton.

129/286

Figure 31: EssaiTouche : interception des frappes de

touches du clavier.

Page 130: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

public void keyReleased(KeyEvent evt){} public void keyTyped(KeyEvent evt) { Graphics g = getGraphics();

if (evt.getKeyChar() == 'r') { g.setColor(Color.red); g.drawOval(100,120,100,100); } else if (evt.getKeyChar() == 'b') { g.setColor(Color.blue); g.drawOval(100,120,100,100); } else if (evt.getKeyChar() == 'v') { g.setColor(Color.green); g.drawOval(100,120,100,100); } }

public void actionPerformed(ActionEvent evt) { if (evt.getSource() == effacer) { repaint(); requestFocus(); } }

public void paint(Graphics g) { FontMetrics ft = getFontMetrics(getFont()); int hauteur = ft.getHeight(); int x = 10, y = 40;

g.drawString("taper r pour voir un cercle rouge",x,y); y += hauteur; g.drawString("taper b pour voir un cercle bleu",x,y); y += hauteur; g.drawString("taper v pour voir un cercle vert",x,y); }

public static void main (String[] args) { EssaiTouche fenetre = new EssaiTouche("touches"); }}

b) Intercepter des frappes composées de touches

On peut intercepter comme nous l'avons vu dans l'exemple précédent les frappes sur les touches alphabétiques ou numériques.On peut aussi intercepter l'utilisation d'associations de ces touches avec des touches modificatrices ("CTRL", "SHIFT", "META" et "ALTCTRL") ou avec les boutons enfoncés de la souris. Toutes les touches du clavier peuvent aussi être utilisées, en particulier les touches de fonction ; les touches sont identifiées par des constantes analogues à celles que nous présentons ci-après (VK_R pour le R, VK_BACK_SPACE, VK_TAB, VK_COMMA, VK_F3, VK_UP, VK_PAGE_UP, VK_HOME, VK_ESCAPE, etc.) ; l'ensemble de ces constantes est consultable dans la documentation de la classe java.awt.event.KeyEvent.

Voici un exemple en java 1.1. import java.awt.*;import java.awt.event.*;

130/286

Page 131: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

//pour traiter les événements des touchespublic class Touches extends Frame implements KeyListener { Color couleur = Color.blue; int hauteur = 150; boolean plein = false;

public Touches(String titre) { super(titre); addKeyListener (this); setSize(400,400); setVisible(true); }

public void keyPressed(KeyEvent evt) { if (evt.getKeyCode() == KeyEvent.VK_R) { couleur = Color.red; if (evt.isControlDown()29) plein = true; else plein = false; } else if (evt.getKeyCode() == KeyEvent.VK_B) { couleur = Color.blue; if ((evt.getModifiers()30 & InputEvent.BUTTON1_MASK) != 0) plein = true; else plein = false; } else if (evt.getKeyCode() == KeyEvent.VK_ENTER) hauteur = 200; else if (evt.getKeyCode() == KeyEvent.VK_TAB) hauteur = 150; repaint(); }

public void keyTyped(KeyEvent evt) {} public void keyReleased(KeyEvent evt) {}

public void paint(Graphics g) { Font font = new Font("TimesRoman", Font.BOLD|Font.ITALIC,14); FontMetrics ft = getFontMetrics(font); int h = ft.getHeight(); int x = 10, y= 10;

g.setFont(font); g.drawString("taper r pour voir un cercle rouge",x,y); y += h; g.drawString("taper CTRL + r pour voir un disque rouge",x,y); y += h; g.drawString("taper b pour voir un cercle bleu",x,y); y += h; g.drawString("bouton gauche + b pour voir un disque bleu",x,y); y += h; g.drawString("barre d'espace pour descendre le cercle",x,y); y += h; g.drawString("tabulation pour repositionner le cercle",x,y); g.setColor(couleur); if (plein) g.fillOval(150, hauteur, 100, 100); else g.drawOval(150, hauteur, 100, 100);

29 méthode de la classe abstraite java.awt.event.InputEvent dont hérite la classe keyEvent 30 méthode de la classe abstraite InputEvent qui retourne un int décrivant le type de touche de modification éventuellement

utilisé, qui peut être une composition des touches de "CTRL", "SHIFT", "META" et "ALT" ou des boutons de la souris.

131/286

Page 132: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

} public static void main (String[] args) { Touches fenetre = new Touches("touches"); }}

2.3. Gérer la souris

Une autre interface utile à implémenter est MouseListener. Elle définit cinq méthodes prenant toutes en paramètre en objet de type MouseEvent : mouseClicked(), mouseEntered(), mouseExited(), mousePressed() et mouseReleased(). Bien entendu, tout objet AWT peut enregistrer un MouseListener. De même pour l'interface MouseMotionListener que nous avons déjà rencontrée dans le tout premier exemple de ce chapitre.

a) Utiliser les « clics » de la souris

En cliquant, nous dessinons un petit cercle noir ou bien de la couleur sélectionnée. En essayant les trois boutons de la souris, et en appuyant aussi en même temps sur la touche "SHIFT", il est possible de constater une différence.

Voici le code de l'application ci-dessus. import java.awt.*;import java.awt.event.*;import java.util.*;

public class EssaiClic extends Frame implements ItemListener, MouseListener31 { java.awt.List liste = new java.awt.List(); Color couleur = Color.black; Vector memoire = new Vector(); // tableau dynamique (objet)

31 l'interface java.awt.event.MouseListener correspond aux événements de type MouseEvent. Cette interface déclare les méthodes :

public void mousePressed(MouseEvent evt); public void mouseReleased(MouseEvent evt); public void mouseEntered(MouseEvent evt); public void mouseExited(MouseEvent evt); public void mouseClicked(MouseEvent evt); qu'il faut donc définir dans notre classe.

132/286

Figure 32: EssaiClic : interception des clics souris.

Page 133: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

public EssaiClic() { setLayout(null)32; liste.add("rouge"); liste.add("vert"); liste.add("bleu"); liste.addItemListener(this); liste.setSize(50,50); liste.setLocation(10,30); add(liste); addMouseListener(this); }

public void itemStateChanged(ItemEvent evt) { couleur = getCouleur(liste.getSelectedItem()); }

public void mousePressed(MouseEvent evt) { CercleGraphique c; int rayon = 5;

if ((evt.getModifiers() & MouseEvent.BUTTON2_MASK) != 0) rayon = 10; else if ((evt.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) rayon = 15; else rayon = 10; c = new CercleGraphique(rayon,evt.getX(),evt.getY(),couleur); c.dessiner(getGraphics()); memoire.addElement(c); }

public void mouseReleased(MouseEvent evt) {} public void mouseEntered(MouseEvent evt) {} public void mouseExited(MouseEvent evt) {} public void mouseClicked(MouseEvent evt) {}

Color getCouleur(String s) { if (s == null) return Color.black; else if (s.equals("rouge")) return Color.red; else if (s.equals("vert")) return Color.green; else if (s.equals("bleu")) return Color.blue; return Color.black; }

public void paint(Graphics g) { Enumeration33 lesCercles = memoire.elements()34;

while (lesCercles.hasMoreElements()) ( ( CercleGraphique )lesCercles.nextElement() ). dessiner(getGraphics()); }

32 on demande ici à ne pas avoir de gestionnaire de répartition. Il faudra donc gérer soi-même la position et la taille de tous les sous-composants graphiques.

33 l'interface java.util.Enumeration contient des déclarations de méthodes : public abstract boolean hasMoreElements(); public abstract Object nextElement(); nécessaires pour effectuer une énumération d'objets. 34 la méthode elements() de la classe java.util.Vector retourne une instance de Enumeration qui permet d'énumérer les

éléments d'une instance de Vector.

133/286

Page 134: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

public static void main(String[] argv) { EssaiClic monCadre = new EssaiClic(); monCadre.setSize(300,300); monCadre.setVisible(true); }}

La classe CercleGraphique est définie comme suit :import java.awt.*; // Pour Color et Graphics

class CercleGraphique extends Cercle implements Tracable{ int x, y; Color35 couleur;

CercleGraphique(int rayon, int x, int y, Color couleur) { super(rayon); this.x = x; this.y = y; this.couleur = couleur; }

public void dessiner36(Graphics g) { if (getRayon() < DIM_MAX37) { g.setColor(couleur); g.drawOval(x-getRayon(),y-getRayon(),2*getRayon(),2*getRayon()); } }}

puis sa classe mère Cercle :class Cercle { private int rayon;

Cercle(int rayon) { this.rayon = rayon; }

public static double circonference(Cercle cercle) { return 2*Math.PI*cercle.rayon; }

public double circonference() { return 2*Math.PI*rayon; } public int getRayon() { return rayon; }}

Et enfin l'interface Tracable :interface Tracable { static final int DIM_MAX = 500; void dessiner(java.awt.Graphics38 g);}

35 on utilise ici une classe de java.awt qui contient ce qui est nécessaire pour travailler avec des couleurs.36 lorsqu'une classe implémente une interface, elle doit définir toutes les méthodes dont les prototypes sont annoncés dans

l'interface, et leur attribuer le modificateur de visibilité public. 37 la constante DIM_MAX définie dans Tracable est connue dans CercleGraphique qui implémente Tracable. 38 la classe java.awt.Graphics définit, un ensemble de fonctionnalités permettant de dessiner dans une fenêtre graphique.

Pour dessiner dans une fenêtre, on passe nécessairement par une instance de Graphics. Entre autres, une instance de Graphics possède des méthodes destinées à tracer des lignes, des cercles, des chaînes de caractères, etc., et permet de choisir la couleur des traits, la police de caractères, etc..

134/286

Page 135: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

Un bon moyen de revoir l'héritage et la notion d'interface, non ?

2.4. Mais encore ?

Dans la volée, nous pouvons aussi parler de l'interface FocusListener : elle est utilisée pour répondre à la prise, ou à la perte, du focus par un élément d'AWT. Cette interface définie deux méthodes : focusGained() et focusLost() qui requièrent en unique paramètre un objet de type FocusEvent. Enfin, parlons aussi de l'interface TextListener qui permet de répondre à un changement de contenu d'une zone de saisie de texte. Cette interface définit une unique méthode : textValueChanged(). Un TextListener s'enregistre après d'un TextComponent, dont dérivent les classes TextArea et TextField. La classe d'événements associée est TextEvent. Pour plus d'informations sur les éléments présentés dans ce paragraphe, ainsi que ceux qui ne l'ont pas été, il est nécessaire de se reporter à la documentation de l'API du JDK.

3. Stratégies de placement des composantsNous allons dans ce paragraphe étudier un ensemble de classes chargées de résoudre les problèmes de positionnement des composants graphiques à l'intérieur des conteneurs. Ce groupe de classes, que sont les gestionnaires de positionnement (on utilisera aussi le terme de stratégies de placement), peut être utilisé soit dans une interface AWT, soit dans une interface Swing, sans distinction. En fait, il existe très peu de gestionnaires : cinq classes, plus quelques-unes introduites avec Swing. Pour les cinq premières, elles sont contenues dans le paquetage java.awt. Les dernières se trouvent dans le paquetage javax.swing. Remarquons que même en travaillant avec Swing, il est malgré tout obligatoire d'importer le paquetage java.awt pour pouvoir manipuler les gestionnaires de placement, car ces derniers ne sont pas ré-écrits dans Swing.

3.1. Affecter une stratégie de placement à un conteneur

La première chose à savoir, est qu'un gestionnaire de placement se définit au niveau d'un conteneur (objet de type java.awt.Container). Il est déconseillé de ne pas en définir un, car dans ce cas il est alors obligatoire de systématiquement positionner tous les composants au pixels près. Cette solution peu utilisable rend malgré tout des services dans certains cas.Bien entendu, une stratégie de placement se définie en créant un objet instance de l'une des classes de stratégie de placement. Une fois cet objet instancié, il ne reste plus qu'à l'affecter à un conteneur. Pour ce faire, on utilise la méthode setLayout(). Elle prend un unique paramètre : la référence du gestionnaire de mise en place. Si la valeur nulle est passée en paramètre, aucune stratégie de placement ne sera utilisée : il faut alors positionner les composants au pixel près.

3.2. La stratégie de placement FlowLayout

Cette première stratégie est certainement la plus utilisée et la plus simple à manipuler. Si elle est affectée à un conteneur, ce dernier va tenter de mettre le plus de composants possible sur une première ligne. Dès que la taille du conteneur ne permet plus d'insérer un nouveau composant, le gestionnaire utilise alors la seconde ligne, et ainsi de suite jusqu'au dernier composant.Il est de plus envisageable de configurer le gestionnaire en spécifiant si l'on souhaite que chaque

135/286

Page 136: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

ligne de composant soit alignée à gauche, à droite ou encore au centre. Cette configuration peut être mise en place soit à la construction de l'objet de positionnement, soit par la suite en utilisant la méthode setAlignment(). Des attributs de la classe FlowLayout sont prédéfinis : FlowLayout.LEFT, FlowLayout.CENTER, FlowLayout.RIGHT, etc..A titre d'exemple, considérons le programme suivant : il créé quelques composants et les place dans une fenêtre. Pour mettre en évidence le rôle de la stratégie de placement, il suffit de retailler la fenêtre, parce qu'en 300x300 on ne voit pas tout.

Le code associé :import java.awt.*;

public class Repartition { public static void main(String[] argv) { RepartiteurFlowLayout fenetre = new RepartiteurFlowLayout(); fenetre.setSize(300,300); fenetre.setVisible(true); }}

class RepartiteurFlowLayout extends Frame { FlowLayout repartiteur = new FlowLayout(); Button efface = new Button("efface"); Button demarre = new Button("demarre"); Checkbox relief = new Checkbox("relief"); Checkbox gras = new Checkbox("gras"); TextArea texte = new TextArea(); Canvas dessin = new Canvas();

public RepartiteurFlowLayout() { setLayout(repartiteur); this.add(dessin); this.add(efface); this.add(demarre); this.add(relief); this.add(gras); dessin.setBackground(Color.blue); this.add(texte); }}

136/286

Figure 33: Repartition : utilisation d'un FlowLayout.

Page 137: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

3.3. La stratégie de placement BorderLayout

Cette seconde stratégie permet d'opérer une division de l'espace utilisable au sein d'un conteneur. Par opposition au FlowLayout, le nombre de zones utilisable par cette stratégie est figé : un BorderLayout divise l'espace utilisable en cinq zones. Dans chacune de ces zones, il n'est possible de placer qu'un unique composant, mais rien n'empêche que certains de ces composants soient des conteneurs. Afin de mieux percevoir la topographie de ce gestionnaire de placement, étudions de plus près un exemple qui fournit le résultat suivant :

import java.awt.*;

public class Repartition { public static void main(String[] argv) { RepartiteurBorderLayout fenetre = new RepartiteurBorderLayout(); fenetre.setSize(300,300); fenetre.setVisible(true); }}

class RepartiteurBorderLayout extends Frame { BorderLayout repartiteur = new BorderLayout(); Button efface = new Button("efface"); Button demarre = new Button("demarre"); Checkbox gras = new Checkbox("gras"); TextField texte = new TextField(10); Button dessin = new Button("dessin");

public RepartiteurBorderLayout() { this.setLayout(repartiteur); this.add(dessin, BorderLayout.CENTER); this.add(efface, BorderLayout.NORTH); this.add(demarre, BorderLayout.SOUTH); this.add(gras, BorderLayout.WEST); dessin.setBackground(Color.blue); this.add(texte, BorderLayout.EAST); }}

Comme nous pouvons le remarquer dans l'exemple, nous ajoutons au conteneur cinq composants. Chacun de ces composants est inséré dans une zone définie par le BorderLayout : un bouton dans la zone supérieure (BorderLayout.NORTH), un autre bouton au sud (BorderLayout.SOUTH), une case à cocher à gauche (BorderLayout.WEST), une zone de saisie de texte à gauche (BorderLayout.EAST) et enfin un bouton au centre (BorderLayout.CENTER). Notons que l'on

137/286

Figure 34: Repartition : utilisation d'un BorderLayout.

Page 138: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

spécifie la zone dans laquelle doit être inséré le composant en invoquant la méthode add() sur un conteneur. Autre chose : il n'est pas obligatoire d'utiliser toutes les zones : dans ce cas-là, le gestionnaire de positionnement s'adaptera automatiquement. Si nous sommes un peu curieux, nous pouvons retailler la fenêtre. Que constatons-nous ? Eh bien, chacun des composants se retaille aussi pour occuper 100% de la superficie de la zone qui lui est associée. Les zones de saisie de texte et le bouton deviennent difforme. A priori, nous pourrions penser qu'une telle stratégie n'est pas utilisable, mais c'est tout le contraire. Elle peut être fort utile à la condition d'y placer d'autres conteneurs (Panel / JPanel). Ainsi, sur chaque conteneur, nous pouvons définir à nouveau une stratégie de placement, et groupe de composants par groupe, nous affinons ainsi la structure de l'interface graphique.

3.4. La stratégie de placement GridLayoutOn a utilisé une instance de GridLayout pour faire un petit tableur très rudimentaire. La moyenne des notes reportées est effectuée et arrondie à la partie entière par défaut.

Le gestionnaire de répartition GridLayout permet de répartir des éléments dans une grille régulière dont on peut préciser le nombre de lignes et de colonnes. Notre exemple utilise ce gestionnaire. En voici le code :

import java.awt.*;import java.awt.event.*;

class GrilleSimple extends Panel { int nbL; int nbCol; TextField [][] tableau; Label[] resultat; Label[] nom; int i, j;

public GrilleSimple(int nbL, int nbCol) { this.nbL = nbL; this.nbCol = nbCol; tableau = new TextField[nbL][nbCol]; add(new Label(""));

for (j = 0; j < nbCol; j++) add(new Label("note " + (j+1), Label.CENTER));

add(new Label("moyenne", Label.CENTER)); nom = new Label[nbL]; resultat = new Label[nbL]; setLayout(new GridLayout(nbL + 1, nbCol + 2));

138/286

Figure 35: Repartition : utilisation d'un GridLayout.

Page 139: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

for (i = 0; i < nbL; i++) { nom[i] = new Label("Eleve " + (i+1)); add(nom[i]); for (j = 0; j < nbCol; j++) { tableau[i][j] = new TextField(); add(tableau[i][j]); } resultat[i] = new Label("",Label.CENTER); add(resultat[i]); } }

public void calculerMoyenne() { int somme; int nbTermes; int r;

for (int i = 0; i < nbL; i++) { somme = nbTermes = 0; for (int j = 0; j < nbCol; j++) { try { somme += Integer.parseInt(tableau[i][j].getText()); nbTermes++; } catch (NumberFormatException exc) {} } try { r = ( int )(somme / nbTermes); resultat[i].setText(Integer.toString(r)); } catch(ArithmeticException exc) { resultat[i].setText(""); } } }} public class Repartition extends Frame implements ActionListener{ Button moyenne = new Button("Calcul de la moyenne"); GrilleSimple grille;

public Repartition(int nbL, int nbCol) { setLayout(new BorderLayout()); grille = new GrilleSimple(nbL, nbCol); add("Center", grille); add("South", moyenne); moyenne.addActionListener(this); pack(); setVisible(true); }

public void actionPerformed(ActionEvent evt) { grille.calculerMoyenne(); } public static void main(String[] argv) { new Repartition(3,3); }

139/286

Page 140: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

}

3.5. La stratégie de placement GridBagLayoutL'exemple qui suit, n'illustre que le gestionnaire de répartition GridBagLayout. Un gestionnaire de répartition de type java.awt.GridBagLayout est plus compliqué que ceux que nous avons vu jusqu'à présent, mais donne beaucoup plus de souplesse. Néanmoins, il n'est pas facile à utiliser et demande de l'habitude. Le principe est d'utiliser une grille virtuelle et de définir pour chaque sous-composant graphique un ensemble de paramètres qui permet de le positionner et de le dimensionner par rapport à la grille virtuelle et aussi par rapport aux autres sous-composants. Pour regrouper l'ensemble des composants, on utilise une instance de java.awt.GridBagConstraints. Ces champs sont :

• gridx et gridy qui donnent l'abscisse et l'ordonnée du sous-composant dans la grille.

• gridwidth et gridheight qui donnent la largeur et la hauteur du sous-composant dans la grille.

• fill qui indique dans quelle direction doit éventuellement augmenter le sous-composant. Quatre possibilités :

• GridBagConstraints.NONE ,

• GridBagConstraints.BOTH ,

• GridBagConstraints.HORIZONTAL ,

• GridBagConstraints.VERTICAL .

• anchor qui donne la position du sous-composant dans sa cellule de la grille lorsqu'il y a de l'espace autour de lui (huit constantes NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST sont prévues à cet effet dans la classe GridBagConstraints).

• ipadx et ipady qui indiquent le nombre de pixels dont on doit accroître le composant en plus de sa taille minimum.

• insets qui est une instance de la classe java.awt.Insets, objet destiné à contenir quatre entiers donnant les marges en haut, à gauche, à droite et en bas des sous-composant graphique.

• weightx et weighty sont deux paramètres de type double qui indiquent de distribuer l'espace supplémentaire selon les directions horizontales et verticales de façon prioritaire aux sous-composants qui ont les plus grandes valeurs de ces paramètres. Les valeurs données sont souvent 0 ou 1.

Enfin, il faut savoir que les contraintes liées à un sous-composant sont attachées à celui-ci et non pas contenues dans le gestionnaire de répartition. Cela entraîne par exemple qu'un même gestionnaire peut servir pour placer des sous-composants dans différents composants.

140/286

Page 141: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

Voici le code d'une application faisant surgir une fenêtre dont l'intérieur correspond à ce que l'on voit ci-dessus. Nous avons défini une classe de nom Repartition qui contient deux méthodes ajouter() utilisant la surcharge. Celles-ci permettent d'ajouter un sous-composant à un composant graphique utilisant un gestionnaire de répartition de type GridBagConstraint, en précisant tout ou partie des paramètres d'une instance de GridBagConstraints.

import java.awt.*;

public class Repartition { static void ajouter(Container composant, Component sousComposant, int x, int y, int largeur, int hauteur, int typeElargissement, int typePosition, int etalementHorizontal,int etalementVertical, int espacementHaut, int espacementBas, int espacementGauche, int espacementDroite, double poidsHorizontal, double poidsVertical) { GridBagConstraints contraintes = new GridBagConstraints();

contraintes.gridx = x; contraintes.gridy = y; contraintes.gridwidth = largeur; contraintes.gridheight = hauteur; contraintes.fill = typeElargissement; contraintes.anchor = typePosition; if (espacementHaut + espacementBas + espacementGauche + espacementDroite > 0) contraintes.insets = new Insets(espacementHaut,espacementBas, espacementGauche,espacementDroite); contraintes.weightx = poidsHorizontal; contraintes.weighty = poidsVertical; (( GridBagLayout )composant.getLayout()). setConstraints(sousComposant,contraintes); composant.add(sousComposant); }

141/286

Figure 36: Repartition : utilisation d'un GridBagLayout.

Page 142: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

static void ajouter(Container composant, Component sousComposant, int x, int y, int largeur, int hauteur, double poidsHorizontal, double poidsVertical) { ajouter(composant,sousComposant,x,y,largeur,hauteur, GridBagConstraints.BOTH, GridBagConstraints.NORTHWEST,0,0,0,0,0,0, poidsHorizontal,poidsVertical); }

public static void main(String[] argv) { Grille fenetre = new Grille(); fenetre.setSize(300,300); fenetre.setVisible(true); }}

class Grille extends Frame { GridBagLayout repartiteur = new GridBagLayout(); Button efface = new Button("efface"); Button demarre = new Button("demarre"); Checkbox relief = new Checkbox("relief"); Checkbox gras = new Checkbox("gras"); TextArea texte = new TextArea(); Canvas dessin = new Canvas();

public Grille() { setLayout(repartiteur); Repartition.ajouter(this,dessin,0,0,2,2,1,1); Repartition.ajouter(this,efface,2,0,1,1,GridBagConstraints.NONE, GridBagConstraints.SOUTH,0,0,10,10,10,10,0,0); Repartition.ajouter(this,demarre,2,1,1,1,GridBagConstraints.NONE, GridBagConstraints.NORTH,0,0,10,10,10,10,0,0); Repartition.ajouter(this,relief,0,2,1,1,GridBagConstraints.BOTH, GridBagConstraints.SOUTHWEST,0,0,10,10,10,10,0,0); Repartition.ajouter(this,gras,0,3,1,1,GridBagConstraints.BOTH, GridBagConstraints.NORTHWEST,0,0,10,10,10,10,0,0); dessin.setBackground(Color.blue); Repartition.ajouter(this,texte,1,2,2,2,1,1); }}

3.6. La stratégie de placement CardLayout

Il s'agit d'étudier l'utilisation d'un java.awt.CardLayout, gestionnaire de répartition qui permet d'avoir plusieurs composants graphiques tels que l'un d'entre eux seulement soit visible à un moment donné. On utilise la classe java.awt.CardLayout, que nous vous encourageons à consulter en ligne.

142/286

Page 143: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

Voici le code de l'application ci-dessus. import java.awt.*;import java.awt.event.*;

public class Repartition extends Frame implements ActionListener { Button precedent = new Button("precedent"); Button suivant = new Button("suivant"); Button leMessage = new Button("message"); Panel pCartes = new Panel(); TextArea zone = new TextArea("Un bel essai"); Label message = new Label("Bonjour",Label.CENTER); Panel panneau = new Panel(); CardLayout repartiteur = new CardLayout(); Panel p = new Panel();

public Repartition() { setLayout(new BorderLayout(5,5)); pCartes.setLayout(repartiteur); pCartes.add("zone",zone); pCartes.add("panneau",panneau); pCartes.add("message",message); add("Center",pCartes); precedent.addActionListener(this); suivant.addActionListener(this); leMessage.addActionListener(this); p.setLayout(new FlowLayout(FlowLayout.CENTER,5,5)); p.add(precedent); p.add(suivant); p.add(leMessage); add("South",p); setSize(300,250); } public void actionPerformed(ActionEvent e) { Object obj = e.getSource();

if (obj==precedent) {repartiteur.previous(pCartes)39;} else if (obj==suivant) {repartiteur.next(pCartes)40;}

39 repartiteur.previous(pCartes); : on passe à la carte précédente. 40 repartiteur.next(pCartes); : on passe à la carte suivante. On pourrait aussi utiliser les méthodes first() et last()

de la classe CardLayout, qui donneraient la première ou la dernière carte.

143/286

Figure 37: Repartition : utilisation d'un CardLayout.

Page 144: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

else if (obj==leMessage) {repartiteur.show(pCartes,"message")41;} }

public static void main(String[] argv) { Repartition monCadre = new Repartition(); monCadre.setVisible(true); }}

Nous allons dans le paragraphe suivant, passer en revue un certain nombre de composants graphiques.

4. Les composants graphiques

4.1. Utilisation d'un bouton

On utilise les classes java.awt.Button / javax.swing.JButton pour lesquelles nous vous encourageons à reprendre les exemples du dessus.

4.2. Utilisation d'un label

Le label est un composant qui permet d'afficher un message figé dans un conteneur et ne possède aucune propriété d'audition des événements. Il est défini par les classes java.awt.Label / javax.swing.JLabel, et s'instancie de la manière suivante :

Label monLabel = new Label("Vitesse du port série : ");

4.3. Utilisation d'une « boîte à cocher »

On utilise les classes :– java.awt.Checkbox / javax.swing.JCheckbox et/ou– java.awt.CheckboxGroup / .

En effet, vous pouvez voir sur la figure ci-dessous deux sortes de boîte à cocher : • les boîtes de nom petit, moyen et grand sont regroupées dans un ensemble, comme

vous le verrez dans le programme, et cela entraîne qu'on ne peut cocher qu'une seule de ces cases. On parle aussi de « boutons radios ». Avec AWT, on utilise les classes java.awt.Checkbox et java.awt.CheckboxGroup, par contre avec Swing, ce sont les classes javax.swing.JRadioButton et javax.swing.ButtonGroup ;

• la boîte de nom disque est isolée et correspond donc à un choix binaire : on veut un disque ou bien un cercle. Avec AWT, on utilise la classe java.awt.Checkbox et avec Swing, javax.swing.JCheckbox.

41 repartiteur.show(pCartes,"message"); : on passe à la carte dont le nom est message.

144/286

Page 145: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

Remarque : Quel que soit le cas, la gestion des événements se fait au travers d'un auditeur de type ItemListener. Voici le code associé :

import java.awt.*;import java.awt.event.*;

public class EssaiCheckbox extends Frame implements ItemListener42 { public EssaiCheckbox() { setLayout(new FlowLayout(FlowLayout.CENTER,5,5)); petit.addItemListener(this)43; moyen.addItemListener(this); grand.addItemListener(this); disque.addItemListener(this); disque.setState(false); add(petit); add(moyen); add(grand); add(disque); } CheckboxGroup44 groupeACocher = new CheckboxGroup(); Checkbox petit= new Checkbox("petit",groupeACocher,true); Checkbox moyen= new Checkbox("moyen",groupeACocher,false); Checkbox grand= new Checkbox("grand",groupeACocher,false); Checkbox disque= new Checkbox("disque"); int petitRayon = 10; int moyenRayon = 50; int grandRayon = 100; int rayon = petitRayon; boolean faireDisque = false;

public void itemStateChanged(ItemEvent evt)45 {

42 l'interface java.awt.event.ItemListener contient la déclaration de la méthode itemStateChanged(). Elle permet, en l'implémentant, de définir des adaptateurs pour les boîtes à cocher et pour différents types de listes proposant des choix entre différents items.

43 cette instruction indique, que lorsqu'il y aura un changement sur le choix de la boîte petit, ce qui provoquera un événement de type ItemEvent (la création d'une instance de cette classe).

44 la classe est destinée à contenir plusieurs objets Checkbox. Une et une seule de ses « boîtes à cocher » sera cochée à un instant donné. Lorsque l'utilisateur coche une des boîtes, l'instance de CheckboxGroup regroupant les boîtes s'occupe de changer l'état des autres boîtes. La boîte du groupe cochée à l'initialisation peut être précisée comme dans notre programme à la création des boîtes.

45 cette méthode doit être définie parce que notre classe implémente l'interface ItemListener. C'est elle qui sera invoquée

145/286

Figure 38: Utilisation de boites à cocher.

Page 146: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

Graphics g = getGraphics();

if (evt.getSource() == petit) rayon = petitRayon; else if (evt.getSource() == moyen) rayon = moyenRayon; else if (evt.getSource() == grand) rayon = grandRayon; else if (evt.getSource() == disque) faireDisque =! faireDisque; else return; repaint()46; }

public void paint(Graphics g) { g.setColor(Color.red); if ( faireDisque ) g.fillOval(150-rayon,150-rayon,2*rayon,2*rayon); else g.drawOval(150-rayon,150-rayon,2*rayon,2*rayon); }

public static void main(String[] argv) { EssaiCheckbox monCadre = new EssaiCheckbox(); monCadre.setSize(300,300); monCadre.setVisible(true); }}

4.4. Utilisation d'une « boîte de liste »

Nous voyons aussi dans cet exemple la méthode de fermeture d'une fenêtre. Sélectionner un item de la boîte de liste provoque le dessin du cercle dans la couleur correspondante. Les classes utilisées sont java.awt.List sous AWT et javax.swing.JList sous Swing.

Voici le code d'une application faisant surgir une fenêtre dont l'intérieur correspond à ce que l'on voit ci-dessus.

import java.awt.*;import java.awt.event.*;

public class EssaiListe extends Frame implements ItemListener47,

lorsqu'une des boîtes sera cochée. cocher fassent ce qu'on attend d'elles. 46 on demande ainsi d'utiliser la méthode paint() pour redessiner totalement le composant graphique concerné.47 l'interface java.awt.event.ItemListener contient la définition de la méthode itemStateChanged() ; cette interface doit

146/286

Figure 39: Utilisation d'une boite de liste.

Page 147: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

WindowListener48 { Color couleur = Color.black; List liste = new List(); public EssaiListe() { liste.add("rouge"); liste.add("vert"); liste.add("bleu"); liste.addItemListener(this)49; addWindowListener(this)50; setLayout(new FlowLayout(FlowLayout.CENTER,5,5)); add(liste); }

public void itemStateChanged(ItemEvent evt) { Graphics g = getGraphics(); couleur = getCouleur(liste.getSelectedItem()); g.setColor(couleur); g.drawOval(100,120,100,100); }

Color getCouleur(String s) { if (s == null) return Color.black; else if (s.equals("rouge")) return Color.red; else if (s.equals("vert")) return Color.green; else if (s.equals("bleu")) return Color.blue; return Color.black; }

public void windowActivated(WindowEvent e)51 {} public void windowDeactivated(WindowEvent e) {} public void windowOpened(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowClosed(WindowEvent e) {} public void windowClosing(WindowEvent e) { System.exit(0)52; }

public void paint(Graphics g) { g.setColor(couleur); g.drawOval(100,120,100,100); }

public static void main(String[] argv) { EssaiListe monCadre = new EssaiListe(); monCadre.setSize(300,300);

être implémentée pour utiliser la méthode addItemListener(ItemListener) dont le paramètre devra être notre fenêtre. 48 implémenter l'interface java.awt.event.WindowListener va nous permettre ici de fermer la fenêtre d'une façon habituelle.

Pour cela, il faudra utiliser la méthode addWindowListener(WindowListener) que l'on trouve dans la classe java.awt.Window dont hérite la classe java.awt.Frame et définir correctement la méthode windowClosing(WindowEvent) déclarée dans WindowListener.

49 cette instruction est indispensable pour que la liste puisse recevoir le clic d'un utilisateur demandant le changement de choix d'item.

50 ne pas oublier cette instruction... 51 cette définition de méthode ne faisant rien est la définition de la méthode de même prototype définie dans l'interface

WindowListener implémentée par notre classe. Le mécanisme des interfaces rend obligatoire de définir cette méthode. C'est la même chose pour les cinq méthodes définies juste après.

52 la méthode statique exit() de la classe java.lang.System fait sortir définitivement de l'application et ferme ici la fenêtre. Cette méthode est appelée ici lorsque l'on demande de fermer la fenêtre grâce au menu, ou bien en cliquant dans le coin en haut à gauche.

147/286

Page 148: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

monCadre.setVisible(true); }}

Cet exemple permet aussi de monter une manière de fermer une fenêtre. On utilise pour cela, dans la méthode addWindowListener() appliquée à notre fenêtre, une instance d'une classe héritée de la classe WindowAdapter. La classe WindowAdapter hérite directement de la classe Object et implémente l'interface WindowListener. Elle redéfinit en conséquence les nombreuses méthodes de cette interface, mais attribue des corps sans instruction à chacune d'entre elles. On redéfinit « sur place » la méthode windowClosing() comme vous pouvez le voir sur l'exemple.

4.5. Utilisation d'une « boite de choix »

Une boîte de choix joue un peu le même rôle qu'un menu déroulant ou qu'une boîte de liste. Nous pouvons voir ci-dessous le code d'une application qui en affiche une. Beaucoup de points dans l'utilisation d'un tel composant sont communs avec l'utilisation d'une boîte de liste. Nous ne les commentons pas. Les classes utilisées sont java.awt.Choice sous AWT et javax.swing.JComboBox sous Swing.

import java.awt.*;import java.awt.event.*;

class EssaiChoix extends Frame implements ItemListener { Choice choix = new Choice(); EssaiChoix() { choix.addItem("noir"); choix.addItem("rouge"); choix.addItem("vert"); choix.addItem("bleu"); choix.addItem("jaune"); choix.setForeground(Color.black); choix.addItemListener(this); setLayout(new FlowLayout(FlowLayout.CENTER,5,5)); add(choix); }

public void itemStateChanged(ItemEvent evt) { setForeground(getCouleur(choix.getSelectedItem()))53; paint(getGraphics()); }

Color getCouleur(String s) { if (s.equals("noir")) return Color.black; else if (s.equals("rouge")) return Color.red; else if (s.equals("vert")) return Color.green; else if (s.equals("bleu")) return Color.blue; else if (s.equals("jaune")) return Color.yellow; return Color.black; } public void paint(Graphics g) { g.drawOval(100,120,100,100); }

53 on demande à ce que la couleur d'avant-plan, c'est-à-dire la couleur par défaut avec laquelle un dessin est effectué, ou encore la couleur qui sera transmise à l'objet Graphics obtenu lors d'un appel à la méthode getGraphics(), corresponde à la couleur sélectionnée.

148/286

Page 149: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

public static void main(String[] argv) { EssaiChoix monCadre = new EssaiChoix(); monCadre.setSize(300,300); monCadre.setVisible(true); }}

4.6. Utilisation d'une « zone de saisie de texte »

On saisie le rayon du cercle que l'on peut alors tracer. Pour définir une zone de texte dans laquelle l'utilisateur peut écrire, il y a deux catégories possibles :

• les classes java.awt.TextField / javax.swing.JTextField : pour une seule ligne de texte ;

• les classes java.awt.TexteArea / javax.swing.JTextArea : pour plusieurs lignes de texte.

On peut aussi utiliser les classes java.awt.Label / javax.swing.JLabel qui affichent une ligne de texte.

Voici le code de l'application qui fait surgir une fenêtre correspondant à la figure ci-dessus.

import java.awt.*;import java.awt.event.*;

public class EssaiSaisie extends Frame implements ActionListener { Button bouton = new Button("trace"); TextField saisie = new TextField(6); int rayon = 30; public EssaiSaisie() { Panel p = new Panel(); setLayout(new BorderLayout(5,5))54; p.add(bouton)55; add("South",p)56;

54 on demande ici d'utiliser un gestionnaire de répartition de la classe java.awt.BorderLayout. Celui-ci permet de choisir de disposer les sous-composants graphiques au nord, à l'ouest, au sud, à l'est ou au centre du composant graphique ayant choisi ce répartiteur.

55 par défaut, un Panel dispose d'un FlowLayout[hgap=5,vgap=5,align=center] ; c'est donc celui-ci qui est utilisé par cette instruction.

56 on demande ainsi d'ajouter le Panel référencé par p au sud de notre fenêtre. Les cinq possibilités sont : add("North",p); add("West",p); add("South",p); add("East",p); add("Center",p);

149/286

Figure 40: Utilisation d'une zone de saisie de texte.

Page 150: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

bouton.addActionListener(this); p = new Panel(); saisie.setText(Integer.toString(rayon)); p.add(saisie); add("North",p); }

public void actionPerformed(ActionEvent e) { if (e.getSource() == bouton) { rayon = Integer.parseInt(saisie.getText()); getGraphics().drawOval(150-rayon,150-rayon,2*rayon,2*rayon); } }

public static void main(String[] argv) { EssaiSaisie monCadre = new EssaiSaisie(); monCadre.setSize(300,300); monCadre.setVisible(true); }}

4.7. Valider l'ajout d'un composant

Voyez notre illustration ci-dessous. Il s'agit d'ajouter un composant graphique à un composant déjà achevé.

L'instruction validate() de la classe java.awt.Container doit être utilisée lorsqu'on veut ajouter ou retirer un composant à un composant C (héritant de la classe Container) lorsque cette modification doit intervenir après la construction du composant C.validate() sert à enregistrer la modification et à redessiner l'ensemble du composant C en conséquence. Si cette instruction n'était pas utilisée ici, la zone de texte n'apparaîtrait pas lorsque l'on clique sur le bouton. Voici le code de l'application en java 1.1 effectuant ce que nous voyons ci-dessus.

import java.awt.*;import java.awt.event.*;

public class Ajout extends Frame { Button bouton = new Button("trace"); TextArea texte;

public Ajout() { add("North",bouton); bouton.addActionListener(new ActionListener()

150/286

Figure 41: Utilisation de validate().

Page 151: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

{ public void actionPerformed(ActionEvent e) { Graphics g = getGraphics(); if (e.getSource() == bouton) { texte = new TextArea("bonjour"); add("Center",texte); //l'instruction à ne pas oublier : validate(); } } }); } public static void main(String[] argv) { Ajout monCadre = new Ajout(); monCadre.setSize(200,200); monCadre.setVisible(true); }}

4.8. Utilisation d'un menu

Sélectionner un item dans le menu provoque le dessin du cercle dans la couleur correspondante. On utilise les classes java.awt.MenuBar / javax.swing.JMenuBar pour la barre de menus, java.awt.Menu / javax.swing.JMenu pour les différents menus associés à la barre et java.awt.MenuItem / javax.swing.JMenuItem pour les item proposés par un menu. Pour ajouter une barre de menu à un conteneur, ces derniers disposent d'une méthode setMenuBar() / setJMenuBar().

Remarquons dans le code ci-dessous l'utilisation d'un raccourci clavier pour la couleur « bleu ». Cette possibilité est offerte par la classe java.awt.MenuShortcut. Dans l'application de notre exemple, activer "CTRL + b" donne la couleur bleu au disque.

import java.awt.*;import java.awt.event.*;

public class EssaiMenu extends Frame implements ActionListener { MenuBar menuBar = new MenuBar(); Menu menuCouleur = new Menu("couleur"); MenuItem item1 = new MenuItem("rouge");

151/286

Figure 42: Utilisation d'un menu.

Page 152: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

MenuItem item2 = new MenuItem("vert"); MenuItem item3 = new MenuItem("bleu", new MenuShortcut('b')57); Color couleur = Color.black;

public EssaiMenu() { this.setMenuBar(menuBar); menuBar.add(menuCouleur); menuCouleur.add(item1); menuCouleur.add(item2); menuCouleur.add(item3); item1.addActionListener(this); item2.addActionListener(this); item3.addActionListener(this); }

public void actionPerformed(ActionEvent e) { Graphics g = getGraphics(); if (e.getSource() == item1) couleur = Color.red; else if (e.getSource() == item2) couleur = Color.green; else if (e.getSource() == item3) couleur = Color.blue; g.setColor(couleur); g.drawOval(100,100,100,100); }

public void paint(Graphics g) { g.setColor(couleur); g.drawOval(100,100,100,100); }

public static void main(String[] argv) { EssaiMenu monCadre = new EssaiMenu(); monCadre.setSize(300,300); monCadre.setVisible(true); }}

5. Complément sur les conteneurs

5.1. Utilisation d'une « boîte de dialogue »

Des clics de la souris tracent des cercles. Différents choix peuvent être faits pour ces cercles en passant par une boîte de dialogue ouverte grâce au bouton "dialoguer". Les classes java.awt.Dialog / javax.swing.JDialog sont utilisées pour faire surgir une fenêtre destinée à donner ou recevoir des informations. La classe Dialog dispose de deux constructeurs :

• public Dialog(Frame, boolean) : le premier paramètre indique la fenêtre à laquelle se rattache le dialogue et le second le choix d'avoir ou non un dialogue « modal », ce qui signifie qu'alors (si le paramètre vaut true) les entrées utilisateurs dans les autres fenêtres sont bloquées jusqu'à ce que la fenêtre de dialogue soit fermée.

• public Dialog(Frame, String boolean) : on indique en plus le titre de la fenêtre de dialogue.

57 constructeur de la classe java.awt.MenuShortcut qui modélise un raccourci clavier pour un menu. Voir directement cette classe pour ses différentes méthodes.

152/286

Page 153: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

Pour que l'utilisateur puisse fermer une fenêtre de dialogue, il faut implémenter l'interface WindowListener, et redéfinir la méthode windowClosing() comme ci-dessous :

public void windowClosing(WindowEvent e) { dispose();}

5.2. Utiliser des ascenseurs

Un ascenseur donne une représentation visuelle d'un niveau relatif. Par exemple, si on se donne une échelle de 1 à 100, le nombre 35 pourrait être représenté par une barre avec un curseur se trouvant aux 35 centièmes de la longueur. Un ascenseur contient en particulier :

• la valeur du minimum de l'intervalle (1 dans notre exemple) • la valeur du maximum de l'intervalle (100 dans notre exemple) • un entier qui donne la valeur courante (35 dans notre exemple).

Ici, comme pour beaucoup de traitements de textes ou d'images, on veut disposer d'une page virtuelle plus grande que le cadre de la fenêtre visible. Pour cela, on imagine que l'on a bien une page de la grandeur voulue et que la fenêtre est une lucarne devant cette page. Pour pouvoir se repérer, il suffit de connaître la position de la lucarne (notre fenêtre) dans la page virtuelle, ce que l'on fait en gérant les coordonnées du coin supérieur gauche de la fenêtre dans la page virtuelle. Ces deux coordonnées sont représentées, pour l'abscisse par un ascenseur horizontal, et pour l'ordonnée par un ascenseur vertical.

import java.awt.*;import java.awt.event.*;import java.util.*;

public class EssaiAscenseur extends Frame implements AdjustmentListener58,MouseListener { int decalageH59 = 0; int decalageV = 0; int largeurFenetre = 300; int hauteurFenetre = 200; Vector memoire = new Vector(); Scrollbar hbar60 = new Scrollbar (Scrollbar.HORIZONTAL, 0, ( int )(largeurFenetre * 0.9), 0, 500); Scrollbar vbar = new Scrollbar(Scrollbar.VERTICAL, 0, ( int )(hauteurFenetre * 0.9), 0, 1000); Color couleur = Color.magenta;

public EssaiAscenseur(String titre) { super(titre); addMouseListener(this);

58 interface qui comporte la méthode adjustmentValueChanged(), qui est redéfinie dans notre classe et qui sera utilisée ici pour gérer les ascenseurs.

59 abscisse du coin supérieur gauche de la fenêtre dans la page virtuelle. decalageV donne l'ordonnée de ce point. 60 on construit un ascenseur où les paramètres indiquent : Scrollbar.HORIZONTAL : une position horizontale (int)(hauteurFenetre * 0.9) : indique la longueur du curseur ; le rapport entre la longueur du curseur et la longueur de la barre sera égal au rapport entre ( int )(largeurFenetre * 0.9) et l'amplitude de l'intervalle de l'ascenseur (maximum moins minimum). 0 : la valeur initiale de la valeur contenue par l'ascenseur 0 : la valeur minimum de l'ascenseur 500 : la valeur maximum de l'ascenseur Ces choix correspondent à une page virtuelle de 500 pixels de large et telle que, au départ, la "lucarne", c'est-à-dire la fenêtre, sera

située à gauche.

153/286

Page 154: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

setLayout(new BorderLayout()); hbar.addAdjustmentListener(this)61; vbar.addAdjustmentListener(this); hbar.setBlockIncrement(( int )(largeurFenetre*0.9))62; vbar.setBlockIncrement(( int )(hauteurFenetre * 0.9)); add("East",vbar); add("South",hbar); setForeground(couleur); }

public void adjustmentValueChanged(AdjustmentEvent evt) { if (evt.getSource() == hbar) decalageH = evt.getValue()63; else if (evt.getSource() == vbar) decalageV = evt.getValue(); repaint(); }

public void mousePressed(MouseEvent evt) { CercleGraphique c; Graphics g = getGraphics(); int x = evt.getX(), y = evt.getY();

c = new CercleGraphique(10, x+decalageH64,y+decalageV, Color.red); g.setColor(couleur); g.fillOval(x-2065,y-20,40,40); memoire.addElement(c); } public void mouseReleased(MouseEvent evt) {} public void mouseEntered(MouseEvent evt) {} public void mouseExited(MouseEvent evt) {} public void mouseClicked(MouseEvent evt) {}

public void paint(Graphics g) { Enumeration lesCercles = memoire.elements(); CercleGraphique c; while (lesCercles.hasMoreElements()) { c =( CercleGraphique )lesCercles.nextElement(); g.fillOval(c.x-20-decalageH66,c.y-20-decalageV,40,40); } } public static void main (String[] args) { EssaiAscenseur fenetre = new EssaiAscenseur("Ascenseur"); fenetre.setSize(fenetre.largeurFenetre, fenetre.hauteurFenetre); fenetre.setVisible(true); }}

61 lorsqu'on agit sur l'ascenseur horizontal, c'est la méthode addAdjustmentListener() de l'instance this qui est utilisée. 62 on indique que, lorsque l'on clique dans la barre à droite du curseur, la valeur courante contenue par l'ascenseur augmente de

(int)(largeurFenetre * 0.9), et diminue d'autant si on clique à gauche. 63 lorsqu'on a manipulé l'ascenseur, et donc changé la valeur courante qu'il contient, on attribue ainsi la nouvelle valeur contenue

par hbar à decalageH. 64 on mémorise les coordonnées du cercle par rapport à la page virtuelle. 65 on dessine le cercle en donnant les coordonnées du coin supérieur gauche de son carré circonscrit de telle sorte que son centre

corresponde à l'endroit du clic. 66 x et y donnant ici les coordonnées du centre du cercle par rapport à la page virtuelle, on retrouve ainsi les coordonnées du coin

supérieur gauche de son carré circonscrit par rapport à la fenêtre réelle.

154/286

Page 155: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

5.3. Utilisation d'un conteneur avec ascenseurs

On reprend l'exemple précédent, et on obtient un résultat tout à fait comparable, mais on utilise la classe java.awt.ScrollPane (respectivement javax.swing.JScrollPane), classe définissant un conteneur destiné à contenir un seul composant, et muni d'ascenseurs horizontaux et verticaux. On a défini une classe Zone qui étend la classe Canvas, et dont une instance sera contenue dans un objet ScrollPane, et donc munie d'ascenseurs. C'est dans cet objet Zone que l'on effectue les dessins.

import java.awt.*;import java.awt.event.*;import java.util.*;

class Zone extends Canvas67 { ScrollPane parent; Vector memoire = new Vector(); Color couleur = Color.magenta;

public Zone(ScrollPane leParent) { parent = leParent; setSize(500, 1000); setForeground(couleur); addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent evt) { CercleGraique c; Graphics g = getGraphics(); int x = evt.getX(), y = evt.getY(); Point point = parent.getScrollPosition()68; Color col = new Color(Color.red); c = new CercleGraphique(20, x + point.x, y + point.y, col); memoire.addElement(c); g.fillOval(c.x - 20 - point.x, c.y - 20 - point.y, 40, 40); } }); } public void paint(Graphics g) { Enumeration lesCercles = memoire.elements(); CercleGraphique c; while (lesCercles.hasMoreElements()) { Point point = parent.getScrollPosition(); c = ( CercleGraphique )lesCercles.nextElement(); g.fillOval(c.x - 20 - point.x, c.y - 20 - point.y, 40, 40); } }}

//pour traiter les événements des touchespublic class EssaiScrollPane extends Frame { Zone zone; ScrollPane panneau = new ScrollPane();

67 cette classe du paquetage java.awt modélise une zone graphique appropriée pour faire du dessin. 68 cette méthode de la classe ScrollPane retourne un objet Point (classe du paquetage java.awt) ; un tel objet contient deux

attributs entiers, x et y. L'objet Point retourné par la méthode getScrollPosition() donne les coordonnées du coin supérieur gauche de la "lucarne".

155/286

Page 156: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

public EssaiScrollPane(String titre) { super(titre); panneau.setSize(300,200); zone = new Zone(panneau); panneau.add(zone); add(panneau); panneau. getHAdjustable()69.addAdjustmentListener (new AdjustmentListener() { public void adjustmentValueChanged(AdjustmentEvent evt) { zone.repaint(); } });

panneau.getVAdjustable().addAdjustmentListener (new AdjustmentListener() { public void adjustmentValueChanged(AdjustmentEvent evt) { zone.repaint(); } }); pack(); } public static void main (String[] args) { EssaiScrollPane fenetre = new EssaiScrollPane("Ascenseur"); fenetre.setVisible(true); }}

6. ConclusionNous n'avons fait qu'entrevoir dans ce chapitre, les fonctionnalités offertes par Java pour construire des IHM professionnelles. Nous avons d'ailleurs laissé de côté un grand nombre de classes de l'API Swing. Nous vous recommandons d'aller consulter aussi souvent que possible l'ensemble des références (ouvrages et Internet) que nous avons indiquées et surtout de tester par vous-même le maximum d'exemples.

7. Séance de TP N°10 : RÉALISER UNE INTERFACE GRAPHIQUE AVEC AWT – AMÉLIORATION DE LA CALCULETTE [5]

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 8h

69 cette méthode de la classe ScrollPane retourne un objet d'une classe implémentant l'interface Adjustable qui correspond à l'ascenseur horizontal. L'interface Adjustable modélise des objets qui contiennent une valeur numérique "ajustable" contenue dans un intervalle borné ; elle déclare entre autres la méthode addAdjustmentListener(). La classe ScrollPane implémente cette interface.

156/286

Page 157: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

Objectif : Apprendre à réaliser une interface graphique sous AWT.

7.1. Travail demandé

On se propose de reprendre la calculette du TP précédent afin que cette dernière bénéficie à présent d'une interface homme-machine graphique du type minimal fourni ci-après.

Utiliser les classes Calculette, Operation et OperationMathematique sans les modifier, afin de profiter des avantages de la programmation objet, puis créer une nouvelle classe nommée CalculetteAWT et utilisant la classe Calculette. Le diagramme de classes correspondant est le suivant :

7.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

8. Séance de TP N°11 : RÉALISER UNE INTERFACE GRAPHIQUE AVEC SWING – AMÉLIORATION DE LA CALCULETTE [6]

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

157/286

Figure 43: Exemple d'IHM AWT pour la Calculette.

Figure 44: Diagramme de classes de la Calculette [5].

Page 158: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 10 - Réaliser une Interface Graphique

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 4h

Objectif : Apprendre à réaliser une interface graphique sous Swing.

8.1. Travail demandé

On se propose de reprendre la calculette du TP précédent. On souhaite que cette dernière bénéficie à présent d'une interface homme-machine graphique utilisant les classes Swing, et du type minimal fourni ci-après.

Utiliser les classes Calculette, Operation et OperationMathematique sans les modifier afin de profiter des avantages de la programmation objet, puis créer une nouvelle classe nommée CalculetteSWING héritant cette fois-ci de JFrame, et utilisant la classe Calculette.

Facultatif : Améliorer l'ergonomie de l'interface graphique.

8.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

158/286

Figure 45: Exemple d'IHM Swing pour la Calculette.

Page 159: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

CHAPITRE 11 - SAISIR DES DONNÉES OU UTILISER DES FICHIERS

Il y a beaucoup de manières prévues en Java pour traiter des flux de données. Nous nous contenterons de donner quelques exemples d'utilisation. Notons avant tout, que quasiment toutes les classes de cette API sont localisées dans le paquetage java.io.*, à l'exception des classes de manipulation de flux sur sockets que nous trouverons dans java.net.*. et dont nous reparlerons dans le chapitre consacré au réseau. Nous terminerons ce chapitre par une description du concept de sérialisation des données sur un flux.

1. Généralités sur les flux de donnéesIl existe quatre classes « mères » abstraites, pour gérer les flux de données héritant directement d'Object : pour traiter des flux d'octets :

• la classe InputStream • la classe OutputStream

pour traiter de flux de caractères : • la classe Reader • la classe Writer

Beaucoup de classes héritent de ces classes de base. Nous ne traiterons que de celles qui sont utilisées dans les applications les plus courantes. Nous vous encourageons, si vous avez un cahier des charges utilisant des traitements plus spécifiques de fichiers, à consulter la documentation pour voir l'étendue des possibilités. Nous illustrerons aussi dans notre dernier exemple l'utilisation de la classe File.

2. Saisir des données envoyées par le clavierSi vous voulez lire des données envoyées tout simplement par le clavier, nous vous indiquons deux méthodes illustrées ci-dessous. Les deux programmes d'illustration ont pour but de lire des entiers envoyés par l'intermédiaire du clavier, et d'en faire la somme. Leurs cahiers des charges diffèrent uniquement sur la façon d'indiquer la fin de la saisie.

2.1. Première méthode

On utilise deux classes de java.io, la classe InputStreamReader et la classe BufferedReader. • La classe InputStreamReader admet un constructeur InputStreamReader(InputStream),

c'est-à-dire un constructeur qui prend en paramètre un flux d'entrée System.in (instance de la classe InputStream). Avec une instance de InputStreamReader, on ne peut plus ou moins lire que des caractères un à un.

• La classe BufferedReader a un constructeur qui prend en argument une instance de Reader dont hérite la classe InputStreamReader. Cette classe permet en particulier de lire une

159/286

Page 160: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

ligne d'un texte, mais en revanche, on ne peut pas lui demander de lire un entier, un double etc..

On lit ici ce qui est envoyé par le clavier ligne par ligne puis on découpe le contenu de chaque ligne avec un StringTokenizer pour récupérer les entiers attendus. L'utilisateur devra taper <Entrée> deux fois de suite pour interrompre la saisie. Voici le programme que nous proposons.

import java.io.*;import java.util.*;

public class SaisieClavier { public static void main (String[] argv) throws IOException, NumberFormatException { int somme = 0; String ligne; StringTokenizer st; BufferedReader entree = new BufferedReader( new InputStreamReader( System.in )); ligne = entree.readLine(); while ( ligne.length() > 0 ) { st = new StringTokenizer(ligne); while ( st.hasMoreTokens() ) somme += Integer.parseInt(st.nextToken()); ligne = entree.readLine(); } System.out.println("La somme vaut : "+somme); }}

Voici une exécution : java SaisieClavier3 12

La somme vaut : 6

2.2. Deuxième méthode

On utilise ici une instance de java.util.Scanner définie depuis la version 5.0 de Java, et qui est un analyseur rudimentaire de types de base ou de chaînes de caractères. Une instance de Scanner découpe ses entrées en éléments syntaxiques (tokens) séparés par défaut par des espaces. Un des constructeurs de la classe Scanner prend en paramètre un InputStream tel que System.in.

import java.util.Scanner;

public class SaisieClavierBis { public static void main (String[] argv) { int somme = 0; const String FIN = "fin"; Scanner entree = new Scanner( System.in ); System.out.println("Donnez vos entiers, terminez avec fin"); while ( entree.hasNext() ) { if ( entree.hasNextInt() )

160/286

Page 161: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

somme += entree.nextInt(); else if ( FIN.equals(entree.next()) ) break; } System.out.println("La somme vaut : "+somme); }}

Voici une exécution : Donnez vos entiers, terminez avec fin1 -2 bonjour 4 3 fin 2La somme vaut : 6

3. Rediriger la sortie standard vers un fichierDans certains cas, il peut être intéressant de rediriger la sortie standard stdout (terminal) vers un fichier. La classe System possède la méthode statique setOut() permettant de rediriger le flux de sortie.

System.setOut( new PrintStream( new BufferedOutputStream( new FileOutputStream("resultat.dat"))));

4. Manipuler les fichiers textes

4.1. Écrire dans un fichier texte

On utilise ici une instance de PrintWriter, dont un constructeur prend en argument un FileOutputStream. Il est possible d'utiliser avec une instance de PrintWriter les méthodes print() et println() de la même façon qu'avec System.out (qui est instancié depuis la classe PrintStream). La classe PrintWriter (qui hérite de la classe Writer) ne fait qu'améliorer la classe PrintStream (qui hérite de OutStream). On aurait pu plus simplement initialiser ecrivain par :

ecrivain = new PrintWriter(new FileOutputStream(arguments[0]));

mais alors les écritures n'utiliseraient pas de mémoire-tampon.

import java.io.*;

public class EcrivainFichierTexte { public static void main(String[] arguments) throws IOException { PrintWriter ecrivain; int n = 5;

ecrivain = new PrintWriter(new BufferedOutputStream( new FileOutputStream(arguments[0]))); ecrivain.println("bonjour, comment cela va-t-il ?"); ecrivain.println("un peu difficile ?");

161/286

Page 162: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

ecrivain.print("On peut mettre des entiers : "); ecrivain.println(n); ecrivain.print("On peut mettre des instances de Object : "); ecrivain.println(new Integer(36)); ecrivain.close(); }}

Après exécution, le fichier indiqué contient : bonjour, comment cela va-t-il ?un peu difficile ?On peut mettre des entiers 5On peut mettre des instances de Object : 36

4.2. Lire dans un fichier texte

4.2.1. Premier exemple

Il s'agit ici de lire un fichier de type texte et de reproduire ce qui est lu directement à l'écran. Un FileReader sert à lire dans un fichier de type texte. Un de ses constructeurs prend en paramètre un nom de fichier. Cet exemple est particulièrement simple.

import java.io.*;

public class LecteurFichierTexte { public static void main(String[] argv) throws IOException { BufferedReader lecteurAvecBuffer = null; String ligne;

try { lecteurAvecBuffer = new BufferedReader(new FileReader(argv[0])); } catch(FileNotFoundException exc) { System.out.println("Erreur d'ouverture"); } while ((ligne = lecteurAvecBuffer.readLine()) != null) System.out.println(ligne); lecteurAvecBuffer.close(); }}

A l'exécution, on obtient en sortie exactement le contenu du fichier dont le nom est indiqué sur la ligne de commande.

4.2.2. Deuxième exemple

Il s'agit maintenant de lire des entiers dans un fichier ne contenant que des entiers et d'en faire la somme. Les classes utilisées ici, l'ont déjà été dans les exemples précédents.

import java.io.*;import java.util.Scanner;

public class LecteurFichierTexteBis { public static void main (String[] argv) throws IOException { int somme = 0;

162/286

Page 163: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

Scanner entree = new Scanner(new File(argv[0])); while (entree.hasNextInt()) { somme += entree.nextInt(); } System.out.println("La somme vaut : "+somme); fichier.close(); }}

Avec un fichier contenant : 5 3 6 2 7-10 23

on obtient : La somme vaut : 36

5. Manipuler les fichiers binaires

5.1. Écrire dans un fichier binaire

Pour traiter des fichiers binaires, c'est-à-dire qui contiennent éventuellement autres choses que des caractères (des int écrits en binaire et pas comme des chaînes de caractères, etc.), on peut utiliser la classe DataOutputStream. Celle-ci possède un constructeur qui prend en paramètre une instance de la classe OutputStream. On aurait pu initialiser notre variable ecrivain par :

DataOutputStream(new FileOutputStream(arguments[0]));

Cela aurait été identique en apparence, mais si on compose comme il est fait ici avec une instance de BufferedOutputStream, les écritures dans le fichier utilisent une mémoire-tampon, ce qui à pour effet de gagner du temps. La classe OutputStream dispose de beaucoup de méthodes ; nous donnons des exemples ci-dessous pour les principales méthodes. Voici le programme que nous proposons :

import java.io.*;

public class EcrivainFichierBinaire { public static void main(String[] argv) throws IOException { DataOutputStream ecrivain; ecrivain = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(argv[0])));

ecrivain.writeUTF("bonjour"); ecrivain.writeInt(3); ecrivain.writeLong(100000); ecrivain.writeFloat((float)2.0); ecrivain.writeDouble(3.5); ecrivain.writeChar('a'); ecrivain.writeBoolean(false); ecrivain.writeUTF("au revoir"); System.out.println(ecrivain.size()); ecrivain.close(); }}

163/286

Page 164: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

Après l'exécution de la commande :java EcrivainFichierBinaire essai

on obtient à l’écran : 47

et le fichier essai est assez illisible.

5.2. Lire dans un fichier binaire

La classe DataInputStream nous permet de lire un fichier binaire. D'autres méthodes de cette classe pourront être consultées dans la documentation. Si on ne désire pas utiliser de mémoire-tampon, on peut initialiser la variable lecteur plus simplement par :

lecteur = new DataInputStream(new FileInputStream(arguments[0]));

import java.io.*;

public class LecteurFichierBinaire { public static void main(String[] argv) throws IOException { DataInputStream lecteur; lecteur = new DataInputStream(new BufferedInputStream(new FileInputStream(argv[0]))); System.out.println(lecteur.readUTF()); System.out.println(lecteur.readInt()); System.out.println(lecteur.readLong()); System.out.println(lecteur.readFloat()); System.out.println(lecteur.readDouble()); System.out.println(lecteur.readChar()); System.out.println(lecteur.readBoolean()); System.out.println(lecteur.readUTF()); lecteur.close(); }}

Pour la commande : java LireFichierBinaire essai

où le fichier essai est issu de l'exécution du programme de l'exemple précédent, on obtient : bonjour31000002.03.5afalseau revoir

6. Utiliser la classe FileOn se propose ici d'exploiter la classe java.io.File. Celle-ci permet de lister les fichiers d'un répertoire, de savoir si un fichier existe, de renommer un fichier, de supprimer un fichier, etc.. Le tableau suivant présente certaines des méthodes couramment utilisées. Pour chacune d'elles,

164/286

Page 165: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

nous donnons une courte description, mais pour de plus amples informations sur cette classe, il est toujours possible de se référer à sa documentation en ligne.

String getName(); Retourne le nom du fichier.String getPath(); Retourne la localisation du fichier en relatif.String getAbsolutePath(); Idem mais en chemin absolu.String getParent(); Retourne le nom du répertoire parent.boolean renameTo(File newFile); Permet de renommer un fichier.boolean exists() ; Est-ce que le fichier existe ? boolean canRead(); Le fichier est-t-il lisible ? boolean canWrite(); Le fichier est-t-il modifiable ? boolean isDirectory(); Permet de savoir si c'est un répertoire.boolean isFile(); Permet de savoir si c'est un fichier.long length(); Quelle est sa longueur (en octets) ?boolean delete(); Permet d'effacer le fichier.boolean mkdir(); Permet de créer un répertoire.

String[] list(); On demande la liste des fichiers localisés dans le répertoire.

Une partie des méthodes de la classe java.io.File sont illustrées ci-dessous. Pour exécuter notre programme, nous devons indiquer le nom d'un répertoire sur la ligne de commande. Nous indiquerons en fait le répertoire dans lequel il s'exécute de façon à vérifier que le fichier créé par le programme figurera bien ensuite dans celui-ci.

import java.io.*;

public class EssaiFile { public static void main(String[] argv) throws IOException { File repertoire, fichier = null , nouveauFichier; String[] listeFichiers; PrintWriter ecrivain; repertoire = new File(argv[0]); if ( !repertoire.isDirectory() ) System.exit(0);

fichier = new File("fichier.essai"); System.out.println("le fichier "+fichier.getName()+ (fichier.exists()?" existe":" n'existe pas")); //en sortie : le fichier fichier.essai n'existe pas

ecrivain = new PrintWriter(new FileOutputStream("fichier.essai")); ecrivain.println("bonjour"); ecrivain.close(); System.out.println("le fichier "+fichier.getName()+ (fichier.exists()?" existe":" n'existe pas")); //en sortie : le fichier fichier.essai existe System.out.println("Sa longueur est " + fichier.length()); //en sortie : Sa longueur est 8 System.out.println("Son chemin complet est \n " + fichier.getAbsolutePath());; //en sortie : //Son chemin complet est // /pub/frosch/coursJava/fichiersEtSaisies/fichier.essai System.out.println();

165/286

Page 166: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

listeFichiers = repertoire.list(); for (int i = 0;i < listeFichiers.length; i++) System.out.println(listeFichiers[i]); System.out.println();

nouveauFichier = new File("autre.essai"); fichier.renameTo(nouveauFichier); System.out.println("le fichier " + fichier.getName() + (fichier.exists()?" existe":" n'existe plus")); //en sortie : le fichier fichier.essai n'existe plus System.out.println("le fichier " + nouveauFichier.getName()+ (nouveauFichier.exists()?" existe":" n'existe pas")); //en sortie : le fichier autre.essai existe nouveauFichier.delete(); }}

A la sortie, on obtient, pour l'instruction java EssaiFile ../fichiersEtSaisies/ : le fichier fichier.essai n'existe pasle fichier fichier.essai existeSa longueur est 8Son chemin complet est /pub/frosch/coursJava/fichiersEtSaisies/fichier.essai

fichier.essaiEssaiFile.javafile.htmlEssaiFile.classSaisieClavier.javaSaisieFichier.javaEssaiStream.javaEssaiStreamBis.javaLireFichierBinaire.javageneralites.htmlEssaiWriter.javaEssaiFileReader.javaEssaiFileOutputStream.javalireFichierBinaire.htmlecrireFichierTexte.htmlecrireFichierBinaire.htmlsaisirAuclavier.htmllireFichierTexte.htmlEcrireFichierBinaire.javaEcrireFichierTexte.java

le fichier fichier.essai n'existe plusle fichier autre.essai existe

7. SérialisationLa sérialisation consiste à pouvoir prendre un objet en mémoire et à en sauvegarder l'état sur un flux de données (vers un fichier, par exemple). Ce concept permettra aussi de reconstruire, ultérieurement, l'objet en mémoire à l'identique de ce qu'il pouvait être initialement. La sérialisation peut donc être considérée comme une forme de persistance des données.

166/286

Page 167: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

7.1. Présentation du concept

7.1.1. Les problématiques

La sérialisation n'est pourtant pas aussi simple à mettre en oeuvre. La première difficulté réside dans le fait que les attributs (en effet, ce sont eux qu'il faut sauvegarder pour enregistrer l'état de l'objet) ne sont pas tous de type scalaire (int, float, double, etc.). Certains sont de type agrégat (sous-objet ou tableau). Or, en Java, les agrégats sont obligatoirement stockés sur le tas (la mémoire autre que la pile des opérandes) : ils sont donc inévitablement référencés. Que signifie alors la sérialisation d'un pointeur et ne serait-ce pas plutôt l'objet pointé qu'il faut enregistrer ? La seconde difficulté vient de la gestion par le mécanisme de sérialisation, de deux objets qui se pointent mutuellement, et du risque éventuel de les enregistrer indéfiniment.

7.1.2. Les solutions

En fait, il n'y a pas des solutions, mais une solution à tous ces problèmes : la réflexion. La réflexion permet par programmation, d'obtenir des informations sur un type chargé en mémoire. Ces informations permettent de décrire les attributs, les méthodes et les constructeurs de la classe considérée. Ces informations sont stockées dans les méta-classes. Une méta-classe est donc un type qui en identifie un autre. En fait, si l'on y réfléchit bien, les concepts de méta-classes et de réflexion sont les clés de voûte de tout le système Java (JNI (Java Native Interface), RMI (Remote Method Invocation), etc.).A titre d'information, le concept d'introspection peut être vu comme un aboutissement de la réflexion. Ce que traite en plus l'introspection, ce sont les conventions de codage définies en Java. Ainsi, à partir des méthodes exposées par une classe on peut aussi en déduire ses propriétés (les méthodes accesseurs get() et set()) et ses événements (les auditeurs). Mais cela n'apporte rien de plus pour ce qui est de la sérialisation.Pour que l'on puisse manipuler les méta-classes, il faut que le compilateur joigne la table des symboles au fichier bytecode généré. C'est ce que ne sait pas faire C++ : les méta-classes en sont donc plus difficiles à fournir (certain compilateurs y arrivent malgré tout en mode debug).Pour pouvoir utiliser la réflexion, l'environnement Java vous fournit la classe java.lang.Class, ainsi qu'un bon nombre d'autres classes utilitaires localisées dans le paquetage java.lang.reflect. L'exemple suivant affiche l'ensemble des attributs d'une classe en précisant si, pour chaque attribut, son type est scalaire ou s'il s'agit d'un agrégat.

import java.lang.reflect.*;

public class MetaDonnees { public static void main(String[] args) { Class metaClass = javax.swing.JButton.class; /* ou Class metaClass = new JButton().getClass(); */

Field attributs[] = metaClass.getFields(); for (int i = 0; i < attributes.length; i++) { boolean scalaire = attributs[i].getType().isPrimitive(); System.out.print(scalaire ? "Scalaire" : "Objet"); System.out.print(" : "+attributs[i].getName()+" de type "); System.out.println(attributs[i].getType()); } }}

167/286

Page 168: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

7.2. Le support de sérialisation en Java

7.2.1. Utilisation de la sérialisation

Maintenant que nous avons les pré-requis, nous pouvons nous focaliser sur la mise en oeuvre de la sérialisation via l'API de Java. Tout ce que nous devons savoir est localisé dans le paquetage java.io. Nous y trouvons notamment les classes : ObjectInputStream et ObjectOutputStream. Ces deux classes proposent, respectivement, les méthodes readObject() et writeObject() qui vont permettre la sérialisation et la désérialisation. L'exemple de code suivant présente le mécanisme. Pour ce faire, la méthode enregistrerFenetre() construit une fenêtre contenant différents composants graphiques (zone de saisie de texte, boutons, arborescence, etc.) puis enregistre l'objet sur le disque dur. A partir de là, tous les autres composants doivent aussi être sérialisés. La méthode chargerFenetre(), permet quant à elle l'opération inverse : elle reconstruit la fenêtre ainsi que tous les éléments initialement contenus. En guise de test, demandons d'abord à enregistrer une fenêtre. Regardons alors la taille du fichier obtenu sur le disque. Puis demandons à recharger la fenêtre un certain nombre de fois. Ensuite, entrons "quitter", pour sortir de la fenêtre.

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

public class Serialisation { private final static Reader reader=new InputStreamReader(System.in); private final static BufferedReader keyboard = new BufferedReader(reader);

/** * Permet de créer une fenêtre et de la sérialiser dans un fichier. */ public void enregistrerFenetre() throws IOException { JFrame window = new JFrame("Ma fenêtre"); JPanel pane = ( JPanel )window.getContentPane(); pane.add(new JLabel("Barre de status"), BorderLayout.SOUTH); pane.add(new JTree(), BorderLayout.WEST); JTextArea textArea = new JTextArea("Ceci est le contenu !!!"); textArea.setBackground(Color.GRAY); pane.add(textArea, BorderLayout.CENTER);

JPanel toolbar = new JPanel(new FlowLayout()); toolbar.add(new JButton("Ouvrir")); toolbar.add(new JButton("Enregistrer")); toolbar.add(new JButton("Couper")); toolbar.add(new JButton("Copier")); toolbar.add(new JButton("Coller"));

pane.add(toolbar, BorderLayout.NORTH); window.setSize(400,300);

FileOutputStream fos = new FileOutputStream("fenetre.obj"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(window); oos.flush(); oos.close(); }

168/286

Page 169: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

/** * Permet de reconstruire la fenêtre à partir des données du * fichier. */ public void chargerFenetre() throws Exception { FileInputStream fis = new FileInputStream("fenetre.obj"); ObjectInputStream ois = new ObjectInputStream(fis); JFrame window = ( Jframe )ois.readObject(); ois.close();

window.setVisible(true); }

/** * Permet de saisir différentes commandes. Testez plusieurs * chargements consécutifs : plusieurs fenêtres doivent apparaître */ public static void main(String[] args) throws Exception { Serialisation object = new Serialisation(); while ( true ) { System.out.print("Saisir le mode d'execution : "); String mode = keyboard.readLine(); if (mode.equalsIgnoreCase("quitter")) break; if (mode.equalsIgnoreCase("enregistrer")) object.enregisterFenetre(); if (mode.equalsIgnoreCase("charger")) object.chargerFenetre(); } System.exit(0); }}

7.2.2. Coder une classe sérialisable

Nous venons de voir comment enregistrer ou recharger un objet sur un flux de données. Nous allons, maintenant voir comment coder une classe sérialisable. En effet, par défaut, les classes ne permettent pas de sauvegarder l'état d'un objet sur un flux de données. Il faut explicitement le demander. Par contre, une grande majorité des classes du JDK version 1.4 et supérieures ont été définies comme étant sérialisables et c'est pour cette raison que l'exemple précédent a fonctionné sans difficulté.La classe est tout d'abord marquée comme étant sérialisable et cela en implémentant l'interface java.io.Serializabl e . Cette dernière ne contient aucune méthode : il n'y a donc rien d'autre à faire. L'extrait de code suivant montre une classe dont les attributs pourront être envoyés vers un flux de données.

import java.io.*;import java.util.*;public MaClasse implements Serializable { private String monPremierAttribut; private Date monSecondAttribut; private long monTroisièmeAttributs;

// . . . d'éventuelles méthodes . . .}

169/286

Page 170: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

Dans certains cas, nous pourrions avoir besoin qu'un attribut ne soit pas sérialisé pour une classe donnée. Cela est réalisable sans difficulté puisqu'il suffit de rajouter le qualificateur transient à l'attribut en question. Le code suivant définit une classe dont le second attribut ne sera jamais sérialisé.

import java.io.*;import java.util.*;public MaClasse implements Serializable { private String monPremierAttribut; private transient Date monSecondAttribut; // non sérialisé private long monTroisièmeAttributs;

// . . . d'éventuelles méthodes . . .}

7.2.3. Un piège à éviter

Pour terminer cette section, il est important de comprendre qu'il existe une source potentielle de fuites mémoire avec le mécanisme de sérialisation. En fait, il faut juste bien comprendre comment fonctionnent les classes ObjectInputStream et ObjectOutputStream. Pour détecter les cycles de pointeurs et donc éviter de sérialiser plusieurs fois un objet dans un flux, les deux classes précédentes gèrent des vecteurs (java.util.Vector). Un tel conteneur, peut contenir autant d'objets, c'est-à-dire de pointeurs, que ce qu'il y a comme mémoire disponible. A chaque fois qu'un objet est manipulé par l'intermédiaire de l'une des deux classes de flux d'objets, sa référence est stockée dans le vecteur associé. La méthode reset() permet de réinitialiser le vecteur contenu dans l'objet de flux. Ainsi, il ne croît pas indéfiniment et le programme n'arrive donc plus à une erreur de type java.lang.OutOfMemoryError. Nous devons absolument tenir compte de cette problématique, en comprenant bien qu'elle n'apparaît que si la connexion réseau est utilisée pour un grand nombre de transfert d'objets.

Exemple :try { OutputStream os = sockService.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(os);

while ( true ) {Message msg = this.creerMessage();

oos.writeObject(msg); oos.reset(); }}catch(Exception e) { e.printStackTrace();}

170/286

Page 171: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 11 - Saisir des données ou utiliser des fichiers

8. Séance de TP N°12 : TRAVAILLER AVEC LES FLUX D'ENTRÉE/SORTIE – AMÉLIORATION DE LA CALCULETTE [7]

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 8h

Objectif : Apprendre à travailler avec les flux d'entrées / sorties en Java.

8.1. Travail demandé

On se propose de reprendre la calculette du TP précédent. On souhaite que cette dernière bénéficie à présent d'une sauvegarde dans un fichier, des 5 dernières opérations effectuées. Choisir le type de fichier (texte, binaire ou pourquoi pas la sérialisation ?) à employer et utiliser l'héritage pour réaliser la classe CalculetteSauvegarde à partir de la classe Calculette comme l'indique le diagramme de classes ci-dessous.

Modifier la classe CalculetteAWT (ou CalculetteSWING) en conséquence.

8.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

171/286

Figure 46: Diagramme de classes de la Calculette [7].

Page 172: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 12 - Communiquer à travers un réseau

CHAPITRE 12 - COMMUNIQUER À TRAVERS UN RÉSEAU

Nous allons étudier dans ce chapitre les mécanismes à mettre en place pour pouvoir ouvrir une connexion réseau entre deux applications. Cette connexion sera établie à l'aide de sockets. Il en résulte plusieurs protocoles utilisables mais nous nous contenterons d'en aborder deux : la connexion utilisant le protocole UDP/IP et celle utilisant le protocole TCP/IP.

1. Quelques définitions• Le protocole UDP permet d'établir une connexion sur une socket, en mode non connecté.

Pour simplifier les choses, on peut dire qu'un programme va envoyer un ou plusieurs paquets d'informations (on parle de datagrammes) sur le réseau et qu'il n'a aucune garantie que ce paquet arrive à destination. Pire encore, s'il envoie plus d'un paquet, l'ordre de réception n'est pas garanti. Il est donc clair qu'il est à la charge du programmeur de l'application, de mettre en place un mécanisme assurant une connexion fiable.

• Le protocole TCP est lui, plus agréable dans le sens où il permet d'établir une connexion en mode connecté. On dit aussi que la communication est de type client/serveur. Une donnée envoyée est donc certaine d'arriver à bon port. Mieux si plusieurs données sont envoyées consécutivement, elles seront traitées de l'autre coté de la connexion, dans le même ordre.

Il est donc clair que le protocole TCP est plus simple à utiliser. Cependant, il assurera une connexion moins performante en terme de temps d'exécution. Afin de mettre en place une connexion, différentes classes vous sont proposées dans le paquetage java.net en fonction du protocole choisi. Nous développons par la suite les deux manières de communiquer.

2. Connexion en mode « non connecté »La connexion UDP en mode « non connecté » permet une communication entre deux machines par la transmissions de datagrammes. Elle consiste à :

• côté envoi 1. construire un paquet de données mis dans une instance de la classe

java.net.DatagramPacket, qui contiendra aussi l'adresse de destination. Autrement dit, on commence par écrire un message et le mettre dans l'enveloppe comportant l'adresse. La classe java.net.InetAdress, qui représente des adresses Internet, est utilisé pour contenir l'adresse du destinataire ;

2. créer une instance de la classe java.net.DatagramSocket, qui sera le coursier, et lui confier la charge d'amener le message à destination.

• côté réception 1. créer une instance de la classe DatagramSocket chargée d'attendre l'arrivée

d'un message sur le port de la machine où un message est attendu. Appelons socket cette instance ;

2. créer un paquet, instance de la classe java.net.DatagramPacket, avec un

172/286

Page 173: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 12 - Communiquer à travers un réseau

tampon vide susceptible de recevoir le message ;3. lorsque le message arrive, socket le reçoit ;4. Convertir le contenu du paquet reçu en une chaîne de caractères.

Le programme EnvoiMessage.java se charge d'envoyer un datagramme sur le port 4500 d'une machine dont le nom doit être indiquée sur la ligne de commande. Le message doit être donné sur une ligne après avoir lancé la commande. Le programme ReceptionMessage.java doit lui, être lancé sans argument sur la machine destinataire. Elle attend un message sur le port 4500 ; lorsque celui-ci est arrivé, elle le stocke dans une chaîne de caractères. Elle indique ensuite à l'écran l'adresse de la machine d'origine. Enfin, le message reçu est indiqué à l'écran. Nous avons peu géré les exceptions. Il faudra le faire pour des applications plus fiables.

Voici le programme EnvoiMessage :import java.net.*;import java.io.*;

public class EnvoiMessage { static final int port = 450070; public static void main(String argv[]) throws SocketException, IOException { if (argv.length != 1) { System.out.println("donnez le nom de la machine destinataire"); System.exit(0); } BufferedReader entree = new BufferedReader( new InputStreamReader(System.in)); String ligne = entree.readLine(); int longueur = ligne.length(); byte [] message = new byte[longueur]; InetAddress adresse = null71; DatagramSocket72 socket; try { adresse = InetAddress.getByName(argv[0])73; } catch(UnknownHostException exc) { System.out.println("destinataire inconnu"); } message = ligne.getBytes(); DatagramPacket envoi = new DatagramPacket(message, longueur, adresse, port)74; socket = new DatagramSocket()75; socket.send(envoi); }}

Voici le programme ReceptionMessage

import java.net.*;

70 port de la machine destinataire sur laquelle doit être reçu le message 71 cette classe de java.net représente des adresses Internet. 72 cette classe de java.net définit une socket qui peut recevoir ou envoyer des datagrammes. 73 cette méthode statique de la classe InetAddress retourne l'instance de InetAdress correspondant à une machine spécifiée

par son nom. 74 on fabrique le paquet à envoyer en donnant le message sous forme d'un tableau de byte, la longueur du message, l'adresse et le

port de destination. 75 il faut une socket destinée à recevoir un datagramme sur le port indiqué.

173/286

Page 174: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 12 - Communiquer à travers un réseau

import java.io.*;

public class ReceptionMessage { static final int port = 4500;

public static void main(String argv[]) throws SocketException, IOException { byte [] memoire = new byte[1000]; String texte; DatagramSocket socket = new DatagramSocket(port); DatagramPacket reception = new DatagramPacket(memoire, memoire.length)76; socket.receive(reception); texte = new String(memoire); System.out.println("Reception de la machine "+ reception.getAddress()77.getHostName()78+ " sur le port " +reception.getPort()+" :\n"+ texte ); }}

si on lance sur la machine gargantua la commande java ReceptionMessage

puis si, sur la machine folcoche, on lance la commande java EnvoiMessage gargantua

puis on envoie comment vas-tu ?

alors, sur la machine gargantua, on obtient :

Reception de la machine folcoche.3soft.fr sur le port 40404 :comment vas-tu ?

3. Connexion en mode « connecté »La connexion TCP en mode « connecté » permet une communication entre deux machines sur un modèle client-serveur. Dans notre exemple, un serveur doit attendre des clients pour leur rendre le service suivant : le client envoie un texte, ligne par ligne, et le serveur doit compter le nombre total de mots du texte. L'utilisateur du client transmet via la ligne de commande, le nom d'un fichier dont il veut compter les mots et le nom de la machine qui sert de serveur. Voici le programme côté client :

import java.net.*;import java.io.*;

76 il faut préparer un tampon pour recevoir le message. 77 la méthode getAdress() de la classe DatagramPacket retourne l'adresse de la machine dont un datagramme vient, ou vers

laquelle un datagramme va être envoyé. 78 la méthode getHostName() de la classe InetAdress retourne le nom de la machine d'une adresse Internet.

174/286

Page 175: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 12 - Communiquer à travers un réseau

import java.net.*;

public class Client { static final int port = 1000079;

public static void main(String[] argv) { BufferedReader lecteurFichier = null, entree = null; PrintStream sortie = null; String ligne = null; Socket socket = null;

try { socket = new Socket(argv[1],port)80; lecteurFichier = new BufferedReader(new FileReader(argv[0])); entree = new BufferedReader(new InputStreamReader( socket.getInputStream()))81; sortie = new PrintStream(socket.getOutputStream())82; while (( ligne = lecteurFichier.readLine() ) != null) sortie.println(ligne); sortie.println("xxxxxx")83; System.out.println(entree.readLine()); socket.close()84; } catch(IOException exc) { System.out.println("probleme a determiner"); } }}

Le programme serveur est un peu plus long ; globalement, on crée un serveur sous forme d'un thread. La méthode run() du serveur est une boucle sans fin d'attente des clients ; pour chaque client, le serveur ouvre une socket correspondant à la communication avec ce client puis instancie la classe Service destinée à rendre le service (recevoir un texte ligne par ligne et en compter les mots) sur cette socket. La classe Service ouvre un flot d'entrée et un flot de sortie sur la socket de communication du client. Elle reçoit alors le texte ligne par ligne et utilise des instances de la classe pour compter les mots. Lorsque le mot codé "******" est reçu, le nombre de mots est renvoyé par la socket. Pour permettre au serveur de gérer plusieurs clients à la fois, la classe Service étend la classe Thread et effectue le travail de réception des lignes et des comptes de mots dans la méthode run(). Les techniques de base relatives à la communication réseau sont en grande partie les mêmes que celles de la classe Client visible ci-dessus.

import java.net.*;import java.io.*;import java.util.*;

class Service extends Thread { Socket socket; BufferedReader entree;

79 numéro de port sur lequel le serveur devra attendre les clients.80 on instancie la classe java.lang.Socket à l'aide d'un constructeur prenant en compte le nom de la machine serveur et le port

de cette machine sur lequel sont attendues les connexions. 81 on ouvre un flux d'entrée sur la socket.82 on ouvre un flux de sortie sur la socket. Ce constructeur est "deprecated" en java1.1. sans que cela présente d'inconvénient ici. 83 on envoie au serveur le mot codé "xxxxxx" pour indiquer la fin du texte. 84 ne pas oublier cette instruction.

175/286

Page 176: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 12 - Communiquer à travers un réseau

PrintStream sortie;

Service(Socket socket) { this.socket = socket; try { entree = new BufferedReader(new InputStreamReader (socket.getInputStream())); sortie = new PrintStream(socket.getOutputStream()); } catch (IOException exc){ try { socket.close(); } catch (IOException e) { } } this.start(); }

public void run() { String texte; int compteur = 0; StringTokenizer st;

try { while ( !( texte = entree.readLine() ).equals("xxxxxx")) compteur += ( new StringTokenizer(texte) ).countTokens(); sortie.println("votre texte possede "+compteur+" mots"); } catch (IOException exc){} try { socket.close(); } catch (IOException e) { } }}

public class Serveur extends Thread { final static int port = 10000; ServerSocket receptionniste;

public Serveur() { try { receptionniste = new ServerSocket(port)85; } catch (IOException exc) { System.out.println("impossible de creer le serveur"); } this.start(); }

public void run() { Socket socket; Service c;

try { while (true) { socket = receptionniste.accept()86; c = new Service(socket); } } catch (IOException exc) { System.out.println("probleme de connection"); }

85 la classe ServerSocket sert à recueillir les requêtes de connexion de clients. 86 la méthode accept() de la classe ServerSocket réceptionne la requête de connexion et retourne une socket, instance de la

classe Socket pour gérer la communication avec le nouveau client.

176/286

Page 177: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 12 - Communiquer à travers un réseau

}

public static void main(String argv[]) { new Serveur(); }}

si on lance sur la machine gargantua la commande java Serveur

puis si, sur la machine folcoche, on lance la commande java Client Serveur.java gargantua

alors, sur la machine gargantua, on obtient : votre texte possede 128 mots

4. Séance de TP N°13 : TRAVAILLER AVEC LES RÉSEAUX SOUS UDP – AMÉLIORATION DE LA CALCULETTE [8]

TSIRIS, © Lycée Diderot, 2006

ChampsIntégration et interconnexion de systèmes

(Codage et réalisation)

TâchesT5.2, T5.3 et T5.4 (T3.2, T3.3 et T3.5)

Temps nécessaire 12h

Objectif : Apprendre à mettre en oeuvre une communication réseau via UDP.

4.1. Travail demandé

1. Mettre en oeuvre une communication réseau UDPTester les classes EmetteurUDP et RecepteurUDP sur deux machines distinctes connectées en réseau.

2. Amélioration de la CalculetteOn se propose de reprendre la calculette du TP N°12 pour déporter le calcul sur une machine distante. Ainsi CalculetteSwing est désassociée de CalculetteSauvegarde et se voit associée à deux objets de type EmetteurUDP et RecepteurUDP comme le montre le diagramme de classes suivant :

177/286

Page 178: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 12 - Communiquer à travers un réseau

De la même manière, CalculetteSauvegarde se voit associée à deux objets de type EmetteurUDP et RecepteurUDP pour répondre aux demandes de CalculetteSWING, comme le montre le diagramme de classes suivant :

4.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

5. Séance de TP N°14 : TRAVAILLER AVEC LES RÉSEAUX SOUS TCP – AMÉLIORATION DE LA CALCULETTE [9]

TSIRIS, © Lycée Diderot, 2006

178/286

Figure 47: Diagramme de classes de la Calculette UDP [8] - côté IHM.

Figure 48: Diagramme de classes de la Calculette UDP [8] - côté calculette.

Page 179: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 12 - Communiquer à travers un réseau

Champs Intégration et interconnexion de systèmes (Codage et réalisation)

Tâches T5.2, T5.3 et T5.4 (T3.2, T3.3 et T3.5)

Temps nécessaire 12h

Objectif : Apprendre à mettre en oeuvre une communication client-serveur via TCP.

5.1. Travail demandé

1. Mettre en oeuvre une communication réseau TCPTester les classes Client, Serveur et Service sur deux machines distinctes connectées en réseau.

2. Amélioration de la CalculetteOn se propose de reprendre la calculette du TP N°12 pour déporter le calcul sur une machine distante. On pourra côté serveur suivre le diagramme de classes suivant :

Et côté client, suivre :

179/286

Figure 49: Diagramme de classes de la Calculette TCP [9] - côté Serveur.

Page 180: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 12 - Communiquer à travers un réseau

5.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

180/286

Figure 50: Diagramme de classes de la Calculette TCP [9] - côté Client.

Page 181: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

CHAPITRE 13 - UTILISATION DES THREADS

1. IntroductionUn système multitâches est capable d'exécuter plusieurs programmes en parallèle sur une même machine. Mais attention : dans la quasi totalité des cas il n'y a jamais deux programmes différents qui s'exécutent au même instant. La raison en est simple : la plupart du temps, une machine n'a qu'un seul processeur et ce dernier n'est capable de réaliser qu'une seule chose à la fois. La plupart des systèmes d'exploitation sont équipés d'un ordonnanceur de tâches. Ce composant logiciel a pour mission de donner à tour de rôle le processeur aux programmes en cours d'exécution (on parle alors de processus). La durée pendant laquelle un processus est actif est en fait très courte et c'est grâce à cette activation cyclique (time slicing) et brève que l'utilisateur a l'impression que plusieurs applications sont en cours d'exécution simultanément sur sa machine.

1.1. Les threads

On appelle threads, les sous-tâches (ou processus légers ou encore flux d'exécution) qui peuvent être lancées par un processus. Ci-dessous nous supposons que le processus D exécute en « même temps » les trois threads D1, D2 et D3.

Reprenons l'exemple d'exécution précédent, dans lequel quatre processus s'exécutent « en même temps » et incluons notre processus D et ses trois flux d'exécutions :

181/286

Figure 51: Exemples de quatre processus A, B, C et D en environnement multitâches.

Figure 52: Processus D composé de trois threads D1, D2 et D3.

Page 182: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

La commutation entre les threads d'un processus fonctionne de le même façon que la commutation entre les processus, chaque thread se voit alloué cycliquement, lorsque le processus D est exécuté, une petite tranche de temps processeur. Le partage et la répartition du temps sont effectués uniquement par le système d'exploitation :

1.2. Multithreading et processus

La majorité des systèmes d'exploitation (Ms-Windows, Solaris, Mac OS X, etc.) supportent l'utilisation d'applications contenant des threads, l'on désigne cette fonctionnalité sous le nom de multithreading.La communication entre threads est plus rapide que la communication entre processus et les threads partagent un même espace de mémoire de travail entre eux, au contraire des processus qui ont chacun un espace mémoire personnel.A partir de l'exemple précédent, figurons les processus A, B, C et le processus D avec ses threads dans un graphique représentant une tranche de temps d'exécution allouée par le système, et supposée être la même pour chaque processus.Le système ayant alloué le même temps d'exécution à chaque processus, lorsque par exemple le tour vient au processus D de s'exécuter dans sa tranche de temps, il exécutera une petite sous-tranche pour D1, pour D2, pour D3 et attendra le prochain cycle. Ci-dessous un cycle d'exécution :

182/286

Figure 53: Exemples de quatre processus A, B, C et D dont le dernier est multithreads.

Figure 54: Lorsque D à le processeur, les threads ont tour à tour la main.

Page 183: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

Voici sous les mêmes hypothèses de temps égal d'exécution alloué à chaque processus, le comportement de l'exécution sur 3 cycles consécutifs :

Les langages Ada, Java et C# disposent tous de classes permettant d'écrire et d'utiliser des threads dans nos applications. Nous allons ci-après nous intéresser à la gestion des threads en Java.

2. Les threads en JavaLe langage Java offre la possibilité de manipuler les threads et d'ailleurs, quand nous exécutons un programme Java, nous lançons en fait un processus. Ce processus, de base, contient au moins deux threads : le thread principal qui exécute notre code à partir du main() et un thread secondaire de gestion du ramasse-miettes. Bien entendu, nous pouvons aussi créer nos propres threads au sein de nos applications afin de réaliser plusieurs tâches en « parallèle ».Supposons que les quatre applications de la 1ère partie : A, B, C et D soient toutes des applications Java, et que D soit celle qui comporte trois threads secondaires D1, D2 et D3 exécutés « parallèlement », alors nous aurions le fonctionnement suivant :

183/286

Figure 55: Cycle d'exécution pour les quatre processus A, B, C et D.

Figure 56: Trois cycles d'exécution consécutifs pour les processus A, B, C et D.

Page 184: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

Deux techniques vont nous permettre de créer des threads dans nos applications Java et dans les deux cas, nous pourrons ensuite plus ou moins les contrôler. Nous nous intéresserons ensuite à la synchronisation des threads accédant à des ressources partagées.

3. Création et démarrage d'un threadComme nous venons de le dire plus haut, il existe en Java deux techniques pour créer un thread. Soit nous dérivons notre classe de la classe java.lang.Thread, soit nous implémentons l'interface java.lang.Runnable. Les différences sont subtiles, mais notons déjà que si nous implémentons l'interface Runnable, il nous reste toujours la possibilité de dériver d'une autre classe (n'oublions pas qu'en Java, il n'y a pas d'héritage multiple). De plus, selon la technique que nous choisissons, nous aurons plus ou moins de facilité pour partager ou non des données entre différents threads. Regardons ces deux techniques de plus près.

3.1. Étendre la classe Thread

Voici un premier programme dont l'objectif est de montrer uniquement comment nous définissons et démarrons un thread. Le programme doit lancer trois threads qui écrivent chacun deux fois un même mot. Comme nous gérons la priorité des threads, après avoir écrit la première occurrence de son mot, la priorité du thread devient minimum.

A propos de la priorité Un processus A qui a une priorité supérieure à un celle d'un autre processus B est exécuté avant B. Tous les processus qui ont la priorité la plus élevée peuvent avoir alternativement un peu de temps du processeur, avec un ordre indéterminé. A sa création, un processus a par défaut la même priorité que le processus qui lui a donné naissance. Lorsqu'un processus intervient dans le cadre d'une interface graphique, il faut prendre soin de lui donner une priorité faible de façon à ne pas empêcher le traitement des événements. Voici maintenant le programme de notre exemple.

class Repetiteur extends Thread87 { String chaine;

87 classe du paquetage java.lang que l'on peut étendre pour obtenir une classe dont l'instanciation permettra de créer des processus.

184/286

Figure 57: Exemples de quatre processus Java A, B, C et D en environnement multitâches.

Page 185: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

Repetiteur(String chaine) { this.chaine = chaine; } public void run()88 { for (int i = 0; i < 2; i++) { System.out.println(chaine); setPriority89(Thread.MIN_PRIORITY90); } }}

public class Ecrivain { public static void main(String[] argv) { System.out.println("priorite actuelle : "+ Thread.currentThread()91.getPriority()); System.out.println("priorite minimum : "+ Thread.MIN_PRIORITY); System.out.println("priorite normale : "+ Thread.NORM_PRIORITY); System.out.println("priorite maximum : "+ Thread.MAX_PRIORITY);

// les trois processus qui suivent vont tourner en parallele, // au moins de maniere simulee new Repetiteur("mer").start()92; new Repetiteur("montagne").start(); new Repetiteur("campagne").start(); System.out.println("Derniere instruction!")93; } }

On obtient : priorite actuelle : 5priorite minimum : 1priorite normale : 5priorite maximum : 10Derniere instruction!mer montagne campagne campagnemer montagne

Le premier processus, après avoir écrit mer, devient moins prioritaire et laisse donc le tour au second processus qui écrit montagne qui a son tour doit laisser la place au troisième processus qui écrit campagne. Les deux mots campagne sont écrits l'un après l'autre car les trois processus ont à ce moment la même priorité. Le troisième processus termine donc son travail avant que les deux processus écrivant les mots mer et montagne ne se terminent.

88 cette méthode est définie dans la classe java.lang.thread, où elle ne fait rien ; il faut la redéfinir en y écrivant le code correspondant à ce qu'on attend du thread . La méthode run() doit être appelée par l'intermédiaire de la méthode start().

89 la méthode setPriority() de la classe java.lang.Thread permet de fixer le niveau de priorité d'un processus. 90 priorité minimum pour un thread. 91 la méthode de classe currentThread() retourne une instance de la classe Thread qui est le processus en cours d'exécution.92 la méthode start() est définie dans la classe java.lang.thread. Elle appelle la méthode run() mais, ce qui est très

particulier est qu'elle "retourne" immédiatement, sans attendre que la méthode run() ait terminée son travail. 93 observons que cette instruction est exécutée alors que les trois threads autres que le thread principal n'ont pas encore effectué

d'affichage.

185/286

Page 186: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

3.2. Implémenter l'interface Runnable

Il peut arriver qu'il ne soit pas nécessaire d'utiliser des méthodes spécifiques de la classe Thread, mais juste de faire tourner un processus dont on donne la méthode run(). On peut alors ne pas définir de classe étendant la classe Thread, mais se contenter d'implémenter l'interface java.lang.Runnable. L'avantage est que la classe peut alors étendre une autre classe au choix ; en effet, java ne permettant pas l'héritage multiple, si on définit une classe qui étend la classe Thread, cela interdit d'étendre une autre classe. Le programme ci-dessous fait la même chose que l'exemple donné dans la page précédente, sauf les quelques écritures sur des valeurs de priorité.

class RepetiteurR implements Runnable94 { String chaine;

RepetiteurR(String chaine) { this.chaine = chaine; } public void run() { for ( int i = 0; i < 2; i++) { System.out.println(chaine+" "); Thread.currentThread().setPriority(Thread.MIN_PRIORITY); } }}

public class EcrivainR { public static void main(String[] argv) { new Thread(new RepetiteurR("mer")).start()95; new Thread(new RepetiteurR("montagne")).start(); new Thread(new Repetiteur("campagne")).start(); System.out.println("Derniere instruction!")96; } }

On obtient : Derniere instruction!mer montagne campagne campagne

94 l'interface java.lang.Runnable contient uniquement la déclaration de la méthode public abstract void run() Toute classe qui implémente cette interface pourra définir le corps d'un thread grâce à la méthode run().

95 la classe Thread admet un constructeur qui prend en argument un objet qui implémente l'interface Runnable.Par cette instruction, on construit une instance de la classe RepetiteurR, qui implémente Runnable. On construit un nouveau

Thread ayant pour paramètre notre instance de RepetiteurR : cela implique que la méthode run() du thread construite sera celle de l'instance de RepetiteurR, et s'appliquera aux données de cette même instance. On démarre le thread avec la méthode start() de la classe Thread.

On aurait pu aussi procéder de manière moins condensée en décomposant ainsi : RepetiteurR monRepetiteur; monRepetiteur = new RepetiteurR("mer"); Thread maThread; maThread = new Thread(monRepetiteur); maThread.start();96 observez que cette instruction est exécutée alors que les trois threads autres que le thread principal n'ont pas encore effectué

d'affichage.

186/286

Page 187: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

mer montagne

3.3. Utiliser la méthode join()

On reprend la classe Ecrivain, mais nous souhaiterions que l'instruction : System.out.println("Derniere instruction!");

ne s'effectue qu'après que les trois threads lancés par le programme soit achevés. On utilise pour cela la méthode join() de la classe Thread.

Lorsque l'instruction r1.join(); s'exécute, il y a une mise en attente du processus en cours d'exécution jusqu'à ce que le thread r1 soit terminé.

public class EcrivainJ { public static void main(String[] argv) { Repetiteur r1,r2,r3;

r1 = new Repetiteur("mer"); r2 = new Repetiteur("montagne"); r3 = new Repetiteur("campagne"); r1.start(); r2.start(); r3.start(); try { r1.join(); r2.join(); r3.join(); } catch(InterruptedException exc) {} System.out.println("Derniere instruction"); } }

On obtient : mermontagne campagnecampagne mermontagne Derniere instruction!

4. Gestion des threadsUne fois nos threads créés et lancés, nous pouvons avoir besoin de les contrôler afin d'affiner le comportement de notre application. Il est vrai que nous n'avons pas en Java, un contrôle absolu sur l'exécution de nos threads, mais pas mal de choses restent malgré tout réalisables. Il nous faut, dans un premier temps, mieux comprendre le cycle de vie d'un thread. Par la suite, nous regarderons quelques unes des possibilités de contrôle de plus près.

187/286

Page 188: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

4.1. Cycle de vie d'un thread

Un thread peut passer par différents états, tout au long de son cycle de vie. Le diagramme suivant illustre ces différents stades ainsi que les différentes transitions possibles. Nous reprendrons ensuite en détail chaque point du diagramme, pour mieux comprendre chaque étape de vie d'un thread.Quand nous créons un thread, celui-ci est par défaut dans son état initial. Il faut invoquer la méthode start() pour lui permettre de passer dans un état exécutable. Si nous invoquons, au contraire, la méthode stop(), nous amenons ce thread dans un état terminal, sans qu'il n'ait eu le temps d'exécuter la moindre instruction.Tant que le thread est dans un état exécutable, il est susceptible de pouvoir accéder au processeur. Celui-ci lui sera octroyé durant un bref instant (quelques millisecondes). Au terme de cette durée, le système élira un autre thread exécutable et lui donnera la main. Les systèmes d'exploitations modernes sont qualifiés de préemptifs : c'est-à-dire qu'aucune action n'est à faire par le thread pour que le système lui ôte le processeur. Mais il existe aussi d'autres systèmes qualifiés de non préemptifs : dans ce cas les choses se compliquent un peu. Il est de la charge du thread d'exécuter un appel à la méthode yield() pour pouvoir donner la main à un autre thread. Un thread peut aussi être sorti de la liste des threads en attente du processeur. Dans ce cas il passe dans un état non exécutable. Il lui faudra rejoindre le groupe des threads exécutables pour pouvoir à nouveau accéder au processeur. Pour passer dans l'état non exécutable, deux méthodes peuvent être invoquées : Thread.sleep(long durée), qui suspend le thread durant quelques instants, ou l'une des méthodes wait() de la classe java.lang.Object. Nous reviendrons ultérieurement sur ces dernières.Pour terminer un thread, deux techniques nous sont offertes. Soit nous sortons normalement de la méthode run(). Soit nous invoquons sur notre thread sa méthode stop(). Dans les deux cas, le thread ne pourra jamais redémarrer.

4.2. Démarrage, suspension, reprise et arrêt d'un thread

Pour gérer l'exécution des threads, nous disposons de différentes méthodes. La liste suivante fournit quelques informations sur certaines d'entre-elles.

• public void start() : cette méthode permet de démarrer un thread. En effet, si nous invoquons la méthode run() (au lieu de start()) le code s'exécute bien, mais aucun nouveau thread n'est lancé dans le système. Inversement, la méthode start(), lance un nouveau thread dans le système dont le code à exécuter démarre par l'appel de run().

• public void suspend() : cette méthode permet d'arrêter temporairement un thread en cours d'exécution.

• public void resume() : celle-ci permet de relancer l'exécution d'un thread, au préalable

188/286

Figure 58: Cycle de vie d'un thread.

Page 189: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

mis en pause via suspend(). Attention, le thread ne reprend pas au début du run(), mais continue bien là où il s'était arrêté.

• public void stop() : cette dernière méthode permet de stopper, de manière définitive, l'exécution du thread. Une autre solution pour stopper un thread consiste à simplement sortir de la méthode run().

4.3. Gestion de la priorité d'un thread

Nous pouvons comme nous l'avons vu dans les premiers exemples, jouer sur la priorité de nos threads. Sur une durée déterminée, un thread ayant une priorité plus haute recevra plus fréquemment le processeur qu'un autre thread. Il exécutera donc globalement plus de code.La priorité d'un thread va pouvoir varier entre 0 et 10. Mais attention, il n'est en aucun cas garanti que le système saura gérer autant de niveaux de priorités. Des constantes existent et permettent d'accéder à certains niveaux de priorités : MIN_PRIORITY (0) - NORM_PRIORITY (5) - MAX_PRIORITY (10).L'exemple de code qui suit, lance trois threads supplémentaires. Chacun d'eux se voit affecter une priorité différente. Quel que soit le thread considéré, celui-ci exécute un code très simple dans lequel il incrémente indéfiniment un compteur. Malgré cela, au bout d'un certain temps le thread initial stoppe les trois autres et l'on regarde le nombre d'incrémentations réalisées par chacun d'entre eux.

public class ThreadPriorite extends Thread { private int compteur = 0;

public void run() { while ( true ) counter++; } public int getCompteur() { return this.compteur; }

public static void main(String args[]) throws Exception { ThreadPriorite t1 = new ThreadPriorite(); ThreadPriorite t2 = new ThreadPriorite(); ThreadPriorite t3 = new ThreadPriorite();

t1.setPriority(Thread.MAX_PRIORITY); t2.setPriority(Thread.NORM_PRIORITY); t3.setPriority(Thread.MIN_PRIORITY);

t1.start(); t2.start(); t3.start(); Thread.sleep(5000); t1.stop(); t2.stop(); t3.stop();

System.out.println("Thread 1 : compteur="+t1.getCompteur()); System.out.println("Thread 2 : compteur="+t2.getCompteur()); System.out.println("Thread 3 : compteur="+t3.getCompteur()); }}

Le tableau suivant montre les variations du nombre de constructions, en fonction des priorités affectées.

Thread 1 Thread 2 Thread3Thread.NORM_PRIORITY Thread.NORM_PRIORITY Thread.NORM_PRIORITY

250 752 409 256 697 243 251 964 807

189/286

Page 190: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

Thread.MIN_PRIORITY Thread.NORM_PRIORITY Thread.MAX_PRIORITY11 104 663 20 673 290 1 164 460 398

4.4. Gestion d'un groupe de threads

Une autre possibilité intéressante consiste à regrouper différents threads. Dans un tel cas, nous pouvons invoquer un ordre sur l'ensemble des threads du groupe, ce qui peut dans certains cas sérieusement simplifier le code.Pour inscrire un thread dans un groupe, il faut que le groupe soit initialement créé. Pour ce faire, il faut instancier un objet de la classe ThreadGroup. Un fois ce dernier créé, nous pouvons y rattacher nos threads via un des constructeurs de la classe Thread. Par la suite, un thread ne pourra en aucun cas changer de groupe. Si nous avons mis en oeuvre l'héritage, n'oublionspas que dans ce cas tous les constructeurs parents sont invoqués lors de la création d'un objet.Une fois tous nos threads attachés au groupe, nous pouvons alors invoquer les méthodes de contrôle d'exécution des threads sur l'objet de groupe. Les noms des méthodes sont identiques à celles de la classe Thread : suspend(), resume(), stop(), etc..

4.5. Un exemple: Dessiner un disque

L'objectif principal ici est d'illustrer comment démarrer un thread, le suspendre, le faire reprendre où il en était avant d'avoir été suspendu, ou encore l'arrêter définitivement. Nous aurions pu utiliser les méthodes suspend(), resume() et stop() de la classe Thread, mais comme ces méthodes sont dépréciées depuis la version 1.2 du langage, nous souhaitons aborder un autre moyen de s'en sortir. Le code de notre exemple est un peu long, mais néanmoins l'utilisation des méthodes remplaçantes suspendre(), reprendre() et stopper() ne présente aucune difficulté.

Nous pourrons aussi remarquer l'utilisation de la méthode isAlive() qui permet de savoir si un thread est ou non en cours d'exécution. La méthode retourne la valeur true si l'exécution de la méthode run() est commencée et non terminée ; si le thread a été interrompu par la méthode stop(), isAlive() retourne false, mais elle retourne true si le thread est suspendu.

import java.awt.*;import java.awt.event.*;import java.util.*;

190/286

Figure 59: Exemple d'application de dessin multithreads.

Page 191: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

class ThreadSimple extends Thread { int r = 10; int debut; Component fenetre; boolean doitContinuer; boolean doitFinir;

ThreadSimple(int debut, Component fenetre) { this.debut = debut; this.fenetre = fenetre; }

void suspendre() { doitContinuer = false; } void reprendre() { synchronized(this) { doitContinuer = true; notify(); } }

void stopper() { synchronized(this) { doitFinir = true; notify(); } } public void run() { Graphics g = fenetre.getGraphics(); doitContinuer = true; doitFinir = false; for (int i = 0; i < 30; i++) { try { sleep(200); synchronized(this) { while (!doitContinuer && !doitFinir) wait(); } } catch (InterruptedException exc) {} if (doitFinir) break; g.setColor(new Color((debut+528424*i)%Integer.MAX_VALUE)); g.drawOval(150-r, 150-r,2*r,2*r); r += 2; } }}

public class EssaiThreadSimple extends Frame implements ActionListener{ ThreadSimple thread = null; Random alea97; Button bTracer = new Button("tracer"); Button bPauser = new Button("pauser"); Button bStopper = new Button("stopper"); Button bEffacer = new Button("effacer");

public EssaiThreadSimple() { setLayout(new FlowLayout(FlowLayout.CENTER,10,10)); setSize(300,300); bTracer.addActionListener(this); bPauser.addActionListener(this); bStopper.addActionListener(this); bEffacer.addActionListener(this);

97 la classe java.util.Random permet d'avoir un générateur pseudo-aléatoire d'int, de long, de float, de double.

191/286

Page 192: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

add(bTracer); add(bPauser); add(bStopper); add(bEffacer); alea = new Random((new Date()).getTime())98; setVisible(true); } public void actionPerformed(ActionEvent evt) { Object source = evt.getSource();

if (source == bTracer) { int debut = ( int )Math.abs(alea.nextLong()); if ((thread == null)||(!thread.isAlive()99)) { thread = new ThreadSimple(debut, this); thread.start(); } else thread.reprendre(); }

else if ((source == bPauser) && (thread != null)) thread.suspendre(); else if (source == bStopper) { if (thread != null) thread.stopper(); thread = null; } else if (source == bEffacer) repaint(); } public static void main(String[] argv) { new EssaiThreadSimple(); }}

5. Synchronisation de threads et accès aux ressources partagéesLorsque que nous lançons une JVM, nous lançons un processus. Ce processus possède plusieurs threads et chacun d'entre eux partage le même espace mémoire. En effet, l'espace mémoire est propre au processus et non à un thread. Cette caractéristique est à la fois un atout et une contrainte. En effet, partager des données pour plusieurs threads est relativement simple. Par contre les choses peuvent se compliquer grandement si la ressource partagée doit être modifiée : il faut synchroniser les accès concurrents. Nous sommes certainement face à l'un des problèmes informatiques les plus délicats à résoudre.Pour mieux comprendre les choses, imaginons que deux threads cherchent à modifier, par exemple en l'incrémentant, l'état d'un attribut statique, de type entier, nommé MaClasse.entierPartage. Seul un thread à la fois peut exécuter du code, mais n'oublions pas que les systèmes les plus modernes sont préemptifs ! De plus une instruction telle que :

98 la classe java.util.Date possède beaucoup de méthodes pour gérer les dates et les temps ; par exemple, la méthode getTime() utilisée ici donne le nombre de millisecondes écoulées depuis le 1er janvier 1970, 0 heure, sous forme d'un long. On utilise ce nombre comme graine du générateur aléatoire.

99 la méthode isAlive() retourne la valeur true si l'exécution de la méthode run() est commencée et non terminée ; si le thread a été interrompu par la méthode stop(), isAlive() retourne false, mais elle retourne true si le thread est suspendu.

192/286

Page 193: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

MaClasse. entierPartage = MaClasse. entierPartage + 1;

se traduit en plusieurs instructions en langage machine. Imaginez qu'un premier thread évalue l'expression MaClasse. entierPartage + 1 mais que le système lui enlève le CPU, juste avant l'affectation, au profit d'un second thread. Ce dernier se doit lui aussi d'évaluer la même expression. Le système redonne la main au premier thread qui finalise l'instruction en effectuant l'affectation, puis le second en fait de même. Au final de ce scénario, l'entier n'aura été incrémenté que d'une seule et unique unité. De tels scénarios peuvent amener à des comportements d'applications chaotiques. Il est donc vital d'avoir à notre disposition des mécanismes de synchronisation.

5.1. Notions de verrous

L'environnement Java offre un premier mécanisme de synchronisation : les verrous (locks en anglais). Chaque objet Java possède un verrou et, seul un thread à la fois peut l'utiliser pour verrouiller l'objet. Si d'autres threads cherchent à verrouiller le même objet, ils seront endormis jusqu'à que l'objet soit déverrouillé. Cela permet de mettre en place ce que l'on appelle plus communément une section critique.Pour verrouiller un objet par un thread, il faut utiliser le mot réservé synchronized. En fait, il existe deux façons de définir une section critique. Soit on synchronise un ensemble d'instructions sur un objet, soit on synchronise directement l'exécution d'une méthode pour une classe donnée. Dans le premier cas, on utilise l'instruction synchronized. Dans le second cas, on utilise le qualificateur synchronized sur la méthode considérée. Le tableau suivant indique la syntaxe à utiliser dans les deux cas.

synchronized ( objet ) { // Instructions de // manipulation d'une // ressource partagée.}

public synchronized void operer(...) { // Le code de la méthode synchronizée.}

5.2. Attendre l'accès à une ressource

Mais poser des verrous ne suffit par toujours. Dans certains cas, nos threads doivent patienter, pour ne pas consommer trop de temps CPU, avant qu'une ressource ne soit disponible. Pour gérer ces cas, l'environnement Java propose aussi un support pour pouvoir contrôler leur activité. Ce support est fourni dans la classe Object. Celle-ci propose notamment quatre méthodes permettant d'endormir un thread, ainsi que de le réveiller. Pour endormir un thread, il faut utiliser la méthode wait(). Plusieurs prototypes nous sont fournis afin de pouvoir attendre soit indéfiniment, soit durant un délai maximal. Il est vrai que la méthode Thread.sleep() permet aussi de faire patienter un thread, mais il sera alors impossible de lui faire reprendre son activité avant la fin du timeout. Par contre, la méthode wait() le permet.Pour réveiller des threads endormis, nous pouvons utiliser les méthodes notify() et notifyAll(). Soit nous choisissons de réveiller un unique thread endormi sur un objet sur lequel il faut se synchroniser (notify()), soit nous décidons de tous les réveiller (notifyAll()).

5.3. Le modèle « producteur/consommateur »

Pour mettre en oeuvre un exemple de synchronisation évolué, nous allons considérer le modèle du producteurs/consommateurs. Dans un tel cas, une ressource partagée est utilisée par des threads qui produisent et par des threads qui consomment. Mais attention, il est hors de question de consommer

193/286

Page 194: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

si rien n'a été produit !Pour implémenter notre objet partagé, nous allons coder une pile (stack en anglais). Une pile est une structure de données qui se comporte un peu comme un pile d'assiettes. Pour empiler des assiettes, on les pose par dessus, et on dépile toujours la dernière empilée. mais attention : notre pile va être bornée. On ne peut donc pas empiler indéfiniment. Pour empiler des données supplémentaires, il faudra attendre que des threads consommateurs aient dépilé les anciennes données.Dans ce cas, les choses sont subtiles. L'objet de synchronisation est clairement la pile. Nous allons donc, au gré de l'exécution du programme, endormir des threads sur cet objet. Mais deux types de threads seront à considérer : ceux qui produisent et ceux qui consomment. Imaginons le scénario suivant : un thread empile une donnée. Il va donc utiliser une méthode pour réveiller un éventuel consommateur. Mais qu'est-ce qui garantit que le thread réveillé ne sera pas un autre producteur ? Si c'était le cas, nous aboutirions à des cas d'inter-blocage : le programme n'évoluerait plus, mais ne se terminerait pas. Il nous faudra donc utiliser la méthode notifyAll() pour réveiller les threads. De plus, il est nécessaire de garantir que tous les threads réveillés qui n'ont pas accès à la ressource se rendorment rapidement. D'où le code des méthodes empiler() et depiler() de la classe suivante.

public class Pile { private int tableau []; private int taille, index;

/** Un constructeur de pile */ public Pile() { this (5); }

/** Un autre constructeur de pile qui prend la taille de celle-ci */ public Pile(int taille) { this.taille=taille; this.index=0; this.tableau=new int[taille]; }

/** Renvoie true si la pile est vide */ public boolean estVide() { return index == 0; }

/** Renvoie true si la pile est pleine */ public boolean estPleine() { return index == taille; }

/** Cette méthode synchronisée permet de dépiler une valeur. Elle est utilisée par les threads consommateurs */ public synchronized int depiler () { try { while ( estVide() ) { System.out.println("Consommateur Endormi"); wait(); } } catch(Exception e) { e.printStackTrace(); } int val = tableau[--index]; notifyAll(); return val; }

/** Cette méthode synchronisée permet d'empiler une valeur. Elle est utilisée par les threads producteurs */ public synchronized void empiler(int valeur) { try { while ( estPleine() ) { System.out.println("Producteur Endormi");

194/286

Page 195: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

wait(); } } catch(Exception e) { e.printStackTrace(); } tableau[index++] = valeur; notifyAll(); }}

Maintenant que notre ressource partagée est prête, il ne nous reste plus qu'à coder nos producteurs et nos consommateurs. Dans les deux cas, ces deux types d'objets partagent tous certaines caractéristiques : ils travaillent tous sur la même pile et dans les deux cas, cadencer les choses via un Thread.sleep() pourra permettre une bonne lisibilité des résultats sur la console. Nous utilisons ici l'héritage pour définir le tronc commun à tous nos threads. La classe de base se nomme Fonctionneur et est abstraite : nous ne voulons pas permettre d'instancier un quelconque objet de ce type-là (de plus, nous n'avons pas d'implémentation de la méthode run()).

abstract class Fonctionneur implements Runnable { /** La pile partagée par tous nos threads */ private static Pile p = new Pile(5); /** Un délai d'attente pour chaque thread */ protected long delai;

/** Un constructeur par défaut. Pas de délai d'attente */ public Fonctionneur () { this(0); }

/** Un constructeur qui prend un délai d'attente pour le thread */ public Fonctionneur (long delai) { this.delai = delai; (new Thread(this)).start(); }

/** Permet de pouvoir récupérer la pile */ public Pile getPile() { return p; }}

Nous pouvons maintenant dériver de cette classe nos deux types de threads : les producteurs (classe Producteur) qui vont produire des valeurs entières et donc les empiler (tant que cette dernière n'est pas pleine) et les consommateurs (classe Consommateur) qui vont lire des valeurs entières sur la pile (tant que cette dernière n'est pas vide).

public class Producteur extends Fonctionneur { private int valeur = 0;

public Producteur() { super(); } public Producteur(long delai) { super(delai); }

public void run() { while ( true ) { try { Thread.sleep (( long )Math.random()*this.delai); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println (this + " empile " + valeur); getPile().empiler(valeur++); }

195/286

Page 196: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

}}

public class Consommateur extends Fonctionneur {

public Consommateur() { super(); } public Consommateur(long delai) { super(delai); }

public void run() { while ( true ) { try { Thread.sleep (( long )Math.random()*this.delai); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this + " depile " + getPile().depiler()); } }}

Maintenant il ne reste plus qu'à coder une classe de démarrage afin d'initier le démarrage des threads à synchroniser sur la pile.

public class Essai { public static void main (String args[]) { // Démarrage de deux Producteurs new Producteur(1000); new Producteur(1000); // Démarrage de deux Consommateurs new Consommateur(1000); new Consommateur(1000);

while ( true ) { // Mise en veille du thread principal try { Thread.sleep (10000L); } catch (Exception e) { e.printStackTrace(); } } }}

5.4. Exemple de la « boite aux lettres »

Le problème traité est de type producteur-consommateur. Un ensemble de personnes disposent d'une boîte aux lettres, mais cette boîte présente la particularité de ne pouvoir contenir qu'une seule lettre : dès qu'elle contient une lettre, elle est considérée comme pleine. Des producteurs peuvent y déposer une lettre si la boite n'est pas pleine, et des consommateurs peuvent y retirer une lettre si la boite est pleine. Les lettres ont un auteur mais pas de destinataire particulier, n'importe quel consommateur peut donc retirer une lettre. Une classe BoiteAuxLettres représente la boîte aux lettres. Contrairement à l'exemple de la pile vue dans le paragraphe précédent, cette fois-ci aucun mécanisme n'est prévu sur la boîte aux lettres pour assurer son bon fonctionnement ; les producteurs et les consommateurs doivent se charger de ce bon fonctionnement. C'est ce qui est fait dans le programme ci-dessous.

import java.util.*;

class BoiteAuxLettres { boolean estPleine = false;

196/286

Page 197: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

String lettre;

void deposer(String l) { this.lettre = l; estPleine = true; } String retirer() { estPleine = false; return lettre; }}

class Producteur extends Thread { BoiteAuxLettres boite; String nom; Random alea; Producteur(BoiteAuxLettres boite, String nom, Random alea) { this.boite = boite; this.nom = nom; this.alea = alea; } public void run() { try { for (int i = 0;i < ProdCons.TOTAL;i++) { sleep(Math.abs(alea.nextInt())%1000); synchronized( boite ) { while ( boite.estPleine ) { boite.wait(); } boite.deposer(" " + nom + ", lettre " + (i+1)); System.out.println(nom+" depose : "+nom+ ",lettre"+(i+1)); boite.notify(); } } } catch(InterruptedException exc) { System.out.println("interruption"); } }}

class Consommateur extends Thread { BoiteAuxLettres1 boite; String nom; String lettre; Random alea;

Consommateur(BoiteAuxLettres1 boite, String nom, Random alea) { this.boite = boite; this.nom = nom; this.alea = alea; }

public void run() { try { for (int i = 0; i < ProdCons.TOTAL; i++) { sleep(Math.abs(alea.nextInt())%1000); synchronized( boite ) { while ( !boite.estPleine ) { boite.wait(); } lettre = boite.retirer(); System.out.println(nom+ " lit "+lettre);

197/286

Page 198: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

boite.notify(); } } } catch(InterruptedException exc) { System.out.println("interruption"); } }}

public class Essai { final static int TOTAL = 3;

public static void main(String[] argv) { BoiteAuxLettres boite = new BoiteAuxLettres(); Random alea = new Random(( new Date() ).getTime()); (new Producteur1(boite,"David", alea)).start(); (new Producteur1(boite,"Najina", alea)).start(); (new Consommateur1(boite,"Sophie", alea)).start(); (new Consommateur1(boite,"Marie", alea)).start(); }}

On a obtenu : Najina depose : Najina, lettre 1Sophie lit Najina, lettre 1David depose : David, lettre 1Marie lit David, lettre 1Najina depose : Najina, lettre 2Sophie lit Najina, lettre 2David depose : David, lettre 2Marie lit David, lettre 2David depose : David, lettre 3Sophie lit David, lettre 3Najina depose : Najina, lettre 3Marie lit Najina, lettre 3

6. Utiliser un démonIl s'agit de collecter dans une chaîne de caractères des mots produits par un ensemble de processus producteurs. Les producteurs ne font que répéter un même mot. Une méthode principale démarre l'ensemble des producteurs de mots ainsi qu'un processus responsable de la collecte des différents mots. L'objet principal de cet exemple est de présenter la notion de démon. Il y a deux sortes de threads ;

• les threads utilisateurs : un thread utilisateur se termine lorsque sa méthode run() s'est achevée, ou lorsqu'elle a été arrêtée par sa méthode stop() ;

• les threads démons : quand tous les threads utilisateurs sont terminés, tout thread démon est stoppé.

La méthode setDaemon(boolean) de la classe Thread permet de choisir si un thread est ou non démon, et la méthode getDaemon() permet de savoir si un thread est ou non démon. La méthode main() est par défaut un thread utilisateur, et un thread lancé par un autre thread hérite

198/286

Page 199: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

de la nature, utilisateur ou démon, de son créateur. Voici notre exemple, où les instances de la classe Collecte sont des démons.

class Collecte extends Thread { StringBuffer texte = new StringBuffer(); boolean faire = false; String mot;

Collecte() { setDaemon(true); } public synchronized void run() { try { while ( true ) { while ( !faire ) wait(); texte.append(mot); System.out.println(texte); faire = false; notifyAll(); } } catch(InterruptedException e){} }}

class Pro extends Thread { Collecte collecte; String mot; Random alea;

Pro(Collecte collecte, String mot, Random alea) { this.collecte = collecte; this.mot = mot; this.alea = alea; }

public void run() { try { for (int i = 0; i < 3; i++) { sleep(Math.abs(alea.nextInt())%10); synchronized ( collecte ) { while ( collecte.faire ) collecte.wait(); collecte.mot = this.mot; collecte.faire = true; collecte.notifyAll(); } } } catch(InterruptedException e){} }}

public class Assemble { public static void main(String[] argv) { Collecte collecte = new Collecte(); Random alea = new Random(( new Date() ).getTime()); new Pro(collecte, "soleil ", alea).start(); new Pro(collecte, "lune ", alea).start(); collecte.start(); }}

199/286

Page 200: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

On a obtenu : soleil soleil lune soleil lune soleil soleil lune soleil soleil soleil lune soleil soleil lune soleil lune soleil soleil lune lune

7. Séance de TP N°15 : UTILISER LES THREADS – AMÉLIORATION DE LA CALCULETTE [10]

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 12h

Objectif : Apprendre à manipuler les threads en Java.

200/286

Page 201: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 13 - Utilisation des threads

7.1. Travail demandé

On se propose de reprendre la calculette du TP précédent. Dans celle-ci, un client distant peut demander la résolution d'une opération mathématique au serveur. Nous souhaitons rajouter la gestion de plusieurs clients à la fois en modifiant la classe Service de manière à ce que celle-ci hérite de la classe java.lang.Thread. Il sera aussi nécessaire de modifier la classe ServeurTCP comme l'indique le diagramme de classes suivant :

7.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

201/286

Figure 60: Diagramme de classes de la Calculette multithreads.

Page 202: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

CHAPITRE 14 - INTERFACER JAVA ET LE MONDE NATIF

Java possède l'avantage d'être portable sur toute machine disposant d'une machine virtuelle, mais l'inconvénient qui en découle est son impossibilité à accéder à des ressources spécifiques telles que des ports d'entrée/sortie non conventionnels (voir l'API Java Communication pour l'accès aux ports séries et parallèles). L'objectif de ce chapitre est donc d'apprendre à faire communiquer Java et le monde natif. Dans un premier temps, nous verrons simplement comment exécuter des applications natives depuis une application Java, puis nous terminerons par une description de l'interface JNI (Java Native Interface) qui permet de faire communiquer les langages Java et C ou C++.

1. Exécuter des applications nativesUne application Java peut avoir, de temps en temps, besoin de lancer des programmes exécutables sur la machine locale. Par exemple, imaginez que vous souhaitiez afficher tous les fichiers .java de votre répertoire courant (et la classe File me direz-vous ? Humm). Vous vous dîtes, pourquoi ne pas lancer sous Ms-Windows la commande dir *.java ou encore son équivalent sous Unix ls *.java et puis afficher le résultat de celle-ci sur la console ?

L'exemple qui suit vous permet de le faire à l'aide des classes java.lang.Runtime et java.lang.Process :

import java.io.*;

/** Montre comment exécuter un programme externe. **/public class RunTimeApp {

public static void main (String arg[]) { try { // Il est possible de connaître l'OS sur lequel on travaille. String os = System.getProperty ("os.name"); Process processus; if (os.equals("Linux") { processus = Runtime.getRuntime().exec ("ls *.java"); } InputStreamReader reader = new InputStreamReader( processus.getInputStream () ); BufferedReader buf_reader = new BufferedReader(reader); String line; while ( (line = buf_reader.readLine ()) != null) System.out.println (line);

} catch (IOException e) { System.out.println (e); } } // main} // class RunTimeApp

Il est tout d'abord nécessaire de récupérer la référence du Runtime courant. Toute application Java

202/286

Page 203: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

en cours possède une instance de la classe Runtime qui réalise l'interface entre l'application et l'environnement dans lequel elle s'exécute. L'accès à cet « environnement d'exécution en cours » se fait à l'aide de la méthode statique getRuntime() :

Runtime rt = Runtime.getRuntime () ;

Il faut ensuite utiliser la méthode exec() de l'objet Runtime afin de créer le processus natif de la commande passée en paramètre et retourne un objet de type Process qui permet d'interagir avec le programme externe :

Process processus = rt.exec ("ls *.java") ;

Process propose des accès en entrée, sortie et erreur sur le processus associé, et permet donc la communication entre ce dernier et le programme Java. La suite du programme utilise les flots d'entrée/sortie que nous avons déjà rencontrés, afin de récupérer sur la console la sortie du programme externe.

Voici un autre exemple qui permet de réaliser la commande ls -l | wc -l :Process ls = Runtime.getRuntime().exec("ls -l");Process wc = Runtime.getRuntime().exec("wc -l");

InputStream lsOutput = ls.getInputStream();OutputStream wcInput = wc.getOutputStream();

// On 'copie' la sortie de ls dans l'entrée de wc :int c;while ( (c = lsOutput.read()) != -1 ) { wcInput.write(c);}

lsOutput.close();wcInput.close();

Les versions de Java supérieures à 5.0, ont vu apparaître la classe java.lang.ProcessBuilder qui ajoute des fonctionnalités à la classe Runtime. ProcessBuilder simplifie l'exécution de processus et permet de les exécuter en modifiant leur environnement. Voici le premier exemple dans sa nouvelle version :

import java.io.*;

/** Montre comment exécuter un programme externe avec ProcessBuilder. **/public class ProcessBuilderApp {

public static void main (String arg[]) { try { // Création d'un ProcessBuilder qui exécutera la commande ls ProcessBuilder pb = new ProcessBuilder("ls *.java"); // Cette commande s'effectuera dans le répertoire : pb.directory("/usr/share/java/src/"); // Lancement du processus Process processus = pb.start(); InputStreamReader reader = new InputStreamReader( processus.getInputStream () ); BufferedReader buf_reader = new BufferedReader(reader); String line; while ( (line = buf_reader.readLine ()) != null) System.out.println (line);

203/286

Page 204: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

} catch (IOException e) { System.out.println (e); } } // main} // class RunTimeApp

2. Interfacer Java et C/C++

2.1. Introduction à JNI

Fourni en standard avec chaque JDK, l'interface JNI propose un mécanisme permettant de définir certaines méthodes non portables, dans un langage tel que le langage C. Les informations qui suivent ont été réalisées pour accéder à du code natif depuis Java 5.0 sur un système Linux Ubuntu x86 muni d'un compilateur gcc . La procédure se révèle assez proche sur un système Ms-Windows avec un autre compilateur.Le schéma suivant, tiré du « HowTo JNI » de David Dagon, indique les différentes étapes du processus.

/=============================\ /===================\ | 1. HelloWorld.java | | 2. Java Byte Code | |-----------------------------| |-------------------| | public native sayHello(); | javac | OxCAFEBABE etc. | | *--------> | | | public void speakUp() | \=========*=========/ | --calls sayHello(); | | | | | javah -jni \=============================/ | V /===============================\ /=================================\ | 3. C/C++ Header File: Hello.h | | 4. JNI Method: Hello.c | |-------------------------------| |---------------------------------| | #include <jni.h> | | /* file: Hello.c */ | | . . . | | #include "Hello.h" <---------------* JNIEXPORT void JNICALL | | #include <stdio.h> | | Java_HelloWorld_sayHello | | JNIExport void JNICALL | | (JNIEnv *, jobject, jstring); | | Java_HelloWorld_sayHello | | | | (JNIEnv *env, jobject thisObj, | | etc. | | jstring strMessage) { | \===============================/ | /* etc. etc. */ | | } | \=============*===================/ | | Compilation: | | gcc -I$JDK_HOME/include -I$JDK_HOME/include/linux \ | -shared -o libHello.so Hello.c V/===================================\ /===============================\| 5. Shared Object: libHello.so | | 6. Java Source Calls Library ||-----------------------------------| |-------------------------------|| 0000000 457f 464c 0101 0001 . . . | | public class Test { || 0000020 0003 0003 0001 0000 . . . | | static { || 0000040 064c 0000 0000 0000 . . . | | System.loadLibrary || . . . *--------> ("Hello"); |\===================================/ | } |

204/286

Page 205: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

| public static void main | | (String argv[]) { | | HelloWorld hw = new |/=================\ javac | HelloWorld(); || 7. Run java | <------------------* hw.sayHello("Howdy"); ||-----------------| | . . . || java Test | \===============================/| || NB: Need to set || LD_LIBRARY_PATH | /======================================\| environment <--------| %LD_LIBRARY_PATH=./$LD_LIBRARY_PATH || variable first! | \======================================/| |\=================/

2.2. Caractéristiques de JNI

2.2.1. Les en-têtes jni.h et jni_md.h

Les fichiers <jni.h> et <jni_md.h> , placés dans les répertoires $JAVA_HOME/include/ et $JAVA_HOME/include/[win32 ou linux]/ définissent l'ensemble des options, des types et des structures utilisés par JNI. <jni_md.h> est spécifique à la plate-forme et appelé par <jni.h>.

2.2.2. Les types de base et leurs équivalents natifs

Type Java Type natif Taille (en bits)

boolean jboolean 8 unsigned

byte jbyte 8

char jchar 16 unsigned

double jdouble 64

int jint 32

float jfloat 32

long jlong 64

short jshort 16

void void -

205/286

Page 206: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

2.2.3. Les objets et leurs équivalents natifs

206/286

Page 207: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

2.2.4. Déclaration des fonctions

package test;

class Prompt {

private native String getLigne(String prompt);

}

JNIEXPORT jstring JNICALL Java_test_Prompt_getLigne(JNIEnv *, jobject, jstring);

Préfixe + NomPaquetage + "_" + NomClasse + "_" + NomMéthode

• JNIEXPORT : option C définie dans <jni_md.h> et correspondant à __declspec(dllexport) ;

• JNICALL : option C définie dans <jni_md.h> et correspondant à __stdcall ;• JNIEnv : structure de données définie dans <jni.h> et renseignant sur l'environnement de

la machine virtuelle d'appel. Elle permet une communication entre la fonction C et le programme Java ;

• jobject : structure définie dans <jni.h> et qui référence l'objet ayant appelé la fonction native.

2.2.5. Accéder à des chaînes de caractères

Le type jstring doit être utilisé avec précaution. Le premier emploi ci-dessous est incorrect :/* Mauvais emploi de jstring */ JNIEXPORT jstring JNICALL Java_Prompt_getLigne(JNIEnv *env, jobject obj, jstring prompt) { printf("%s", prompt); // ...}

207/286

Figure 61: Classes Java et leurs équivalents JNI.

Page 208: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

Il faut effectivement créer un pointeur constant en passant par la fonction GetStringUTFChars()./* Emploi correct de jstring */ JNIEXPORT jstring JNICALL Java_Prompt_getLigne(JNIEnv *env, jobject obj, jstring prompt) { const char *chaine = ( * env )->GetStringUTFChars(env, prompt, 0); printf("%s", chaine); ...

/* libération de mémoire après utilisation */ ( * env )->ReleaseStringUTFChars(env, prompt, chaine); // ...}

2.2.6. Accéder à des tableaux

De la même façon, l'accès à un tableau doit être réalisé avec précaution./* Mauvais utilisation d'un tableau d'entiers */ JNIEXPORT jint JNICALL Java_TabEnt_sommer(JNIEnv *env, jobject obj, jintArray tab) { int i, somme = 0; for (i = 0; i < 10; i++) { somme += tab[i]; } // ... }

La somme des éléments du tableau doit être faite de la manière suivante :/* Utilisation correcte du tableau */ JNIEXPORT jint JNICALL Java_TabEnt_sommer(JNIEnv *env, jobject obj, jintArray tab) { int i, somme = 0;

/* 1. Obtenir la taille du tableau */ jsize taille = ( * env )->GetArrayLength(env, tab);

/* 2. Obtenir un pointeur sur les éléments du tableau */ jint *elements = ( * env )->GetIntArrayElements(env, tab, 0);

/* 3. opérer sur les éléments du tableau (types base ou jobjects */ for (i = 0; i < taille; i++) { somme += elements[i]; }

/* 4. Libérer la mémoire allouée */ ( * env )->ReleaseIntArrayElements(env, tab, elements, 0); }

2.2.7. Accéder à des attributs d'objets

Pour accéder aux attributs il est nécessaire de récupérer leur signature. Celle-ci peut être obtenue à l'aide de la commande :

javap -s -p NomDeLaClasse

208/286

Page 209: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

Cette signature est utilisée lors de l'appel des méthodes GetFieldID() et GetStaticFieldID() (suivant le type static ou non de l'attribut), qui permettent de récupérer l'identifiant des attributs.L'accès aux attributs proprement dit, se fait pour la lecture par l'intermédiaire des fonctions Get<Type>Field() ou GetStatic<Type>Field(), et pour l'écriture par celui des fonctions Set<Type>Field() et SetStatic<Type>Field().

Exemple : Les champs sont de types entier statique et chaîne de caractères. class AccesChamps { static int si; /* signature "I" */ String s; /* signature "Ljava/lang/String;" */} /* lancer javap -s -p AccesChamps pour obtenir une signature */

/* 1. Récupère l’identifiant du champ */ fid = ( * env )->GetStaticFieldID(env, cls, "si", "I"); /* 2. Trouve l’attribut correspondant */ si = ( * env )->GetStaticIntField(env, cls, fid); /* 3. Opère dessus */ ( * env )->SetStaticIntField(env, cls, fid, 200); /* 1. Récupère l’identifiant du champ */ fid = ( * env )->GetFieldID(env, cls, "s", "Ljava/lang/String;"); /* 2. Trouve l’attribut correspondant */ jstr = ( * env )->GetObjectField(env, obj, fid); /* 3. Opère dessus */ jstr = ( * env )->NewStringUTF(env, "123"); ( * env )->SetObjectField(env, obj, fid, jstr);

2.2.8. Appeler une méthode

Il faut tout d'abord trouver la classe de l’objet dont on souhaite appeler la méthode. Cela se fait par l'intermédiaire de la fonction : GetObjectClass(). Chaque méthode possède un identifiant que l'on obtient par un appel à : GetMethodID(). Enfin, pour appeler la méthode cela dépend de son type de retour : JNI propose une API pour chaque type : CallVoidMethod(), etc..

Exemple :jclass cls=( *env )->GetObjectClass(env, obj);jmethodID mid=( *env )->GetMethodID(env,cls,"afficherMessage","(I)V");( *env )->CallVoidMethod(env, obj, mid, parm1);

2.3. Exemple : la classe PortES

La classe Java suivante doit nous permettre d'accéder à un port d'entrée/sortie et d'y écrire ou d'y lire, cela par l'intermédiaire de méthodes dites natives. En effet, Java ne permet pas directement ce type d'opérations dépendantes d'une machine et de son système d'exploitation. Pourtant, il peut être intéressant pour une application Java d'accéder à ces entrées/sorties pour, par exemple, accéder à une carte d'instrumentation.

public class PortES{ public PortES() {}

public native int initialiser(int unPort);

209/286

Page 210: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

public native void ecrire(int uneValeur); public native int lire(); public native int fermer(); static { /* charge une bibliothèque libPortES.so sous Unix et * PortES.dll sous Windows */ System.loadLibrary("PortES"); }}

Le code précédent est compilé avec le JDK 1.5.0 sur GNU/Linux par la commande : javac PortES.java.

2.3.1. Génération du fichier PortES.h

Le fichier d'en-tête est généré à l'aide du programme javah fourni avec le JDK. javah analyse le bytecode du fichier Java compilé afin de construire un fichier .h. Toutes les déclarations de fonctions sont de la forme :

JNIEXPORT typeDeRetour JNICALL Java_NomPackge_NomClasse_nomFonction (JNIEnv *, jobject, ...);

Le code de PortES.h a été généré par la commande : javah -jni PortES

et ne doit en aucun cas être modifié./* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class PortES*/

#ifndef _Included_PortES#define _Included_PortES#ifdef __cplusplusextern "C" {#endif/* * Class: PortES * Method: initialiser * Signature: (I)V */JNIEXPORT jint JNICALL Java_PortES_initialiser(JNIEnv *,jobject,jint);

/* * Class: PortES * Method: ecrire * Signature: (I)V */JNIEXPORT void JNICALL Java_PortES_ecrire(JNIEnv *,jobject,jint);

/* * Class: PortES * Method: lire * Signature: ()I */JNIEXPORT jint JNICALL Java_PortES_lire(JNIEnv *,jobject);

210/286

Page 211: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

/* * Class: PortES * Method: initialiser * Signature: (I)V */JNIEXPORT jint JNICALL Java_PortES_fermer(JNIEnv *,jobject);

#ifdef __cplusplus}#endif#endif

2.3.2. Création du code source natif PortES.c

Nous pouvons alors construire manuellement le code source natif en C. Nous l'appellerons par exemple PortES.c.

#include "PortES.h"#include <sys/io.h> /* pour ioperm() fait appel à asm/io.h pour inb() et outb() */

int port;

JNIEXPORT jint JNICALL Java_PortES_initialiser (JNIEnv *env, jobject obj, jint unPort){ port = ( int )unPort; return ( jint )ioperm(port, 1, 1); /* fourni les droits d'accès à l'utilisation du port */}

JNIEXPORT void JNICALL Java_PortES_ecrire (JNIEnv *env, jobject obj, jint uneValeur){ outb(( int )uneValeur, port);}

JNIEXPORT jint JNICALL Java_PortES_lire (JNIEnv *env, jobject obj){ jint valeur = ( jint )inb(port); return valeur;}

JNIEXPORT jint JNICALL Java_PortES_fermer (JNIEnv *env, jobject obj){ return ( jint )ioperm(port, 1, 0); /* ôte les droits d'accès à l'utilisation du port */}

2.3.3. Compilation du code natif

La compilation s'effectue à l'aide de la commande suivante :gcc -I/usr/lib/jdk1.5.0/include -I/usr/lib/jdk1.5.0/include/linux -O -shared -o libPortES.so PortES.c

211/286

Page 212: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

La compilation doit produire une bibliothèque partagée (*.so ou *.dll), d'où l'utilisation de l'option -shared. Nous spécifions le répertoire des fichiers d'en-têtes /usr/lib/jdk1.5.0/include en supposant que le JDK a été installé dans le répertoire /usr/lib/jdk1.5.0/. Le point important est que l'un des répertoires contient le fichier d'en-tête jni.h indépendant de la machine, et que l'autre (sous-répertoire linux pour les JDK 1.2 et supérieurs sous GNU/Linux, sous-répertoire genunix pour le JDK 1.1 sous GNU/Linux ou encore win32 pour le JDK 1.2 sous Ms-Windows) contient le fichier d'en-tête <jni_md.h> dépendant de la machine.

2.3.4. Installation et test

Vous devez posséder à présent les fichiers suivants sur votre système : • PortES.c

• PortES.class

• PortES.java

• PortES.o

• PortES.h

• libPortES.so

Précisez alors au système le chemin d'accès à la bibliothèque partagée : export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

Puis créez une classe Java de test permettant d'instancier la classe PortES et d'utiliser les méthodes natives.

2.3.5. Automatiser une partie de la procédure

Voici un exemple de fichier Makefile :## Fichier Makefile pour Linux/JDK 1.2#

## Modifier les macros suivantes en fonction de votre système#JDK = /usr/local/jdk1.2.2CFLAGS = -g -O -shared -I$(JDK)/include -I$(JDK)/include/linuxCFLAGS2 = -I$(JDK)/include -I$(JDK)/include/linuxCC = gcc

all: make port @LD_LIBRARY_PATH=.:$$LD_LIBRARY_PATH; export LD_LIBRARY_PATH; \ port: PortES.class libPortES.so

clean: rm -f *.class *.so *.o

cleanall: rm -f *.class *.so *.o *~ .*~

212/286

Page 213: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

PortES.class: PortES.java javac PortES.java javah -jni PortES

libPortES.so: PortES.c PortES.h ($CC) ($CFLAGS) -o libPortES.so PortES.c

# ou encore# libPortES.so: PortES.c PortES.h# ($CC) ($CFLAGS2) -c PortES.c -o PortES.o# ld -G PortES.o -o libPo

3. Séance de TP N°16 : LANCER UNE APPLICATION NATIVE – AMÉLIORATION DE LA CALCULETTE [11]

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 4h

Objectif : Apprendre à exécuter une application native depuis un programme Java.

213/286

Page 214: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 14 - Interfacer Java et le monde natif

3.1. Travail demandé

On se propose de reprendre la calculette du TP précédent et de rajouter dans l'interface graphique cliente une possibilité de lancer un éditeur de texte à l'aide de la classe Executeur.

3.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne ;• Voici un extrait de la documentation produite pour la classe que vous devez réaliser :

public class Executeur extends java.lang.ObjectPermet de lancer et stopper une application native. Elle utilise : IOException : Indique une erreur d'entrée/sortie quelconque. Process : Représente un processus (programme distinct de la machine virtuelle Java) et permet de le contrôler. Runtime : Permet à une application de dialoguer avec son contexte d'exécution. String : Représente une chaîne de caractères dont le contenu ne peut être modifié. SecurityException : Déclenchée par le gestionnaire de sécurité pour indiquer qu'une violation de sécurité a eu lieu. System : Contient plusieurs attributs et méthodes utiles au dialogue avec le système d'exploitation.

Version: 1.0 Author: Alain Lebret

Field Summary

private java.lang.String

commande Chaîne de commande

private java.lang.Process

p Sous-processus à exécuter

private java.lang.Runtime

r Espace d'environnement dans lequel l'instance d'Executeur existe

Constructor Summary

Executeur() Constructeur par défaut

Method Summary

void lancer(java.lang.String cmd) Permet l'exécution du sous-processus de nom cmd.

void stopper() Permet l'arrêt du sous-processus

214/286

Page 215: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

CHAPITRE 15 - ALGORITHMIQUE EN JAVA

1. Structures de données Voir les transparents de cours associés. L'API Java propose un grand nombre de structures de données définies dans le paquetage java.util, telles que des listes chaînées (LinkedList), des piles (Stack), des files d'attente (PriorityQueue), des arbres (TreeSet, TreeMap), et bien plus encore.

2. Les méthodes de tri et de recherche

2.1. Introduction

Rechercher un élément dans un ensemble, ou trier les éléments de cet ensemble suivant un critère précisé, sont deux actions régulièrement effectuées dans la vie courante.Par exemple, la bibliothèque d'un CDI répertorie un certain nombre d'ouvrages. Ceux-ci peuvent être triés par auteur, thème, titre, genre, etc., et la méthode de tri doit être la plus adaptée (critères de tri). Chargée de répondre à la demande d'un étudiant, la bibliothécaire effectue la recherche d'un ouvrage. Cette recherche doit être optimisée (vitesse de recherche).Nous nous contenterons dans ce document de ne présenter que des méthodes qui opèrent sur des tableaux d'entiers. A charge pour l'étudiant d'aller plus loin.

2.2. Les méthodes de tri

Voici trois méthodes de tri classiques :

2.2.1. Le tri à bulle

a) Principe

Soit à trier suivant l'ordre croissant le tableau d'entiers suivant : { 1, 5, 4, 2, 3, 6, 7, 8, 9 }

La méthode consiste à comparer deux par deux les éléments du tableau dans l'ordre croissant de celui-ci. La première valeur est comparée avec la seconde, si elle est supérieure on les échange, sinon on la compare avec la troisième, etc.. Puis on prend la deuxième que l'on compare avec la troisième puis, etc..

b) Algorithme

{ TRI A BULLES }CONST NOMBRE_ELEMENTS 10 VAR tableau[NOMBRE_ELEMENTS] DE ENTIER;

215/286

Page 216: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

PROC trierBulles()VAR i, j, temp : ENTIER;DEBUT POUR i ← NOMBRE_ELEMENTS A 1 POUR j ← 1 A i SI (tableau[j-1] > tableau[j]) temp ← tableau[j-1]; tableau[j-1] ← tableau[j]; tableau[j] ← temp; FINSI FINP FINPFIN

c) Nombre d'échanges maximal

Pour un ensemble de N éléments, il y aura 1,5⋅N 2−N permutations au maximum. Par

exemple un tableau de 1000 éléments pourra demander jusqu'à 1.498.500 permutations. Autant dire que vous n'utiliserez jamais le tri à bulle à moins d'avoir du temps à perdre.

2.2.2. Le tri par sélection

a) Principe

L'algorithme de tri par sélection commence par rechercher le plus petit élément de l'ensemble et le permute avec le premier. Puis il cherche le deuxième plus petit élément de l'ensemble qu'il permute avec l'élément occupant la deuxième position, et ainsi de suite. Soit par exemple à trier le tableau suivant :

{ 1, 5, 4, 2, 3, 6, 7, 8, 9 }Etape 1 :

{ 1, 2, 4, 5, 3, 6, 7, 8, 9 }Etape 2 :

{ 1, 2, 3, 5, 4, 6, 7, 8, 9 }Etape 3 :

{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }

b) Algorithme

{ TRI PAR SELECTION }CONST NOMBRE_ELEMENTS 10 VAR tableau[NOMBRE_ELEMENTS] DE ENTIER;

PROC trierSelection()VAR i, j, minimum, temp : ENTIER;DEBUT POUR i ← 1 A NOMBRE_ELEMENTS minimum ← i; POUR j ← i A NOMBRE_ELEMENTS SI (tableau[j] < tableau[minimum]) minimum ← j; FINSI FINP temp ← tableau[minimum]; tableau[minimum] ← tableau[i];

216/286

Page 217: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

tableau[i] ← temp; FINPFIN

c) Nombre d'échanges maximal

Pour un ensemble de N éléments, il y aura N 2/4 3⋅ N−1 permutations au maximum. Par

exemple un tableau de 1000 éléments pourra demander jusqu'à 252.997 permutations.

2.2.3. Le tri par insertion

a) Principe

Le principe du tri par insertion est d'insérer à la n-ième itération le n-ième élément à la bonne place. L'algorithme considère au départ que le tableau est trié puis à chaque itération compare successivement un élément avec ses précédents et décale ses intermédiaires si c'est nécessaire. Soit par exemple à trier le tableau suivant :

{ 1, 5, 4, 2, 3, 6, 7, 8, 9 }étape 1 :

{ 1, 5, 4, 2, 3, 6, 7, 8, 9 } // 5 est traité. Supérieur à 1 il reste en place.

étape 2 : { 1, 4, 5, 2, 3, 6, 7, 8, 9 } // 4 est traité. Il est inséré avant son plus grand précédent (5).

étape 3 : { 1, 2, 4, 5, 3, 6, 7, 8, 9 } // 3 est traité. Il est inséré avant son plus grand précédent (4)

étapes supérieures :{ 1, 2, 3, 4, 5, 6, 7, 8, 9 } // Poursuite sans modification jusqu'à la fin.

b) Algorithme

{ TRI PAR INSERTION }CONST NOMBRE_ELEMENTS 10 VAR tableau[NOMBRE_ELEMENTS] DE ENTIER;

PROC trierInsertion()VAR i, j, element : ENTIER;DEBUT POUR i ← 1 A (NOMBRE_ELEMENTS - 1) element ← tableau[i]; j ← (i - 1); TANT QUE (j >= 0 ET tableau[j] > element) tableau[j + 1] ← tableau[j]; j ← (j - 1); FINTQ tableau[j + 1] ← element; FINPFIN

c) Nombre d'échanges maximal :

Pour un ensemble de N éléments, il y aura N⋅N−1 / 4 permutations au maximum. Par exemple un tableau de 1000 éléments pourra demander jusqu'à 249.750 permutations.

217/286

Page 218: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

2.2.4. Le tri rapide

En langage Java, la classe java.util.Arrays entre autres, fournit la méthode sort() qui implémente l'algorithme du tri rapide (quicksort en anglais). Pour un ensemble de N éléments, il y aura N /6⋅log N permutations au maximum. Par exemple un tableau de 1000 éléments ne demandera au maximum que 500 permutations.

2.3. Les méthodes de recherche

Nous nous contenterons de découvrir deux algorithmes simples et laisserons de côté les algorithmes faisant intervenir les tables de hachage. Le lecteur curieux pourra étudier les ouvrages cités en fin de document.

2.3.1. La recherche linéaire

a) Principe

C'est LA méthode que l'on emploie généralement lors de la recherche d'un élément dans un ensemble. On parcours l'ensemble, élément par élément, jusqu'à découverte de celui qui est recherché. Cette méthode n'est évidemment pas optimale mais elle fonctionne aussi si l'ensemble n'est pas trié.

b) Algorithme

{ RECHERCHE LINEAIRE }CONST NOMBRE_ELEMENTS 10 VAR tableau[NOMBRE_ELEMENTS] DE ENTIER;

FONCT ENTIER rechercherLineairement(element : ENTIER)VAR elementActuel : ENTIER;DEBUT elementActuel ← NOMBRE_ELEMENTS; TANTQUE ((element <> tableau[elementActuel]) ET (elementActuel >= 0)) elementActuel ← elementActuel - 1; FINTQ RET elementActuel;FIN

2.3.2. La recherche dichotomique (ou binaire)

a) Principe

« Diviser pour mieux trouver » serait le mot d'ordre de la recherche dichotomique. La méthode consiste à découper l'ensemble E en des morceaux de plus en plus petits. Pour cela, on débute en recherchant l'élément « milieu » de l'ensemble. Cela nous permet de découper E en E1 et E2, puis on regarde si l'élément recherché est plutôt dans E1 ou E2. On recherche alors l'élément « milieu » du sous-ensemble concerné et on le découpe. L'opération est réitérée jusqu'au succès.

b) Algorithme

{ RECHERCHE DICHOTOMIQUE }

218/286

Page 219: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

CONST NOMBRE_ELEMENTS 10 VAR tableau[NOMBRE_ELEMENTS] DE ENTIER;

FONCT ENTIER rechercherDichotomiquement(element : ENTIER)VAR hMilieu : ENTIER; gauche : ENTIER; droite : ENTIER;

DEBUT hMilieu ← 0; gauche ← 0; droite ← (NOMBRE_ELEMENTS - 1); TANTQUE (droite >= gauche) { Recherche du milieu du tableau } hMilieu ← (( gauche + droite ) / 2); SI (element < tableau[hMilieu]) droite ← (hMilieu - 1); SINON gauche ← (hMilieu + 1); FINSI { Si l'élément est trouvé, hMilieu } { est renvoyé en guise de position } SI (element = tableau[hMilieu]) RET hMilieu; FINTQ { Si aucun élément n'est trouvé, la } { valeur renvoyée est -1 } RET -1;FIN

3. CryptographieVoir les transparents de cours associés. L'API Java développe des algorithmes de cryptographie tels que DES, Triple DES, RC2, RSA, accessibles avec les classes des paquetages java.security et javax.crypto. Le site http://javaalmanac.com/ propose un certain nombre d'exemples.

4. CompressionVoir les transparents de cours associés. L'API Java propose de base les algorithmes de compression ZIP et GZIP définis dans le paquetage java.util.zip. Le site http://javaalmanac.com/ propose un certain nombre d'exemples.

5. Séance de TP N°17 : LISTES, FILES ET PILES EN JAVA

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

219/286

Page 220: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 7h

Objectif : Apprendre à manipuler Listes, Files et Piles en langage Java.

5.1. Travail demandé

5.1.1. Préparation du paquetage structures

1. Créer l'arborescence suivante :./tp_structures/./tp_structures/javadoc/./tp_structures/src/./tp_structures/src/structures/Le répertoire ./tp_structures/src/structures/ servira au stockage du source des classes du paquetage de structures de données et son répertoire parent au stockage du source des classes de test.

2. Créer les classes Noeud, Liste, FileAttente et Pile du paquetage structures, telles qu'elles sont décrites dans le cours. Attention, les noeuds devront pouvoir stocker des objets. Pensez à documenter les sources correctement. Valider les classes par des tests significatifs.

5.1.2. Mise en oeuvre de la classe Pile

Modifier la classe TraitementTexte ci-dessous, pour lui ajouter la fonctionnalité « Annulation de commande ». La dernière action ("Couper", "Copier", "Coller" ou "Effacer") pourra être ainsi annulée de manière irréversible lors d'un appui sur le bouton "Annuler". L'application permettra au maximum l'annulation des cinq dernières commandes.

5.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne ;• Squelette des Codes sources Java :

/* * Liste.java * * Lycée Diderot * Département Informatique Industrielle * 61, rue David d'Angers * 75019 Paris * * TP - Session 2006/07 * Copyright (c) 2006 TSIRIS. */package structures;

/** * La classe Liste met en oeuvre une liste chaînée * simple. * * @version 1.0 * @author Karpof */

220/286

Page 221: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

public class Liste { /** * Un attribut qui ... */ private String monAttribut = null; //... //... } // Fin Liste

• Source de la classe TraitementTexte :import java.awt.*;import java.util.Date;import java.awt.event.*;import javax.swing.*;

/** * Mini traitement de texte inspiré par celui d'Emannuel Puybaret, * auteur du livre "Les cahiers du programmeur Java", éditions * Eyrolles. Pour son utilisation dans une page HTML, insérer le code * suivant : &lg;APPLET CODE=TraitementTexte WIDTH=300 HEIGHT=200 * CODEBASE="./" ALT="Ca marche pas" NAME="TraitementTexte" * ALIGN=MIDDLE&gt; &lg;/APPLET&gt;. */public class TraitementTexte extends JFrame implements ActionListener { /** * */ private static final long serialVersionUID = 1L; private JTextArea texte = new JTextArea(); private JButton bCouper = new JButton("Couper"); private JButton bCopier = new JButton("Copier"); private JButton bColler = new JButton("Coller"); private JButton bEffacer = new JButton("Effacer"); private JButton bAnnuler = new JButton("Annuler");

private String texteCopie = "";

public TraitementTexte() { super("Traitement de texte"); // Choix d'un layout BorderLayout (FlowLayout par défaut) setLayout(new BorderLayout()); // Création d'une barre de boutons avec les commandes // Couper/Copier/Coller/Effacer/Annuler (layout // FlowLayout par défaut) JPanel panel = new JPanel(); panel.add(bCouper); bCouper.addActionListener(this); panel.add(bCopier); bCopier.addActionListener(this); panel.add(bColler); bColler.addActionListener(this); panel.add(bEffacer); bEffacer.addActionListener(this); panel.add(bAnnuler); bAnnuler.addActionListener(this); // Ajout en haut de la barre de boutons add("North", panel); // Ajout au centre de la zone de saisie add("Center", texte);

221/286

Page 222: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

// Ajout en bas d'un label add("South", new Label("Lycée Diderot - TS IRIS, " + new Date().getTime())); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent evt) { System.exit(0); } }); this.setSize(400, 200); this.setVisible(true); }

/** Méthode appelée par la machine virtuelle quand on clique * sur un bouton. */ public void actionPerformed(ActionEvent event) { if (event.getSource() == bCouper) { // Simulation d'une action Copier/Effacer texteCopie = texte.getSelectedText(); texte.replaceRange("", texte.getSelectionStart(), texte.getSelectionEnd()); } if (event.getSource() == bCopier) texteCopie = texte.getSelectedText(); if (event.getSource() == bColler) texte.replaceRange(texteCopie, texte.getSelectionStart(), texte.getSelectionEnd()); if (event.getSource() == bEffacer) texte.replaceRange("", texte.getSelectionStart(), texte.getSelectionEnd()); if (event.getSource() == bAnnuler) { // // // } return; }

public static void main(String[] args) { new TraitementTexte(); }}

6. Séance de TP N°18 : TRI ET RECHERCHE EN JAVA

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

222/286

Page 223: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

Temps nécessaire 2x7h

Objectif : Apprendre à trier et rechercher des données en langage Java.

6.1. Travail demandé

6.1.1. Tri d'un tableau d'entiers

Réaliser à partir du diagramme de classes suivant, l'interface Trieur ainsi que les classes qui l'implémente TrieurABulle, TrieurParInsertion, TrieurParSelection et TrieurRapide. Ces dernières mettent en oeuvre les différents algorithmes de tri d'un tableau d'entiers. Un attributs permutations permettra de stocker le nombre de permutations nécessaires au tri et sera affiché à la fin des appels à trier(). Valider les classes avec un tri par ordre croissant du tableau d'entiers suivant : { 1, 5, 4, 2, 3, 9, 7, 8, 6 }

6.1.2. Recherche dans un tableau d'entiers

Réaliser à partir du diagramme de classes suivant, l'interface Chercheur ainsi que les classes qui l'implémente ChercheurLineaire et ChercheurDichotomique. Ces dernières mettent en oeuvre les algorithmes de recherche linéaire et dichotomique d'un entier dans un tableau d'entiers. Un attributs iterations permettra de stocker le nombre d'itérations nécessaires à la recherche et sera affiché à la fin des appels à rechercher(). Valider les classes avec l'exemple précédemment trié en recherchant le nombre 7.

223/286

Figure 62: Diagramme des classes de tri de données.

Page 224: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

6.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

7. Séance de TP N°19 : CRYPTOGRAPHIE EN LANGAGE JAVA

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 2x7h

Objectif : Mettre en oeuvre les algorithmes de cryptage classiques tels que le chiffre de César et celui de Vigenère. Facultatif : Casser un chiffre.

7.1. Travail demandé

Remarque : les textes à crypter seront écrits en français, ne comprendront que des lettres non accentuées minuscules et majuscules ({a..z}, {A..Z}) et les ponctuations seront conservées en l'état. Le site de l'ABU peut être consulté pour la récupération de textes électroniques.

7.1.1. Préparation du paquetage crypto

1. Créer l'arborescence suivante :./tp_crypto/./tp_crypto/javadoc/./tp_crypto/src/./tp_crypto/src/crypto/Le répertoire ./tp_crypto/src/crypto/ servira au stockage du source des classes du paquetage de cryptographie et son répertoire parent au source des classes de test.

224/286

Figure 63: Diagramme des classes de recherche dans un tableau d'entiers.

Page 225: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

2. Créer une classe abstraite ChiffreurDechiffreur du paquetage crypto. Celle-ci possédera outres des attributs et des méthodes adéquates, les deux méthodes abstraites chiffrer() et dechiffrer(). Justifier les choix pris.

7.1.2. Implémentation dans le paquetage crypto

1. Chiffrement de César • Créer la classe ChiffreurDechiffreurCesar héritant de ChiffreurDechiffreur

et implémentant les méthodes chiffrer() et déchiffrer().• La valider.• Produire la documentation.

2. Chiffrement de Vigenère • Créer la classe ChiffreurDechiffreurVigenere héritant de

ChiffreurDechiffreur et implémentant les méthodes chiffrer() et déchiffrer().

• La valider.• Produire la documentation.

3. Une IHM de cryptage • Réaliser une IHM de test pour faciliter l'utilisation des classes du paquetage. Les

chaînes de caractères à chiffrer seront placées dans des zones de texte (TextArea).

7.1.3. Cryptanalyse : Casser les chiffres

1. Classe abstraite Casseur du paquetage crypto • Créer une classe abstraite Casseur comportant outre les attributs et méthodes

adéquates, la méthode abstraite publique String casser(String cryptogramme, int type).

2. Classe CasseurCesar • Réaliser la classe CasseurCesar chargée de déchiffrer un texte préalablement crypté

avec le chiffre de César. Suivant la valeur du paramètre type, la méthode casser() emploiera par le biais de méthodes privées :

1) l'algorithme de parcours de tous les décalages possibles (type=1) ; 2) l'algorithme d'analyse des fréquences (type=2). Pour ce dernier il faudra construire la méthode double calculerFrequence(String texte, char lettre) retournant la fréquence de lettre dans texte. Les fréquences de chaque caractère seront triées par ordre décroissant pour le cryptogramme. La distance entre le caractère de plus haute fréquence dans le texte crypté et le 'E' dans l'alphabet donne la valeur de la clef.

• Ajouter une IHM. Le rajout de l'affichage des distributions de lettres en rapport avec le calcul de fréquences sera particulièrement apprécié.

• Valider la classe à partir d'un cryptogramme fourni par un groupe concurrent. • Conclure.

7.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne ;• Squelette des Codes sources Java :/*

225/286

Page 226: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

* ChiffreurDechiffreur.java * * Lycée Diderot * Département Informatique Industrielle * 61, rue David d'Angers * 75019 Paris * * TP - Session 2005/06 * Copyright (c) 2005 TSIRIS2. */package crypto; /** * Classe abstraite fournissant le cadre aux * classes de chiffrement déchiffrement classiques. * * @version 1.0 * @author Bob Sinclar */public abstract class ChiffreurDechiffreur { /** * Un attribut qui ... */ private String monAttribut = null; ... ... } // Fin ChiffreurDechiffreur

8. Séance de TP N°20 : COMPRESSION DE DONNÉES EN LANGAGE JAVA

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 2x7h

Objectif : Mettre en oeuvre les algorithmes de compression classiques tels que les compressions RLE et LZW.

8.1. Travail demandé

8.1.1. Préparation du paquetage compression

1. Créer l'arborescence suivante :./tp_compression/./tp_compression/javadoc/./tp_compression/src/

226/286

Page 227: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 15 - Algorithmique en Java

./tp_compression/src/compression/Le répertoire tp_compression/src/compression/ servira au stockage du source des classes du paquetage de compression et son répertoire parent au source des classes de test.

2. Créer une interface CompresseurDecompresseur dans le paquetage compression (cf. ci-dessous). Quelles différences quant au choix d'une interface par rapport à celui de la classe abstraite du TP de cryptage ? Voyez-vous un avantage ?

8.1.2. Implémentation dans le paquetage compression

1. Compression RLE (Semaine 1)• Créer la classe CompresseurDecompresseurRLE implémentant l'interface

CompresseurDecompresseur. Les accès fichiers se feront à l'aide des classes FileInputStream et FileOutputStream du paquetage java.io. (voir outils_rle.zip). Attention l'implémentation de l'algorithme est ici un peu plus complexe que dans le cours : Il faut en effet prendre en compte le fait que l'octet de contrôle, pour nous SEP=255, peut apparaître dans le fichier à compresser et doit, dans ce cas, être traité à part.

• La valider.• Produire la documentation.

2. Compression LZW (Semaine 3 : après le TP sur les Listes/Piles/Files/...) • Créer la classe CompresseurDecompresseurLZW implémentant l'interface

CompresseurDecompresseur. Vous utiliserez pour stocker les codes du dictionnaire la classe Dictionnaire et pour manipuler les chaînes la classe TableauOctets.

• La valider.• Produire la documentation.

3. Une IHM de compression • Réaliser une IHM test pour faciliter l'utilisation des classes du paquetage. L'IHM

devra permettre de choisir l'option de compression (ou décompression) ainsi que les fichiers à compresser (ou à décompresser).

• Les fichiers compressés porteront l'extension ".rle" (ou ".lzw").• Un affichage du gain de compression obtenu sera réalisé sur l'IHM. Faîtes l'essai RLE

puis LZW avec les fichiers : germinal.txt, lena.ppm, lenac.ppm et lenag.ppm. Comparer les gains en compression obtenus par les deux méthodes en fonction du type de fichier (texte ou image). Puis comparer les valeurs obtenues avec celles de l'algorithme de compression GZIP implémenté dans la classe CompresseurDecompresseurGZIP.

8.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne ;• les fichiers d'utilitaires outils_rle.zip et outils_lzw.zip contenant les classes et les images.

227/286

Page 228: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 16 - La généricité

CHAPITRE 16 - LA GÉNÉRICITÉ

La généricité est apparue dans la version 5.0 du langage Java. Elle fournit un mécanisme équivalent aux templates du langage C++, ou encore aux modules génériques du langage Ada. Les exemples de ce chapitre sont issus du tutoriel Java de Sun Microsystems, Inc..

1. IntroductionDans tout développement d'application, les bogues sont légions. La rigueur lors des phases de modélisation, de planification et de développement proprement dits, peut aider à les réduire mais pas à les éliminer. La généricité ajoute au code une possibilité de détecter plus facilement à la compilation un certain nombre d'erreurs.

2. Une classe BoitePrenons l'exemple d'une classe Boite, non générique, qui opère sur des objets de type quelconque. Elle ne comporte que deux méthodes, ajouter() qui permet d'ajouter un objet dans la boite, et retirer() qui permet de l'extraire.

public class Boite { private Object objet; public void ajouter(Object objet) { this.objet = objet; } public Object retirer() { return objet; }}

La classe permet de manipuler tout objet Java. Supposons que nous souhaitions restreindre le type contenu dans la boite à, par exemple, des objet de type Integer uniquement. La solution consiste à placer dans notre documentation, une information sur cette restriction. Cette information n'est malgré tout pas accessible au compilateur.

public class EssaiBoite1 { public static void main(String[] args) { // ATTENTION : Ne mettez que des Integer dans cette boite !!!! Boite boiteEntier = new Boite(); boiteEntier.ajouter(new Integer(5)); Integer unEntier = ( Integer )boiteEntier.retirer(); System.out.println(unEntier); }}

Le programme précédent créé un objet Integer et le passe à la méthode ajouter(), puis le récupère au travers de la méthode retirer() en le plaçant dans unEntier. Tout se passe bien ! Mais qu'en est-il si un programmeur inconscient souhaite utiliser une chaîne de caractères comme dans l'exemple suivant :

228/286

Page 229: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 16 - La généricité

public class EssaiBoite2 { public static void main(String[] args) { // ATTENTION : Ne mettez que des Integer dans cette boite !!!! Boite boiteEntier = new Boite(); // Imaginons que ce soit une petite partie d'un très long // programme modifiée par un programmeur : boiteEntier.ajouter("10"); // Oups

// ... Et ce qui suit l'est par un autre programmeur Integer unEntier = ( Integer )boiteEntier.retirer(); System.out.println(unEntier); }}

Cette fois-ci, nous avons stocker la chaîne de caractères 10 mais nous allons récupérer un Integer. Tout se passe bien à la compilation puisqu'une chaîne de caractères est bien un objet, mais à l'exécution une exception est levée et le programme se « plante » :

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at EssaiBoite2.main(BoiteDemo.java:7)

L'utilisation de la généricité aurait permis la détection du bogue, dès la phase de compilation.

3. Types génériquesNous créons la déclaration d'un type générique en changeant le code public class Boite en public class Boite<T>. De cette façon, nous introduisons une variable type appelée T qui pourra être utilisée partout dans la classe. Cette technique est aussi applicable aux interfaces.

/** * Version de la classe Boite en utilisant la généricité. */public class Boite<T> { private T t; // T signifie "Type" public void ajouter(T t) { this.t = t; } public T retirer() { return t; }}

Dans le code, toutes les occurrences d'Object ont été remplacées par T. Pour référencer cette classe générique depuis notre code, nous devrons réaliser une invocation du type générique, en remplaçant T par une valeur concrète telle que par exemple Integer.

Boite<Integer> boiteEntier;

Pour instancier cette classe, nous utilisons aussi le mot réservé new mais en rajoutant <Integer> entre le nom de la classe et les parenthèses :

229/286

Page 230: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 16 - La généricité

boiteEntier = new Boite<Integer>();

ou encore sur une seule ligne :

Boite<Integer> boiteEntier = new Boite<Integer>();

Une fois l'objet créé, il est possible d'appeler sa méthode retirer() sans qu'il soit nécessaire de la transtyper, ainsi que nous le montrons dans l'exemple suivant.

public class EssaiBoite3 { public static void main(String[] args) { Boite<Integer> boiteEntier = new Boite<Integer>(); boiteEntier.ajouter(new Integer(10)); Integer unEntier = boiteEntier.retire(); // pas de cast! System.out.println(unEntier); }}

De plus, si nous essayons d'ajouter un type incompatible dans la boite, comme un String, la phase de compilation nous indiquera l'erreur suivante.

EssaiBoite3.java:5: ajouter(java.lang.Integer) in Boite<java.lang.Integer> cannot be applied to (java.lang.String) boiteEntier.ajouter("10"); ^ 1 error

Remarque : il est important de comprendre que ces variables de type ne sont en aucun cas des types. Ainsi, il est inutile de chercher après de quelconques fichiers T.java ou T.class, car ces derniers n'existent pas. De plus, T n'est pas une partie du nom de la classe Boite, et d'ailleurs, lors de la phase de compilation, les informations génériques seront retirées du code.

4. Conventions pour nommer les paramètres de typePar convention, le nom des paramètres de types sont des caractères majuscules uniques, de manière à les identifier rapidement dans le code. Les noms les plus courants sont :

• E – élément (utilisé dans les classes Java Collections (Collection, List, Set, Tree, Map, etc.) ;

• K – Clé ; • N – Nombre ; • T – Type ; • V – Valeur ; • S, U, V, etc. - 2ème, 3ème, 4ème types.

5. Méthodes et constructeurs génériquesLes paramètres de type peuvent aussi être déclarés à l'intérieur des signatures des méthodes ou des

230/286

Page 231: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 16 - La généricité

constructeurs, cela afin de créer des méthodes et des constructeurs génériques. Le principe est le même que lors de la déclaration d'un type générique, à la différence que la portée des paramètres est cette fois-ci, limitée aux corps des méthodes et des constructeurs.

/** * Cette version introduit une méthode générique. */public class Boite<T> { private T t; public void ajouter(T t) { this.t = t; } public T retirer() { return t; }

public <U> void inspecter(U u){ System.out.println("T: " + t.getClass().getName()); System.out.println("U: " + u.getClass().getName()); }

public static void main(String[] args) { Boite<Integer> boiteEntier = new Boite<Integer>(); boiteEntier.ajouter(new Integer(10)); boiteEntier.inspecter("Un peu de texte par exemple"); }}

Ici, nous avons ajouter une méthode générique appelée inspecter(), qui définie un paramètre de type appelé U. Cette méthode accepte un objet et affiche son type sur la sortie standard. Elle affiche aussi le type de T.

Le résultat de ce programme est :

T: java.lang.IntegerU: java.lang.String

Un utilisation plus réaliste des méthodes génériques, pourrait être la suivante :public static <U> void remplirBoites(U u, List<Boite<U>> boites) { for (Boite<U> boite : boites) { boite.ajouter(u); }}

Pour utiliser la méthode précédente, nous pourrions écrire quelque chose du genre :Crayon rouge = ...;List<Boite<Crayon>> boitesACrayon = ...;Boite.<Crayon>remplirBoites(rouge, boitesACrayon);// Ou encore// Boite.remplirBoites(rouge, boitesACrayon);

6. Paramètres génériquesIl arrive que l'on souhaite restreindre les types autorisés à être passés à des paramètres de type. Par exemple, une méthode qui opère sur les nombre pourrait n'accepter que des instances de Number ou d'une de ses classes filles. Pour déclarer un paramètre de type restreint (en anglais, on utilise le terme bounded type

231/286

Page 232: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 16 - La généricité

parameter), nous plaçons le nom du paramètre, suivi du mot réservé extends ainsi que de la classe qui le restreint.

/** * Cette version introduit un paramètre de type restreint. */public class Boite<T> { private T t;

public void ajouter(T t) { this.t = t; } public T retirer() { return t; }

public <U extends Number> void inspecter(U u){ System.out.println("T: " + t.getClass().getName()); System.out.println("U: " + u.getClass().getName()); }

public static void main(String[] args) { Boite<Integer> boiteEntier = new Boite<Integer>(); boiteEntier.ajouter(new Integer(10)); boiteEntier.inspecter("Un peu de texte"); // ERREUR ! }}

En modifiant notre méthode générique pour yinclure un paramètre de type restreint, nous amenons la phase de compilation à échouer puisque notre appel à inspecter utilise toujours une chaîne de caractères.

Boite.java:21: <U>inspecter(U) in Boite<java.lang.Integer> cannot be applied to (java.lang.String) boiteEntier.inspecter("10"); ^1 error

Remarque : Il est possible de restreindre à certaines classes, mais aussi d'ajouter des interfaces dans la restriction. Cela est réalisé à l'aide du caractère &. Par exemple :

<U extends Number & MonInterface>

Nous n'avons dans ce chapitre fait qu'effleurer le mécanisme de généricité. Nous engageons le lecteur à consulter les ouvrages en bibliographie pour compléter ces premières notions, ainsi qu'à les approfondir en manipulant les Collections Java.

232/286

Page 233: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 17 - Quatre TP supplémentaires en Java

CHAPITRE 17 - QUATRE TP SUPPLÉMENTAIRES EN JAVA

1. Séance de TP N°21 : RESSOURCES PARTAGÉES AVEC LES THREADS

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 2x7h

Objectif : Mettre en oeuvre des ressources partagées entre threads.

1.1. Travail demandé

1. Test des classes de manipulation de threads1. Créer l'arborescence suivante :

./tp_threads/

./tp_thread/javadoc/

./tp_thread/src/2. Tester les classes fournies dans le fichier outilsThreads.zip dans l'ordre des paquetages

suivants : simple, ressourcecritique, semaphore, tampon et producteurconsommateur. Expliquer succinctement le principe général d'accès à une ressource commune par plusieurs threads concurrents. Faire ensuite une synthèse comparative des différentes méthodes utilisées (par exemple sous forme de tableau).

3. Dans le paquetage producteurconsommateur, modifier les sources de manière à ce que la classe Tampon puisse stoker jusqu'à 5 objets de type Object. Le producteur et le consommateur devront pouvoir écrire et lire un objet et un seul à la fois dans le tampon. Valider la classe avec des chaînes de caractères, puis produire la documentation.

2. Mettre en oeuvre des ressources partagées

Réaliser un oscilloscope. Une fenêtre de simulation permet d'afficher un signal sinusoïdal. Ce dernier est produit par un thread GenerateurSignal et lu par un thread Afficheur au travers d'une ressource partagée MemoirePartagee. Proposer un diagramme de classes pour la structure de l'application, puis créer les classes GenerateurSignal, Afficheur, MemoirePartagee, ainsi que la fenêtre d'affichage pour obtenir un fonctionnement équivalent.

233/286

Page 234: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 17 - Quatre TP supplémentaires en Java

1.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

2. Séance de TP N°22 : COMMUNIQUER VIA LA LIAISON SÉRIE RS-232 EN JAVA

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 7h

Objectif : Mettre en oeuvre une communication utilisant la liaison série RS-232 en Java.

2.1. Travail demandé

1. SimpleWrite du paquetage javacomm• Sur la première machine :

Démarrer le programme minicom comme précédemment et configurer le port série avec les paramètres suivants : 4800 bauds, 7 bits, parité paire, 1 bit de stop, pas de contrôle de flux.

• Sur la seconde machine :Modifier en conséquence, compiler puis exécuter SimpleWrite.java. Entrer alors un texte dans la console de SimpleWrite et vérifier sa réception sur minicom.Tester la classe SimpleRead du paquetage javacomm.

• Sur la première machine :• Démarrer le programme minicom comme précédemment et configurer le port série avec les

paramètres suivants : 1200 bauds, 8 bits, pas de parité, 1 bit de stop, pas de contrôle de flux.

Sur la seconde machine :Modifier en conséquence, compiler puis exécuter SimpleRead.java. Entrer alors rapidement un texte long dans la console de minicom, et vérifier sa bonne réception.

2. Gestion de flux1. Programme de communication 2. En s'inspirant des classes vues précédemment, réaliser le diagramme de classes UML d'un

programme de communication permettant d'envoyer et de recevoir des données sur une liaison série simultanément (i.e. en half-duplex puisque nous n'utilisons qu'un port série et des threads). Ce programme sera muni d'une IHM graphique permettant la configuration, l'envoi ou la réception de messages à l'aide de zones de textes.

• Réaliser et valider le programme. 3. Produire la documentation.4. Protocole Xon / Xoff

• Ajouter une gestion du protocole Xon/Xoff.

234/286

Page 235: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 17 - Quatre TP supplémentaires en Java

2.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne ;• Le paquetage javacomm (Sun MicroSystems) et sa documentation ;• 2 ordinateurs et un câble série NULL-MODEM.

3. Séance de TP N°23 : LE PARCOURS DU CAVALIER AUX ÉCHECS

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 14h

Objectif : Réaliser une application Swing permettant de mettre en oeuvre le parcours du Cavalier sur un échiquier.

3.1. Travail demandé

Le mouvement étrange du cavalier, au jeu d’échecs, fait que ses déplacements sont particulièrement fascinants. Une question qui a fait couler beaucoup d’encre est celle de savoir si un cavalier peut arriver à parcourir toutes les cases de l’échiquier en ne passant qu'une seule fois sur chacune d'elles ? La réponse est oui.Parmi les nombreux mathématiciens qui ont cherché à résoudre ce problème, on peut citer De Moivre, Euler, Vandermonde, Warnsdorff , Roget. L'étudiant intéressé consultera l'ouvrage de W. Rouse Ball : Mathematical Recreations And Essays. Nous allons mettre en oeuvre l’ingénieuse règle donnée par Warnsdorff en 1823 qui permet quasiment toujours de trouver une solution.

A chaque coup, il faut jouer le cavalier sur la case d'où il y a le moins de sorties vers les cases non encore

occupées.

Cette règle qui est une règle de bon sens, n'a jamais été prouvée, mais elle est applicable à tout échiquier et reste valable même si on devait changer le mouvement du cavalier. De plus, elle offre l'avantage suivant qui peut surprendre au premier abord : une personne qui chercherait à la main le parcours du cavalier arriverait probablement à bon port même si elle commettait des erreurs. Lorsque le cavalier se trouve sur une case donnée, il existe au plus 8 cases Y1, Y2, ...Y8 auxquelles il a accès. On compte le nombre Nl de cases de fuite possible à partir de Y1, N2 à partir de Y2, N3 à partir de Y3, ..., N8 à partir de Y8. La règle de Warnsdorff nous dit qu'il faut choisir comme case suivante, celle qui offre le nombre Ni minimum, puis on recommence le processus. Il est évident que Ni doit être différent de zéro, sinon cela signifierait que le cavalier est

235/286

Page 236: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 17 - Quatre TP supplémentaires en Java

dans un cul-de-sac. De plus, comme plusieurs de ces nombres Ni peuvent être égaux, on peut adopter la tactique de choisir le premier ou bien de choisir au hasard entre ceux-ci ; ce qui fournirait plusieurs solutions à partir d'une même case de départ.

Considérons l’exemple suivant où le cavalier est placé sur la case (4, 4)100.

Y1

Y1

Y2 3

Y1

Y2

Y5

Y2

Y8

Y3 2 2

1 1 1Y

7Y

4Y

4Y

3

Y6

Y5

Figure 1 Figure 2 Figure 3

Figure 1 1) On tourne autour de cette case dans le sens des aiguilles d'une montre et on constate qu'il existe 8 cases de fuite Yl, Y2...Y8.2) On tourne autour des cases Yi (i=1..8) et pour chacune de ces dernières on compte le nombre Ni de cases de fuite. On trouve:

Nl = 7 N2 = 7 N3 = 5 N4 = 5 N5 = 5 N6 = 5 N7 = 7 N8 = 73) On cherche le plus petit Ni (i=1..8). Puisque plusieurs de ces nombres sont égaux nous optons pour le premier de la liste. C'est donc la case Y3 qui est choisie et le cavalier ira sur la case (6, 3). Puis on recommence le processus.

Figure 21) On tourne autour de la case (6, 3) et on constate qu'il y a 5 cases de fuite Y1, Y2, ...Y5.2) On tourne autour des cases Yi (i=1..5) et pour chacune on compte le nombre Ni de cases de fuite. On trouve: Nl = 5 N2 = 2 N3 = 3 N4 = 7 N5 = 73) Le Ni (i=1..5) minimum est 2 qui correspond à la case Y2. On déplace donc le cavalier sur la case (7, 1) et on recommence le processus.

Figure 31) On tourne autour de la case (7, 1) et on constate qu'il n'y a que deux cases de fuite possible Yl et Y2.2) On tourne autour de ces cases de fuite Yi (i=1..2) et pour chacune on compte le nombre Ni

de cases de fuite. On trouve: Nl = 3 N2 = 63) Le Ni (i=1..2) minimum est 3 qui correspond à la case Y1. On déplace donc le cavalier sur la case (5, 0).

On continue le processus jusqu'à ce que le parcours soit terminé, soit que le cavalier achève le tour, soit qu'il échoue par manque de case de fuite !

Proposer un modèle objet pour résoudre le problème du cavalier décrit ci-dessus, puis le coder.Remarque :

100la case en haut à gauche est la case (0, 0).

236/286

Page 237: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 17 - Quatre TP supplémentaires en Java

Soit un échiquier d’ordre N inférieur ou égal à 8. Les cases seront numérotées de 0 à N-1 horizontalement et verticalement. Afin de nous faciliter la tâche dans la recherche des cases de fuite nous allons utiliser un échiquier qui sera élargi de deux cases supplémentaires dans les deux sens. L'ordre maximal de l'échiquier supporté par le programme étant 8 nous pouvons donc déclarer un tableau d’entiers : int echiquier[12][12];Voici par exemple, l'échiquier utilisé si N=5. De cette façon, on ne risque pas de provoquer des erreurs fatales lorsque l'on cherche des cases de fuite en dehors de l'échiquier. En effet, ces cases supplémentaires (en grisé) sont initialisées à 1 tandis que les cases blanches (l'échiquier proprement dit) sont remplies de zéros. Une case sera libre si et seulement si elle contient un zéro. Lorsqu'on pose le cavalier sur une case libre, celle-ci voit le zéro qu'elle contenait remplacé par le numéro du déplacement.

1 1 1 1 1 1 1 1 11 1 1 1 1 1 1 1 11 1 0 0 0 0 0 1 11 1 0 0 0 0 0 1 11 1 0 0 0 0 0 1 11 1 0 0 0 0 0 1 11 1 0 0 0 0 0 1 11 1 1 1 1 1 1 1 11 1 1 1 1 1 1 1 1

3.2. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

4. Séance de TP N°23 : LE JEU DU MORPION EN RÉSEAU

TSIRIS, © Lycée Diderot, 2006

Champs Codage et réalisation

Tâches T3.2, T3.3 et T3.5

Temps nécessaire 14h

Objectif : Réaliser un jeu du morpion en mode client-serveur.

4.1. Travail demandé

L'application à réaliser est constituée d'un programme ServeurMorpion qui permet à deux ClientMorpion (applets ou applications) de se connecter à ce serveur et de jouer au morpion sur un plateau de 9 cases comme le montre la figure suivante :

237/286

Page 238: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 17 - Quatre TP supplémentaires en Java

Ce TP met en oeuvre les connaissances acquises sur les threads ainsi que sur la communication réseau en mode connecté client/serveur.

4.1.1. Fonctionnement de l'application

a) Côté Serveur :

Le ServeurMorpion a les caractéristiques suivantes :• plateau : Représentation locale du plateau de jeu.• sortie : Zone de texte pour l'affichage des messages d'informations.• joueurs : Tableau des deux threads Joueurs.• serveur : ServerSocket pour accepter les connexions clients.• joueurCourant : Index du joueur courant.

et permet les opérations suivantes :• ServeurMorpion() : Initialise les caractéristiques.• void executer() : Attente de deux connexions pour que le jeu puisse commencer. Lors de

la réception de la demande de connexion de chaque client, une instance de la classe Joueur est créée pour gérer le client dans un thread d'exécution distinct. Cela permet au clients de jouer de manière indépendante.

• void afficher(String s) : Affichage des messages d'information destinés à l'administrateur du serveur dans la zone de texte.

• synchronized boolean mouvementValable(int empl, int joueur) : Déterminer si un mouvement « empl » tenté par « joueur » est valable. La méthode mouvementValable() est synchronisée, ce qui permet une seule tentative de mouvement à la fois. Cela interdit aux deux joueurs de modifier simultanément l'état du jeu. Si le Joueur qui tente de faire valider un mouvement n'est pas le joueur courant, c'est-à-dire celui qui est autorisé à faire le mouvement, il est placé dans un état d'attente (wait()) jusqu'à ce qu'il reçoive son tour. Si la position du mouvement à valider est déjà occupée sur le plateau, false est retournée. Sinon, le serveur place une marque du joueur dans sa représentation locale du plateau du jeu, notifie à l'autre objet Joueur que son adversaire a fait mouvement (message au client), invoque la méthode notify pour que le Joueur en attente, s'il existe, puisse valider un mouvement, et retourne enfin true pour signaler que le mouvement est valable.

• boolean estOccupe(int empl) : Utilisée par mouvementValable() pour savoir si un emplacement « empl » est déjà utilisé. Retourne true si c'est le cas.

238/286

Figure 64: Capture des fenêtres clients et serveur du Morpion

Page 239: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 17 - Quatre TP supplémentaires en Java

• boolean partieFinie() : Gestion de la fin de partie.

Le Joueur a les caractéristiques suivantes :• connexion : Socket pour la connexion.• entree : Flux d'entrée DataInputStream. • sortie : Flux de sortie DataOutputStream.• controle : Référence du ServeurMorpion parent.• nombre : Le nombre associé au Joueur. 0 ou 1.• marque : La marque associée au Joueur (X ou O).• threadSuspendu : Indicateur pour l'état du thread. Vaut true si le joueur est suspendu

(en attente ou endormi).

et permet les opérations suivantes :• Joueur(Socket s, ServeurMorpion t, int num) : A la création du Joueur, son

constructeur récupère l'objet Socket représentant la connexion au client ainsi que les flux d'entrée et de sortie associés. Le premier client connecté se voit automatiquement affecter les croix « X » et le second client les ronds « O ».

• void autreJoueurAJoue(int empl) : méthode d'information du joueur non courant pour le coup joué.

• void run() : La méthode run() de Joueur contrôle les informations envoyées au client et celles reçues de ce dernier. Tout d'abord, elle informe le client de ce que la connexion est bel et bien active, puis lui passe ensuite le caractère qu'il devra placer sur le plateau du jeu lors d'un de ses mouvements. Un verrouillage du joueur (synchronized(this)) est effectué en début d'exécution, car aucun des joueurs n'est autorisé à effectuer un mouvement lors de sa première connexion : Le joueur « X » ne peut jouer que lorsque le joueur « O » se connecte, et le joueur « O » ne peut jouer qu'après un mouvement du joueur « X ». Ensuite, chaque itération de boucle de la méthode run() lit un entier en provenance du client. Celui-ci représente l'emplacement où le client souhaite placer sa marque. La méthode mouvementValable() du ServeurMorpion lui indique la validité du mouvement. Les emplacements sont gérés comme suit :

0 1 2

3 4 5

6 7 8

Le Joueur transmet au client les messages suivants :

Message Méthode émettrice

"Mouvement valable." run()

"Mouvement interdit, réessayez." run()

"L'adversaire a joué " suivi par la valeur de l'emplacement (entier)

autreJoueurAJoue()

"Autre joueur connecté. À vous de jouer." run()

"Joueur [X/O] connecté, veuillez patienter" run()

239/286

Page 240: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 17 - Quatre TP supplémentaires en Java

Message Méthode émettrice

"Attente d'un autre joueur." run()

Le serveur conserve et met à jour les informations relatives au plateau de jeu, de sorte qu'il peut déterminer si un mouvement demandé par un des joueurs est valable ou non. Par contre comme nous pouvons le constater, il ne gère pas encore la fin de partie ni le résultat de celle-ci ! Le diagramme de classes côté serveur est le suivant :

b) Côté Client :

Chaque applet (ou application) ClientMorpion entretient sa propre version d'interface utilisateur graphique, laquelle informe sur l'état du jeu. Les clients ne peuvent placer une « marque » que sur une case libre du plateau de jeu. La classe Case sert à mettre en place les 9 cases du plateau et la classe EcouteurCase à gérer le clic souris sur la case courante. Un diagramme de classes définissant le côté client est proposé ci-après.

Le ClientMorpion a les caractéristiques suivantes :• id : Champs de texte pour l'indication de marque du joueur.• affichage : Zone de texte pour l'affichage des messages.• panneauPlateau , panneau2 : Plateaux pour contenir les cases du jeu.• plateau : Cases du plateau de jeu.• caseCourante : Case courante.• connexion : Socket pour la connexion.• entree : Flux d'entrée DataInputStream. • sortie : Flux de sortie DataOutputStream.• threadSortie : Thread pour permettre à cette applet/application de mettre à

jour en continu sa sortie dans la zone de texte affichée.• maMarque : Marque « X » ou « O » associée.• monTour : Indicateur de tour.

240/286

Figure 65: Diagramme de classes côté serveur.

Page 241: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 17 - Quatre TP supplémentaires en Java

et permet les opérations suivantes :• void init() : Permet de définir l'interface utilisateur et le plateau. Dans le cas d'une

application, il s'agira du constructeur de ClientMorpion.• void start() : Établir la connexion au serveur et prendre les flux associés. Démarrer un

thread séparé pour permettre à cette applet de mettre à jour en continu sa sortie dans la zone de texte affichée sans bloquer le système.

• void run() : Tread de contrôle qui permet une mise à jour permanente de l'affichage de la zone de texte. Elle lit les messages en provenance du serveurMorpion, tout d'abord pour connaître sa marque associée puis ensuite pour les traiter en appelant sa méthode traiterMessage().

• void traiterMessage(String s) : Traitement du message s en provenance du serveur (voir ci-dessus pour la liste des messages possibles). L'interface graphique est mise à jour.

• void envoyerClicACase(int empl) : Envoie au serveur la valeur entière de l'emplacement empl. Est appelée par EcouteurCase dans sa méthode mouseReleased().

• void setCaseCourante(Case c) : Met à jour la case courante à l'aide de c.

La Case a les caractéristiques suivantes :• marque : Marque prise par la case : « », « X » ou « O »• emplacement : Valeur de l'emplacement. Les emplacements sont gérés comme pour Joueur :

0 1 2

3 4 5

6 7 8

et permet les opérations suivantes :• Case(char m, int empl) : Construit une case de marque m à l'emplacement empl.• Dimension getPreferredSize() : Méthode de JPanel permettant de fixer une taille

adéquate pour le composant.

241/286

Figure 66: Diagramme de classes côté client.

Page 242: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 17 - Quatre TP supplémentaires en Java

• Dimension getMinimumSize() : Méthode de JPanel permettant d'imposer une taille minimale.

• void setMarque(char c) : Configure la marque de la case à la valeur c.• int getEmplacementCase() : Retourne l'emplacement correspondant à la case.• public void paintComponent(Graphics g) : Méthode de JComponent à redéfinir pour

dessiner la case.

L' EcouteurCase a les caractéristiques suivantes :• applet : Le ClientMorpion parent.• caseCourante : La case courante.

et permet les opérations suivantes :• EcouteurCase(ClientMorpion t, Case c) : Construit un nouvel auditeur d'événements

souris pour le ClientMorpion t et la case c.• void mouseReleased(MouseEvent e) : Redéfinie la méthode mouseReleased() de la

classe MouseAdapter afin de mettre à jour la case courante dans l'applet parente et d'envoyer l'emplacement de la case au serveur à l'aide de la méthode envoyerClicACase().

4.2. Travail demandé

4.2.1. Client pour le jeu du Morpion

Un ServeurMorpion est disponible pendant les heures de TP à l'adresse 172.16.112.160, port 5027, pour vous permettre de tester vos clients. Attention, une seule instance est exécutée sur le serveur, donc gare aux télescopages. Pour le travail à la maison, le paquetage serveurMorpion.jar est téléchargeable.

Construire une application ou une applet ClientMorpion permettant d'implémenter le jeu. Vous pouvez vous inspirer des caractéristiques fournies dans ce document pour la réalisation de votre client, ou bien proposer une interface graphique plus sophistiquée. Le tout est que vous respectiez le protocole de communication décrit.

4.2.2. Serveur pour le jeu du Morpion

Concevoir le ServeurMorpion répondant aux caractéristiques énoncées plus haut. Le tester avec votre client.

4.3. Conditions

• JDK version 1.5.0_X et sa documentation en ligne.

242/286

Page 243: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Chapitre 18 - Annexes

CHAPITRE 18 - ANNEXES

243/286

Page 244: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 1 – Utilisation d'Eclipse

ANNEXE 1 – UTILISATION D'ECLIPSE

Le logiciel Eclipse est un IDE (environnement intégré de développement / Integrated Development Environment) conçu à l'origine pour le langage Java. Ce logiciel est Open Source et peut être téléchargé sur le site www.eclipse.org. Nous utilisons également un plugin pour ce logiciel afin de manipuler les diagrammes UML. Il s’agit d'ArgoEclipse, une intégration de l'outil de modélisation ArgoUML récupérable directement depuis l'IDE et, dont les instructions d'installation se trouvent sur le site argoeclipse.tigris.org. Il n’est pas question dans ce chapitre de couvrir l'ensemble des fonctionnalités de l'IDE Eclipse et seules les principales seront présentées ici ; charge vous est donnée de découvrir les autres possibilités offertes afin d’accroître encore l'efficacité dans votre travail de développement. L'intérêt d’un tel outil est de permettre au programmeur de se concentrer sur l’essentiel de son travail en se dégageant des problèmes techniques sans importance.

1. Créer un projet

Eclipse fonctionne sur le principe des projets. Pour créer un nouveau projet, il faut tout d'abord sélectionner le menu File|New|Project... puis choisir Java Project et enfin cliquer sur Next. Il est nécessaire de donner un nom au projet, par exemple test, puis de choisir éventuellement un répertoire de l'espace de travail où seront rangés les fichiers de ce projet. Cliquons alors sur Next.Dans la fenêtre suivante, il est possible de modifier le répertoire des codes sources par défaut, de même pour la partie inférieure de la fenêtre où il est possible de préciser le répertoire où seront rangés les fichiers compilés (les .class). Enfin, la création du projet se conclut par un clic sur Finish.

2. Créer un paquetage

Pour créer un paquetage, il faut se placer sur l'icône du dossier src, de l'arborescence de gauche, appelée Package Explorer (explorateur de paquetages), et cliquer-droit puis New et Package. Nommons ce paquetage, pack1 par exemple.

3. Créer une classe/interface

Sur l'icône du paquetage, cliquons-droit et New puis choisissons Class ou Interface. Créons une classe C1 puis une interface I1 : il suffit de donner le nom et de cliquer sur Finish. Des éditeurs pour ces entités s’ouvrent automatiquement. Créons une signature de méthode public void opererI1() pour I1, un constructeur avec un paramètre entier et une méthode public void opererC1() (mettons des corps vides) pour C1. Sauvegardons les fichiers édités.Nous allons créer un autre classe, appelons-la C2, qui implémente l’interface I1 et hérite de C1. Pour cela, remplissons les champs Superclass et Interfaces (éventuellement à l’aide des boutons fournis). Dans la partie inférieure, activons Constructors from superclass et vérifions que Inherited abstract methods est déjà cochée.Nous pouvons remarquer la génération automatique du constructeur et de la méthode opererI1().

244/286

Page 245: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 1 – Utilisation d'Eclipse

Ajoutons un attribut entier x à cette classe et enregistrons. Dans la fenêtre d'édition, cliquons-droit puis Source, puis Generate Getter and Setter, une fenêtre s'ouvre, nous permettant de générer automatiquement les méthodes accesseurs getX() et setX(). Faisons-le.

4. Différentes vues et fenêtres

Window|Show View|Package Explorer affiche une fenêtre permettant de parcourir les paquetages et leur contenu.Window|Show View|Outline présente la structure d’un fichier édité, différentes icônes permettent de différencier les éléments publiques, privés, etc..Window|Open Perspective|Java Browsing offre une présentation différente des différents paquetages, classes et éléments.Window|Open Perspective|Java permet de revenir à la présentation précédente.

5. Autre création et code

Créons un second paquetage pack2, et créons-y une classe C3 dans laquelle nous allons spécifier un attribut de type C1 : private C1 att. Nous pouvons remarquer un symbole qui apparaît dans la marge, celui-ci signale une erreur (la croix blanche sur fond rouge). Le code que nous saisissons est analysé au fur et à mesure et en cas d’erreur, la source d’erreur estimée est soulignée de rouge dans le code : ici C1. L'ampoule mentionne qu'une proposition d'aide de correction est disponible. Cliquons-gauche sur cette ampoule, le premier choix de correction suggère l'import, c'est ce qu'il faut faire donc faisons-le à l'aide d'un double-clic. Le code nécessaire est alors ajouté. Une autre manière de procéder est : dans la fenêtre d'édition, de cliquer-droit puis Source, puis Organize Imports qui permet d'ajouter tous les imports nécessaires en une fois.Ajoutons une méthode public void opererC3(int i). Dans le corps de cette méthode tapons this. (avec le point). Si nous patientons un bref instant après le point, les complétions possibles (c'est-à-dire autorisées par le type) apparaissent par ordre alphabétique et nous pouvons choisir celle qui nous convient en la sélectionnant par la touche <Entrée>. Sinon, au fur et à mesure que nous tapons des caractères, les propositions se réduisent. Choisissons att. Ensuite tapons . (point), à nouveau les complétions apparaissent, choisissons opererC1().Plaçons notre pointeur de souris sur ce opererC1(). Deux solutions, <CTRL>-clic-gauche ou cliquer-droit puis Open declaration, nous permettent d'accéder directement au code de cette méthode. Il en est de même si nous opérons sur un nom de classe ou d'interface.Après un clic-droit sur un élément du code (classe, méthode, attribut, etc.), le menu Search offre différentes possibilités. Notamment le choix References|Workspace qui permet de connaître tous les endroits du code où cet élément apparaît. Ces occurrences sont affichées dans une fenêtre à part (en bas de l’écran généralement) et sont accessibles par un clic. Essayons sur la définition de la méthode opererC1() de la classe C1, nous retrouvons son usage dans la classe C3.Ce menu Search offre d’autres possibilités à découvrir.

6. Pour générer la Javadoc

Plaçons notre curseur sur la signature de la méthode opererC3(). Cliquons-droit puis Source, puis Add javadoc Comment. Le squelette pour la documentation est automatiquement inséré.

245/286

Page 246: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 1 – Utilisation d'Eclipse

7. Pour exécuter le code

Plaçons une méthode main() dans la classe C3, et contentons-nous de lui faire afficher un message quelconque. Puis sélectionnons Run|Run... (ou l'icône ). Choisissons Java Application et enfin cliquons sur New. Le champ Project: doit être déjà à jour. Cliquons sur Search... , les classes du projet contenant un main() sont proposées (ici il n'y en a qu’une), sélectionnons-la, validons et cliquons sur Run. Le programme est alors exécuté et une trace d'exécution apparaît dans la console en bas de l'écran.

8. Pour une remise en forme du projet

Des outils peuvent nous aider à réorganiser (refactoring) notre projet : déplacer des classes, modifier des noms de méthodes, etc.. Dans le code de C1.java, sélectionnons la méthode opererC1(), cliquons-droit puis Refactor puis Rename... et changeons le nom de la méthode en procederC1() par exemple, puis validons. Nous pouvons vérifier dans C3 que le code de la méthode a bien été adapté. Nous pouvons également changer la signature d’une méthode. Pour cela, dans l'explorateur de paquetages, sélectionnons C2.java, cliquons-droit puis Refactor, puis Move... et choisissons le paquetage pack2. La classe est alors déplacée et les modifications nécessaires sont faites, notamment la mise à jour des imports.

9. Eclipse et UML

Installer le plugin ArgoEclipse puis visualiser le tutoriel vidéo disponible sur la page : http://argoeclipse.tigris.org/documentation/video/screencast.html

9.1. Génération UML à partir du code Java

En utilisant les fonctions d'importation de codes sources, réalisons les diagrammes de classes pour les paquetages pack1 et pack2.

9.2. Création de code Java à partir de diagrammes UML

La démarche adoptée dans le paragraphe précédent et appelée reverse engineering, n'est en général pas appliquée en début d'analyse puisqu'à priori les diagrammes de classes doivent précéder l'écriture de code.Dans la zone de diagramme du paquetage pack2, créons une nouvelle classe C4.Un clic-droit sur le diagramme de classes permet d'y ajouter des méthodes, attributs, etc.. Ajoutons une méthode public int opererC4(), puis un attribut privé dong de type String, qui aura une valeur par défaut ding et pour lequel nous allons générer les accesseurs automatiquement.Ajoutons un constructeur public prenant un objet String en paramètre et visualisons le code source ainsi généré. Le diagramme UML et le code source sont synchronisés.

10. Déboguer un programme

Eclipse dispose d’un débogueur offrant de nombreuses possibilités. Nous pouvons exécuter une application en mode débogueur via le menu Run|Debug... ou encore, en cliquant sur le bouton .

246/286

Page 247: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 1 – Utilisation d'Eclipse

Nous pouvons placer des points d’arrêt dans le code. Il suffit pour cela de cliquer dans la zone d’ascenseur située à gauche de la fenêtre d'édition (double-clic ou clic-droit puis Add Breakpoint). En mode débogueur, le flux d’exécution du programme fait une pause sur ces points. Dans la perspective debugger qui s’affiche alors, il est possible d'examiner dans la zone supérieure droite, l'état de l’objet invoquant et des variables locales de la méthode interrompue.A vous de découvrir plus en détail le fonctionnement de ce débogueur lorsque vous en aurez le temps et/ou en éprouverez le besoin (reprenez par exemple votre TP Java n° 2).

11. Importer et exporter

Nous pouvons aussi récupérer des fichiers et/ou paquetages créés en dehors d'Eclipse pour les inclure dans un projet. Pour cela : File|Import... puis choisissons File System. Ensuite sélectionnons le(s) fichier(s) ou le(s) répertoire(s) à inclure (dans le cas où nous souhaitons récupérer tout un paquetage, il faut sélectionner le répertoire racine du répertoire correspondant au paquetage).Pour générer un fichier jar pour un projet, choisissons File|Export... puis JAR file et ensuite sélectionnons les fichiers et/ou paquetages à inclure dans l'archive, puis précisons si les codes sources doivent être inclus ou non. Donnons un nom à l'archive. En cliquant sur Next (2 fois de suite), nous arrivons à la fenêtre permettant de gérer le fichier MANIFEST et d'indiquer la classe comportant la méthode main() (main-class) de l'archive jar afin de rendre cette dernière exécutable.

247/286

Page 248: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 2 - Images et animations

ANNEXE 2 - IMAGES ET ANIMATIONS

L'objectif de cette annexe est d'apprendre à manipuler des images et à effectuer une animation.

1. Charger une image

Charger une image n'est pas difficile. Nous présentons une façon valable aussi bien pour une application que pour une applet.Nous utilisons la classe java.awt.Toolkit. Cette classe, sert essentiellement, lorsqu'elle est implémentée, à créer des objets dépendant de la plate-forme et ne doit donc pas être utilisée pour un tel usage si l'on veut un programme portable. Néanmoins, quelques méthodes de cette classe seront utilisées avec profit. La méthode getToolkit() de la classe java.awt.Component nous permet de disposer de l'objet Toolkit de notre fenêtre. Cet objet Toolkit nous permet alors d'utiliser la méthode getImage(String) de la classe Toolkit pour charger une image. On indique à la méthode getImage() l'adresse du fichier contenant l'image pour une application et l'URL de l'image pour une applet.

import java.awt.*;

public class MonImage extends Frame { Image duke, logo;

public MonImage() { duke = getToolkit().getImage("DukeWithHelmet.png"); logo = getToolkit().getImage("java.jpg"); }

public void paint(Graphics g) { g.drawImage(duke, 20, 20, 150, 150, this)101; g.drawImage(logo, 150, 96, this)102;

101 cette méthode de la classe java.awt.Graphics prend en paramètres une image, les coordonnées du coin supérieur gauche, les dimensions (l'image d'origine pourra être dilatée ou réduite en fonction de ces indications) et un paramètre de la classe ImageObserver que toute instance de la classe java.awt.Component implémente. Ce paramètre ne sert souvent pas, et on aurait pu ici indiquer la valeur null à la place de this.

102 cette seconde méthode drawImage() se dispense des paramètres donnant la dimension de l'image ; l'image aura sa dimension

248/286

Figure 67: Capture de la fenêtre MonImage.

Page 249: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 2 - Images et animations

}

public static void main(String[] argv) { MonImage monCadre = new MonImage(); monCadre.setSize(300,220); monCadre.setVisible(true); }}

2. Utiliser un MediaTracker

Nous pouvons remarquer qu'au moment de l'apparition de l'image, le dessin se trace progressivement, en tout cas lorsque les images sont chargées pour la première fois. Cela vient du fait qu'il faut du temps pour charger l'image et que le dessin se fait au fur et à mesure du chargement, Si on veut éviter au moins partiellement cela, on peut utiliser une instance de la classe java.awt.MediaTracker, qui permet de surveiller le chargement des images.

import java.awt.*;

public class ImageTrack extends Frame { Image duke, logo; MediaTracker tracker; public ImageTrack() { tracker = new MediaTracker(this); duke = getToolkit().getImage("DukeWithHelmet.png"); tracker.addImage(duke, 0)103; logo = this.getToolkit().getImage("java.jpg"); tracker.addImage(monde,0); try { tracker.waitForID(0)104; } catch(InterruptedException e) {} }

public void paint(Graphics g) { if (tracker.statusID(0,false) != MediaTracker.COMPLETE105) return;

d'origine. D'autres jeux de paramètres sont disponibles pour cette méthode en consultant la documentation de la classe Graphics.

103 on ajoute ainsi une image au tracker. Cela ne débute néanmoins pas le chargement de l'image. Une même valeur de paramètre, ici la valeur 0, peut être attribuée à plusieurs images. On appellera "identificateur de l'image" la valeur de ce paramètre , bien que celui-ci n'identifie pas à proprement parlé l'image.

104 on débute avec cette instruction le chargement de toutes les images d'identificateur 0 et on attend jusqu'à ce que ce chargement soit terminé. La méthode waitForAll() ferait la même chose pour toutes les images à la fois. Si le chargement des images ne s'est pas fait correctement, mais si l'opération de chargement n'a plus rien à faire, le chargement est considéré comme terminé.

105 est égal à MediaTracker.COMPLETE si le chargement des images d'identificateur 0 s'est terminé avec succès. Si le paramètre booléen vaut true (ici, il vaut false), le chargement d'images qui ne serait pas commencé serait alors démarré par la méthode

249/286

Figure 68: Capture de la fenêtre ImageTrack.

Page 250: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 2 - Images et animations

g.drawImage(duke, 20, 20, 150, 150,this); g.drawImage(java, 150, 96, this); }

public static void main(String[] argv) { ImageTrack monCadre = new ImageTrack(); monCadre.setSize(300,200); monCadre.setVisible(true); }}

3. Utiliser une image arrière

L'intérêt de ce que nous faisons ici se verra mieux à travers d'une animation ou d'une construction d'image plus compliquée. Il s'agit de préparer l'image que l'on veut voir afficher, dans une image en arrière-plan, dite « image arrière », pour la voir s'afficher d'un seul coup.

On utilise encore cette fois-ci, une instance de MediaTracker pour surveiller la fin du chargement des images. Avant de dessiner l'image, on la dessine dans l'image arrière puis on dessine l'image arrière à l'écran. Pour créer une image, on utilise ici la méthode :

createImage(int largeur, int hauteur)

de la classe java.awt.Component. Il n'est généralement pas utile d'utiliser cette méthode avant que la construction de la composante concernée ne soit achevée, car avant cela, elle retourne null. Comme par ailleurs la méthode paint() de la composante n'est pas exécutée avant que la construction de cette même composante ne soit terminée, tout se passe bien.

import java.awt.*;

public class UneImageArriere extends Frame { int largeurImage; Image duke; Image imageArriere; MediaTracker tracker;

Font font = new Font("TimesRoman", Font.PLAIN, 20); String chaine = "Java est un bon langage!";

public UneImageArriere() { setLayout(new FlowLayout(FlowLayout.CENTER,5,5)); FontMetrics mesure = getFontMetrics(font);

statusID.

250/286

Figure 69: Capture de la fenêtre UneImageArriere.

Page 251: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 2 - Images et animations

setBackground(Color.cyan); largeurImage = mesure.stringWidth(chaine) + 70; duke = getToolkit().getImage("DukeWithHelmet.png"); tracker = new MediaTracker(this); tracker.addImage(duke, 0); try { tracker.waitForID(0); } catch (InterruptedException e) {} }

public void paint(Graphics g) { Graphics graphiqueArriere;

if (tracker.statusAll(false) != MediaTracker.COMPLETE) return; if (imageArriere == null) { setBackground(Color.green)106; imageArriere = createImage(largeurImage,90)107; setBackground(Color.cyan); } graphiqueArriere = imageArriere.getGraphics()108; graphiqueArriere.drawImage(duke, 10, 10, this); graphiqueArriere.setFont(font); graphiqueArriere.drawString(chaine, 70, 40); g.drawImage(imageArriere, 70, 40, this)109; } public static void main(String[] argv) { UneImageArriere monCadre = new UneImageArriere(); monCadre.setSize(500,140); monCadre.setVisible(true); }}

4. Une animation peu réussie

L'animation dont le code source est ci-dessous n'est pas très fluide et l'objectif est de montrer avec les deux exemples suivants comment on peut l'améliorer. Le problème est double :

• la méthode repaint() appelle la méthode update() qui commence par nettoyer le

106 le fond de l'image arrière sera vert. Remarquez que cela ne se passe pas de la même façon avec une applet. Pour avoir le même effet avec une applet, il suffit. lorsque l'image est créée, et avant d'y tracer ce que l'on veut y tracer, de remplir l'image par un rectangle plein vert.

107 on crée ici une image qui nous servira d'image arrière. 108 il faudra utiliser l'objet Graphics graphiqueArriere pour tracer dans imageArriere. 109 lorsque l'image arrière est prête, il ne reste plus qu'à la tracer dans notre fenêtre.

251/286

Figure 70: Capture de la fenêtre Hypnotiseur1.

Page 252: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 2 - Images et animations

contenu de la fenêtre, ce qui peut donner l'impression de flashs blancs, avant de redessiner le contenu avec la méthode paint().

• le tracé de l'ensemble des arcs est long, et ne se fait donc que progressivement.

import java.awt.*;

public class Hypnotiseur1 extends Frame implements Runnable { int largeur = 200, hauteur = 200, dep = 0; Image imageArriere; public Hypnotiseur1() { (new Thread(this)).start(); }

public void run() { while ( true ) { dep = (dep - 1) % 5; repaint(); try { Thread.sleep(500); } catch(InterruptedException exc){} this.getToolkit().sync()110; } }

public void paint(Graphics g) { int i, j;

if (g == null) return; for (i = dep-10; i < largeur/2; i += 10) { g.setColor(Color.black); for (j = i; j < i+5; j++) if (j > 0) g.drawOval(j,j,largeur-2*j,hauteur-2*j); g.setColor(Color.white); for (j = i+5; j < i+10; j++) if (j > 0) g.drawOval(j,j,largeur-2*j,hauteur-2*j); } }

public static void main(String[] argv) { Hypnotiseur1 monCadre = new Hypnotiseur1(); monCadre.setSize(monCadre.largeur,monCadre.hauteur); monCadre.setVisible(true); }}

5. Un peu mieux sans utiliser la méthode repaint()En principe, l'hypnotiseur est plus fluide que le précédent. Ici, on utilise simplement la méthode paint(Graphics) à la place de la méthode repaint() qui, sinon commence par effacer le contenu de la fenêtre, ce qui n'est pas utile ici puisque de toute manière, toute la zone qui change entre un dessin et le suivant se trouve repeinte par la méthode paint(). Rappelons que la méthode repaint() appelle la méthode update(Graphics). Cette dernière, à moins d'être redéfinie, nettoie tout le composant concerné et attribue à l'objet Graphics passé en paramètre la couleur d'avant-plan du composant concerné. Puis elle appelle la méthode

110 certains systèmes gardent en mémoire-tampon certains événements d'un objet Graphics. Cette instruction assure que les dessins qui sont à faire se font. Elle est souvent indispensable dans des animations.

252/286

Page 253: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 2 - Images et animations

paint(graphics) pour repeindre le composant quand une partie de l'image est détériorée, la méthode paint() est appelée pour retracer la partie abîmée ; cette partie est décrite à travers l'objet Graphics passé en paramètre.

import java.awt.*;

public class Hypnotiseur2 extends Frame implements Runnable { int largeur = 200, hauteur = 200, dep = 0; Image imageArriere; public Hypnotiseur2() { (new Thread(this)).start(); }

public void run() { while ( true ) { dep = (dep - 1) % 5; paint(getGraphics()); try { Thread.sleep(500); } catch(InterruptedException exc){} this.getToolkit().sync()111; } }

public void paint(Graphics g) { int i, j;

if (g == null) return; for (i = dep-10; i < largeur/2; i += 10) { g.setColor(Color.black); for (j = i; j < i+5; j++) if (j > 0) g.drawOval(j,j,largeur-2*j,hauteur-2*j); g.setColor(Color.white); for (j = i+5; j < i+10; j++) if (j > 0) g.drawOval(j,j,largeur-2*j,hauteur-2*j); } }

public static void main(String[] argv) { Hypnotiseur2 monCadre = new Hypnotiseur2(); monCadre.setSize(monCadre.largeur,monCadre.hauteur); monCadre.setVisible(true); }}

6. Une animation avec une image arrière

Nous espérons que cette fois-ci, l'hypnotiseur est plus efficace. En fait, il est encore un peu « haché » à cause du temps nécessaire au dessin de l'image arrière. Le principe est simple et à déjà été utilisé dans un de nos exemples précédents : le dessin est dessiné dans un image arrière et, lorsque l'image arrière est terminée, elle est dessinée d'un seul coup dans la fenêtre.

import java.awt.*;

111 certains systèmes gardent en mémoire-tampon certains événements d'un objet Graphics. Cette instruction assure que les dessins qui sont à faire se font. Elle est souvent indispensable dans des animations.

253/286

Page 254: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 2 - Images et animations

public class Hypnotiseur3 extends Frame implements Runnable { int largeur = 200, hauteur = 200, dep = 0; Image imageArriere; boolean peint = false;

public Hypnotiseur3() { ( new Thread(this) ).start(); }

public void run() { Graphics g, gArriere; while ( !peint )112

try { Thread.sleep(500); } catch(InterruptedException exc){} imageArriere = createImage(largeur, hauteur); gArriere = imageArriere.getGraphics(); g = this.getGraphics(); while (true) { dep = (dep - 1) % 10;; paint(gArriere); g.drawImage(imageArriere,0,0,this); try { Thread.sleep(100); } catch(InterruptedException exc){} } } public void paint(Graphics g) { int i, j; peint = true; g.setColor(Color.white); g.fillRect(0,0,largeur,hauteur); g.setColor(Color.black); for (i = dep-10; i < largeur/2; i += 10) { for (j = i; j < i+5; j++) if ( j > 0 ) g.drawOval(j,j,largeur-2*j,hauteur-2*j); } }

public static void main(String[] argv) { Hypnotiseur3 monCadre = new Hypnotiseur3(); monCadre.setSize(monCadre.largeur,monCadre.hauteur); monCadre.setVisible(true); }}

7. Une image qui défile

On trace un dessin dans une image puis on fait défiler celle-ci dans la fenêtre.

112 la variable peint est une variable d'instance initialisée à faux, et qui prend la valeur vrai lorsque la méthode paint() est appelée pour la première fois. Elle sert à repérer que la construction de la fenêtre est terminée. Rappelez-vous que la méthode createImage() de la classe Component renvoie null tant que la composante concernée n'est pas construite.

254/286

Page 255: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 2 - Images et animations

L'essentiel du travail consiste à prendre quelques mesures en n'oubliant pas lors du déplacement de l'image, de nettoyer la partie du fond qui a été découverte. D'autre part, nous mettons à contribution la méthode sync() de l'instance de la classe java.awt.Toolkit liée à la fenêtre. En effet, dans certains cas, les affichages à faire dans une fenêtre sont mis dans une mémoire-tampon et l'affichage effectif est différé. La méthode sync() provoque un affichage immédiat (l'accès à l'objet Toolkit lié à la fenêtre se fait par la méthode getToolkit() de la classe java.awt.Component).

import java.awt.*;

class Banderole extends Frame implements Runnable { Image imageArriere; int largeurTexte, largeurFenetre = 300, hauteurFenetre = 150; int x = largeurFenetre, largeurImage, hauteurImage; Image duke = null; String chaine; MediaTracker tracker;

public Banderole() { chaine = "Java est un beau langage et son "+ "apprentissage est un vrai plaisir"; duke = getToolkit().getImage("DukeWithHelmet.png"); setBackground(Color.yellow); tracker = new MediaTracker(this); tracker.addImage(duke, 0); ( new Thread(this) ).start(); }

public void run() { Graphics g; Font font; int ordonnee, pas = 10; try { tracker.waitForID(0)113; } catch(InterruptedException e) { return; }

font = new Font("TimesRoman",Font.PLAIN,25); FontMetrics mesure = getFontMetrics(font); largeurTexte = mesure.stringWidth(chaine); largeurImage = largeurTexte + duke.getWidth(this) + 10; hauteurImage = duke.getHeight(this); if (hauteurImage < mesure.getHeight()) hauteurImage = mesure.getHeight();

113 cette attente est réalisée ici car le chargement des images ne se termine pas avant que la construction de la fenêtre ne soit terminée. Ainsi, la méthode createImage() utilisée juste après, qui retourne null si la construction de la fenêtre n'est pas terminée, pourra travailler correctement.

255/286

Figure 71: Capture de la fenêtre Banderole.

Page 256: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 2 - Images et animations

imageArriere = createImage(largeurImage, hauteurImage); g = imageArriere.getGraphics(); g.drawImage(duke, 0, 0, this); g.setFont(font); g.setColor(Color.blue); g.drawString(chaine, duke.getWidth(this) + 10, hauteurImage-mesure.getDescent()); g = getGraphics(); ordonnee = (hauteurFenetre-hauteurImage)/2; while (true) { g.drawImage(imageArriere, x, ordonnee, this); getToolkit().sync(); try { Thread.sleep(500); } catch(InterruptedException exc){} x = x - pas; if (x + largeurImage < largeurFenetre) g.clearRect(x + largeurImage, ordonnee, pas, hauteurImage); if (x < -largeurImage) x = largeurFenetre; } }

public static void main(String[] argv) { Banderole monCadre = new Banderole(); monCadre.setSize(monCadre.largeurFenetre,monCadre.hauteurFenetre); monCadre.setVisible(true); }}

256/286

Page 257: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 3 – Internationalisation en Java

ANNEXE 3 – INTERNATIONALISATION EN JAVA

Java propose un certains nombre de classes pour permettre l'internationalisation des applications, l'important étant de séparer le code des textes affichés dans le programme.

• java.util.Locale : permet de définir un objet représentant une région géographique, politique ou encore culturelle. Plus simplement, un objet Locale spécifie la langue, le pays, la monnaie, la manière de représenter les dates, etc. ;

• java.util.ResourceBundle : littéralement « paquet de ressource », permet de définir des ressources nécessaires à l’exécution d’un programme. Lorsqu'un programme a besoin d'une ressource locale, un String par exemple, il doit la charger depuis le paquet approprié à l'utilisateur courant.

Par exemple, si l'on souhaite créer une application affichant des messages "Bienvenue" en français, et en japonais, il faut d'abord créer deux fichiers message_fr.properties et message_ja.properties (l'extension .properties est importante), contenant les textes suivants :

# Fichier pour dire bienvenue en français (message_fr.properties) bienvenue=Bonjour

et # Fichier pour dire bienvenue en japonais (message_ja.properties) # Se dit YÔKOSO en japonais bienvenue=\u3088\u3046\u3053\u305D

Le programme d'affichage est alors le suivant :import java.util.*; public class AfficheurPolyglotte { public static String getBienvenue(String f, String cle, Locale lc) { String s = null; try { ResourceBundle rb = ResourceBundle.getBundle(f, lc); s = rb.getString(cle); } catch (MissingResourceException e) { s = null; } return s; } public static void main(String args[]) { String fichier = "message"; String bienvenue = "bienvenue"; Locale francais = Locale.FRENCH; Locale japonais = Locale.JAPANESE; System.out.println("Français = " + francais); System.out.println("Japonais = " + japonais);

257/286

Page 258: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 3 – Internationalisation en Java

System.out.println(getBienvenue(fichier, bienvenue, francais)); System.out.println(getBienvenue(fichier, bienvenue, japonais)); }}

258/286

Page 259: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 4 - Travailler avec une base de données MySQL

ANNEXE 4 - TRAVAILLER AVEC UNE BASE DE DONNÉES MYSQL

Le système de gestion de base de données Open Source MySQL peut être téléchargé à partir du site : http://www.mysql.com/. De nombreux exemples de manipulations en langage Java peuvent aussi être trouvés sur le site http://www.javaalmanac.com. Nous rappelons dans un premier temps les différentes commandes MySQL afin que de permettre la manipulation de bases de données et tables, indépendamment de tout autre outil. Nous terminons par la mise en place d'une connexion entre Java et une base de données MySQL, de manière à pouvoir relier et faire interagir applications Java et bases.

1. Rappels sur MySQL

1.1. Création de bases et de tables

Nous souhaitons créer la base de données tsii contenant la table Etudiants. Remarquons que les requêtes SQL sous le moniteur du client mysql se terminent par un ;.

• Enregistrement d’un utilisateur au SGBDR :mysql -h 172.16.112.131 -u frosch -p froschEnter password: ********

• Création de la base de données tsii : mysql> CREATE DATABASE tsii;

• Utilisation de la base de données tsii : mysql> USE tsii; Database changed

• Vérification des tables présentes dans la base : mysql> SHOW TABLES; Empty set (0.00 sec)

• Création d’une table Etudiants :Remarque : Par défaut, les champs peuvent être vides (valeur NULL). Il peut être nécessaire de l'éviter, il faut alors leur donner une valeur par défaut.

mysql> CREATE TABLE Etudiants (Nom VARCHAR(25), Prénom VARCHAR(20), Naissance DATE, INSEE BIGINT(20));

• Revérification des tables présentes dans la base : mysql> SHOW TABLES; +----------------+ | Tables in tsii |

259/286

Page 260: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 4 - Travailler avec une base de données MySQL

+----------------+ | Etudiants | +----------------+

• Vérification de la structure de la table Etudiants :mysql> DESCRIBE Etudiants;

+-----------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-----------+-------------+------+-----+---------+-------+| Nom | varchar(25) | YES | | NULL | || Prénom | varchar(20) | YES | | NULL | || Naissance | date | YES | | NULL | || INSEE | bigint(20) | YES | | NULL | |+-----------+-------------+------+-----+---------+-------+

1.2. Remplissage d'une table

Charger une table depuis un fichier textemysql> LOAD DATA LOCAL INFILE "Etudiants.txt" INTO TABLE Etudiants;

qui charge le fichier local contenant les champs séparés par des tabulations :Bellier Olivier 1982-11-10 \NBrulin Charles 1980-11-04 \NCésarine Sébastien \N \NClef Olivier \N \NEl Mhard Brahim \N \NFriang Gilles \N \NGiraud Cyril 1979-11-15 \NHryc Olivier 1981-04-19 \NJuffroy Laurent 1982-08-15 \NKoné Farouk \N \NKoroglu Mustafa 1980-03-15 \NLopez Sylvain 1980-03-28 \NNGo Victor 1980-02-29 \NNoirot Sylvain 1982-04-05 \NPetit Neil \N \NSautreau Alban 1980-05-26 \NScelles Willy \N \NSidibé Malick 1981-04-10 \NSirodot Frédéric 1980-03-04 \NSomaskandarajah Prashauth \N \NTea Khievhen 1982-07-14 \NTecher Henri 1982-01-03 \NTiouajni Mounir \N \NTrierscheidt Mariana 1980-04-19 \NXiong Philippe \N \NZhao Ling Bo 1981-12-17 \NZitoumbi Idrisse 1982-12-07 \N

Charger une table directement mysql> INSERT INTO Etudiants VALUES ('Bellier','Olivier',1982-11-10,NULL), ('Brulin','Charles',1980-11-04,NULL), ..., ('Zitoumbi','Idrisse', 1982-12-07,NULL);

260/286

Page 261: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 4 - Travailler avec une base de données MySQL

1.3. Sélection de données d'une table

Sélectionner toutes les donnéesmysql> SELECT * FROM Etudiants;+-----------+----------+------------+--------+| Nom | Prénom | Naissance | INSEE |+-----------+----------+------------+--------+| Bellier | Olivier | 1982-11-10 | NULL || Brulin | Charles | 1980-11-04 | NULL || ... | ... | ... | ... |+-----------+----------+------------+--------+

Sélectionner des données particulièresmysql> SELECT * FROM Etudiants WHERE Nom = "Brulin";+-----------+----------+------------+--------+| Nom | Prénom | Naissance | INSEE |+-----------+----------+------------+--------+| Brulin | Charles | 1980-11-04 | NULL |+-----------+----------+------------+--------+

mysql> SELECT * FROM Etudiants WHERE Naissance >= "1982-11-01";+-----------+----------+------------+--------+| Nom | Prénom | Naissance | INSEE |+-----------+----------+------------+--------+| Bellier | Olivier | 1982-11-10 | NULL || Zitoumbi | Idrisse | 1982-12-07 | NULL |+-----------+----------+------------+--------+

On peut aussi combiner des conditions, comme par exemple pour connaître les étudiants nés après le 1er novembre 1982 et se prénommant Idrisse :

mysql> SELECT * FROM Etudiants WHERE Naissance >= "1982-11-01" AND Prénom = "Idrisse"; +-----------+----------+------------+--------+| Nom | Prénom | Naissance | INSEE |+-----------+----------+------------+--------+| Zitoumbi | Idrisse | 1982-12-07 | NULL |+-----------+----------+------------+--------+

On peut aussi exécuter l'opérateur OR : mysql> SELECT * FROM Etudiants WHERE Naissance >= "1982-11-01" OR Prénom = "Sylvain"; +-----------+----------+------------+--------+| Nom | Prénom | Naissance | INSEE |+-----------+----------+------------+--------+| Lopez | Sylvain | 1980-03-28 | NULL || Noirot | Sylvain | 1982-04-05 | NULL || Zitoumbi | Idrisse | 1982-12-07 | NULL |+-----------+----------+------------+--------+

AND et OR peuvent aussi être utilisés en commun :mysql> SELECT * FROM Etudiants WHERE (Naissance >= "1982-11-01" AND Prénom = "Idrisse") OR (Naissance >= "1982-01-01" AND Prénom = "Sylvain");

Sélectionner des champs particuliers

261/286

Page 262: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 4 - Travailler avec une base de données MySQL

Si l'ensemble des champs ne nous intéresse pas, il est possible de sélectionner des colonnes particulières. Les clauses WHERE, etc. sont toujours exploitables :

mysql> SELECT Nom, Naissance FROM Etudiants

Trier les lignes (clause ORDER BY)mysql> SELECT Nom, Prénom FROM Etudiants ORDER BY Naissance;+----------+------------+| Nom | Prénom |+----------+------------+| Giraud | Cyrille || NGo | Victor || Koroglu | Mustafa || ... | ... || Zitoumbi | Idrisse |+----------+------------+

Pour trier en ordre inverse, on peut utiliser le mot-clé DESC :mysql> SELECT Nom, Prénom FROM Etudiants ORDER BY Naissance DESC;

Pour trier suivant de multiples colonnes. Dans le cas suivant, le tri s'effectue d'abord par rapport aux prénoms triés dans l'ordre alphabétique, puis par ordre décroissant des dates de naissance :

mysql> SELECT Nom, Prénom FROM Etudiants ORDER BY Prénom ASC, Naissance DESC;+----------+------------+| Nom | Prénom |+----------+------------+| Sautreau | Alban || ... | ... || Noirot | Sylvain || Lopez | Sylvain || ... | ... |+----------+------------+

Faire des calculs sur les datesmysql> SELECT Nom, (TO_DAYS(NOW())-TO_DAYS(Naissance))/365 FROM Etudiants;+-----------+-----------------------------------------+| Nom | (TO_DAYS(NOW())-TO_DAYS(Naissance))/365 |+-----------+-----------------------------------------+| Bellier | 18.50 || Brulin | 20.51 || Césarine | NULL || Clef | NULL || El Mhard | NULL |+----------+------------------------------------------+

mysql> SELECT Nom, (TO_DAYS(NOW())-TO_DAYS(Naissance))/365 AS Age FROM Etudiants ORDER BY Nom;+----------+-------+| Nom | Age |+----------+-------+| Bellier | 18.50 || Brulin | 20.51 |

262/286

Page 263: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 4 - Travailler avec une base de données MySQL

| Césarine | NULL || ... | ... |+----------+-------+

ou encore, en triant par âges croissants :mysql> SELECT Nom, (TO_DAYS(NOW())-TO_DAYS(Naissance))/365 AS Age FROM Etudiants ORDER BY Age;+----------+-------+| Nom | Age |+----------+-------+| Césarine | NULL || ... | ... || Zitoumbi | 18.42 || Bellier | 18.50 || Juffroy | 18.73 || Tea | 18.82 || Noirot | 19.10 || ... | ... || NGo | 21.19 || Giraud | 21.48 |+----------+-------+

Pour savoir si des étudiants ont leur anniversaire au mois de mai :mysql> SELECT Nom, Naissance FROM Etudiants WHERE MONTH(Naissance) = 5;+----------+------------+| Nom | Naissance |+----------+------------+| Sautreau | 1980-05-26 |+----------+------------+

Recherche d'expressionsa) Rechercher les noms commençants par 'C':

mysql> SELECT * FROM Etudiants WHERE Nom LIKE "C%";

+-----------+-----------+------------+--------+| Nom | Prénom | Naissance | INSEE |+-----------+-----------+------------+--------+| Césarine | Sébastien | NULL | NULL || Clef | Olivier | NULL | NULL |+-----------+-----------+------------+--------+

b) Rechercher les noms se terminant par 't': mysql> SELECT * FROM Etudiants WHERE Nom LIKE "%t";

+--------------+-----------+------------+--------+| Nom | Prénom | Naissance | INSEE |+--------------+-----------+------------+--------+| Noirot | Sylvain | 1982-04-05 | NULL || Petit | Neil | NULL | NULL || Sirodot | Frédéric | 1980-04-03 | NULL || Trierscheidt | Mariana | 1980-04-19 | NULL |+--------------+-----------+------------+--------+

c) Rechercher les noms contenant un 'h':

263/286

Page 264: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 4 - Travailler avec une base de données MySQL

mysql> SELECT * FROM Etudiants WHERE Nom LIKE "%h%";

d) Rechercher les noms contenant cinq caractères (en utilisant le caractère '_'): mysql> SELECT Nom FROM Etudiants WHERE Nom LIKE "_____";

e) Utilisation de REGEXP (recherche d'expression régulière)Rechercher les noms commençant par b ou B (en utilisant le caractère '^') :

mysql> SELECT * FROM Etudiants WHERE Nom REGEXP "^[bB]";

Rechercher les noms se terminant par 'ot' (en utilisant le caractère $): mysql> SELECT Nom FROM Etudiants WHERE Nom REGEXP "ot$";+--------------+| Nom |+--------------+| Noirot || Sirodot |+--------------+

Rechercher les noms contenant une suite 'ha' (combinaison majuscules/minuscules) : mysql> SELECT * FROM Etudiants WHERE Nom REGEXP "[hH][aA]";+--------------+| Nom |+--------------+| El Mhard || Zhao |+--------------+

Rechercher les noms contenant cinq caractères : mysql> SELECT Nom FROM Etudiants WHERE Nom REGEXP "^.....$";

Compter les donnéesmysql> SELECT COUNT(*) FROM Etudiants;+----------+| COUNT(*) |+----------+| 27 |+----------+

Récupérer le nombre d'étudiants pour toutes les années de naissance : mysql> SELECT YEAR(Naissance) AS Année, COUNT(*) FROM Etudiants GROUP BY Annee;+--------+----------+| Année | COUNT(*) |+--------+----------+| NULL | 10 || 1979 | 1 || 1980 | 7 || 1981 | 3 || 1982 | 6 |+--------+----------+

ou encore pour ne récupérer que les dates fournies (WHERE Naissance IS NOT NULL) :mysql> SELECT YEAR(Naissance) AS Année, COUNT(*) FROM Etudiants WHERE

264/286

Page 265: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 4 - Travailler avec une base de données MySQL

Naissance IS NOT NULL GROUP BY Annee;+--------+----------+| Année | COUNT(*) |+--------+----------+| 1979 | 1 || 1980 | 7 || 1981 | 3 || 1982 | 6 |+--------+----------+

1.4. Associer plusieurs tables (REFERENCES et LAST_INSERT_ID())

Il est souvent intéressant d’associer plusieurs tables entre elles, plutôt que de créer des tables gigantesques. Dans l’exemple qui suit, la table Mathematiques est associée à la table Etudiants. Il n'est ainsi plus indispensable de stocker une seconde fois les informations Nom, Prénom et autres dans la table Mathematiques. Seul le champ Candidat fournit une relation avec le champ index de la table Etudiants.Dans ces deux tables, nous introduisons la notion d'index (chiffre entier permettant le pointage rapide d'une donnée), ainsi que la notion de clé primaire (combinaison de plusieurs champs permettant d'éviter les doublons dans les données). Dans notre cas, la clé primaire de chaque table n'est calculée que par rapport au champ index.

mysql> CREATE TABLE Etudiants (index SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, Nom VARCHAR(25), Prénom VARCHAR(20), Naissance DATE, INSEE BIGINT(20), PRIMARY KEY(index));

mysql> CREATE TABLE Mathematiques (index SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, Controle1 FLOAT(2.2), Controle2 FLOAT(2.2), Candidat SMALLINT UNSIGNED NOT NULL REFERENCES Etudiants, PRIMARY KEY(index));

mysql> INSERT INTO Etudiants VALUES(NULL, 'Bellier','Olivier',1982-11-10, NULL);

mysql> INSERT INTO Mathematiques VALUES(NULL, 12.50, 13.75, LAST_INSERT_ID());

mysql> INSERT INTO Etudiants VALUES(NULL, 'Brulin','Charles',1980-11-04, NULL);

mysql> INSERT INTO Mathematiques VALUES(NULL, 12.00, 13.00, LAST_INSERT_ID());

mysql> SELECT * FROM Etudiants;+--------+-----------+------------+------------+--------+| index | Nom | Prénom | Naissance | INSEE |+--------+-----------+------------+------------+--------+| 1 | Bellier | Olivier | 1982-11-10 | NULL || 2 | Brulin | Charles | 1980-11-04 | NULL |+--------+-----------+------------+------------+--------+

mysql> SELECT * FROM Mathematiques;+--------+-----------+------------+------------+| index | Controle1 | Controle2 | Candidat |+--------+-----------+------------+------------+| 1 | 12.50 | 13.75 | 1 |

265/286

Page 266: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 4 - Travailler avec une base de données MySQL

| 2 | 12.00 | 13.00 | 2 |+--------+-----------+------------+------------+

mysql> SELECT e.* FROM Etudiants e, Mathematiques m WHERE m.Controle2 > 13;+--------+-----------+------------+------------+--------+| index | Nom | Prénom | Naissance | INSEE |+--------+-----------+------------+------------+--------+| 1 | Bellier | Olivier | 1982-11-10 | NULL |+--------+-----------+------------+------------+--------+

2. Utilisation du langage Java pour communiquer avec MySQL

Java fournit un paquetage d'accès générique aux bases de données relationnelles. Celles-ci doivent implémenter un pilote d'accès compatible avec l'API JDBC spécifiée par Sun. Ainsi, toute application Java pourra accéder à une base de données, quelle qu'elle soit (MySQL, Oracle, DB2, Ms-Access, SQL Server, 4D, etc.), du moment qu’un pilote JDBC est fourni, et ce sans que le code soit complètement modifié.Connector/J est un pilote JDBC pour MySQL sous licence GNU/GPL et disponible à l'adresse http://dev.mysql.com/downloads/connector/j/5.0.html. Il permet aux développeurs Java d’effectuer des connexions à un serveur MySQL depuis une application ou encore une Applet Java.

2.1. Établir une connexion

La première chose à faire pour utiliser JDBC est d'obtenir la référence d'un objet de type Connection qui représente la connexion réseau vers une base de données sur laquelle vous souhaitez travailler. Avec JDBC, le gestionnaire de pilotes de bases de données (DriverManager) gère l'établissement des connexions. Celui-ci a besoin du nom du pilote avec lequel il devra établir ses connexions, et dans la majeure partie des cas, il s'agit de la première des deux seules lignes de code à modifier lorsque l'on change de SGBDR. La méthode la plus simple consiste à utiliser Class.forName() sur la classe qui implémente l’interface java.sql.Driver (ici org.gjt.mm.mysql.Driver).

Exemple : Enregistrer le pilote auprès du DriverManager import java.sql.*; ... try { Class.forName("org.gjt.mm.mysql.Driver").newInstance(); } catch (Exception e) { System.err.println("Impossible de charger le pilote."); e.printStackTrace(); } ...

Une fois le pilote enregistré, une connexion peut-être établie. Obtenir une connexion nécessite l’utilisation de l'URL de la base de donnée (deuxième ligne à modifier si vous changez de SGBDR). Cette URL est construite avec la syntaxe suivante (les éléments entre crochets étant optionnels) :

jdbc:mysql://[hostname][:port]/dbname[?param1=value1][&param2=value2]

Exemples :

266/286

Page 267: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 4 - Travailler avec une base de données MySQL

• URL local pour utilisateur froschjdbc:mysql://localhost/tsii?user=frosch&password=frosch

• URL local pour utilisateur froschjdbc:mysql://127.0.0.1/tsii?user=frosch&password=frosch

• URL sur le poste berty.TSII du réseau sans utilisateur définijdbc:mysql://berty.TSII/tsii

• URL sur le poste d’adresse 192.9.20.92 (berty.TSII) du réseau sans utilisateur définijdbc:mysql://192.9.200.92/tsii

L'URL est ensuite passée en paramètre à la méthode getConnection() de la classe DriverManager. Cette dernière retourne une référence sur un objet de type Connection : Exemple : Obtenir une connexion en local

... try { Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost/tsii?user=frosch&password=frosch"); // Faire quelque chose avec la connexion .... } catch (SQLException e) { System.out.println("SQLException: " + e.getMessage()); System.out.println("SQLState: " + e.getSQLState()); System.out.println("VendorError: " + e.getErrorCode()); }

Une fois la connexion établie, il est possible d'effectuer des manipulations sur la base de données.

2.2. Travailler avec les classes Statement et PreparedStatement

Statement et PreparedStatement sont deux classes qui facilitent la manipulation des données d’une base.

Travailler avec StatementStatement permet l’exécution de requêtes SQL, et de récupérer les résultats de ces dernières dans un objet de type ResultSet.Pour obtenir un objet de type Statement, il faut appeler la méthode createStatement() de l'objet Connection précédemment créé. La classe Statement possède les méthodes executeQuery(String SQL) pour effectuer des requêtes SELECT, executeUpdate(String SQL) pour effectuer des requêtes UPDATE/INSERT et execute(String SQL) plus générale.

Travailler avec ResultSetResultSet représente les lignes retournées comme réponse à une requête. Une fois un objet de type ResultSet obtenu, il est possible de récupérer chaque champ pour chaque ligne. Les objets de type ResultSet sont toujours positionnés avant la première ligne (si elle existe) des données récupérées, il est alors nécessaire d'appeler ResultSet.next() et de tester si la valeur retournée est true (on pointe sur la ligne suivante), ou false (plus de lignes).

267/286

Page 268: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Annexe 4 - Travailler avec une base de données MySQL

Exemple : Utiliser Statement et ResultSet ... try { // Utilisation de la connexion précédemment créée Statement stmt = conn.createStatement(); ResultSet rs= stmt.executeQuery("SELECT Nom FROM Etudiants");

while ( rs.next() ) { System.out.println(rs.getString(1)); // ou mieux, récupérez-les dans une Jtable de SWING! }

// fermeture des accès à la base rs.close(); stmt.close(); conn.close(); } catch (SQLException e) { System.out.println("SQLException: " + e.getMessage()); System.out.println("SQLState: " + e.getSQLState()); System.out.println("VendorError: " + e.getErrorCode()); } ...

Travailler avec PreparedStatementPreparedStatement hérite de la classe Statement. Seule une étude de la documentation Java permettra de faire le tour des méthodes intéressantes de la classe.

2.3. Utiliser les fonctionnalités propres à MySQL

MySQL possède quelques fonctionnalités qui ne se retrouvent pas dans l'API standard de JDBC. Pour y accéder, il est nécessaire de transtyper les objets de type Statement ou PreparedStatement que vous utilisez, par :

org.gjt.mm.mysql.Statement ou org.gjt.mm.mysql.PreparedStatement.

Il est possible d'appeler les méthodes getLastInsertID() pour récupérer la valeur générée pour chaque champ AUTO_INCREMENT, et getLongUpdateCount() pour récupérer le plus grand nombre pour la mise à jour, exprimé avec un entier long.

268/286

Page 269: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Glossaire

GLOSSAIRE

API Application Programming Interface. Une API contient un ensemble de fonctions qui facilitent la programmation. L'API de java contient entre autres les paquetages java.applet, java.awt (AWT pour Abstract Window Toolkit), java.awt.image, java.io, java.lang, java.net et java.util.

ATTRIBUTS ET MÉTHODES DE CLASSE Un attribut ou une méthode d'une classe sont dits de classe lorsque le modificateur static est appliqué lors de leur déclaration. On dira aussi qu'il s'agit d'un attribut ou d'une méthode statiques. Un attribut déclaré static existe dès que sa classe est évoquée, en dehors et indépendamment de toute instanciation. Quel que soit le nombre d'instanciations de la classe (0, 1, ou plus), un attribut de classe, i.e. statique, existe en un et un seul exemplaire. Un tel attribut sera utilisé un peu comme une variable globale d'un programme non objet. Une méthode, pour être de classe ne doit pas manipuler, directement ou indirectement, des attributs non statiques de sa classe. En conséquence, une méthode de classe ne peut utiliser directement aucun attribut ni aucune méthode non statiques de sa classe ; une erreur serait détectée à la compilation. De l'extérieur d'une classe ou d'une classe héritée, un attribut ou une méthode de classe pourront être utilisés précédés du nom de leur classe (nomClasse.nomAttributOuMethode). Par exemple, pour utiliser la méthode de classe sin() de la classe java.lang.Math avec un argument x, on écrira Math.sin(x).

ATTRIBUTS ET MÉTHODES D'INSTANCE Un attribut ou une méthode d'une classe sont dits d'instance dès que le modificateur static n'est pas appliqué lors de leur déclaration. Un attribut d'instance n'existe par rapport à une allocation mémoire que dans une instance de cette classe. Avant qu'une instanciation de la classe ne soit effectuée, l'attribut n'a aucune existence physique. Si plusieurs instances de la même classe coexistent, il y aura un exemplaire d'un certain attribut d'instance de la classe par instance créée. Une méthode doit être d'instance lorsqu'elle manipule, en lecture ou en écriture, des attributs d'instance de sa classe. À l'envers, il sera plus correct de déclarer en static une méthode n'utilisant aucun attribut ou méthode non statiques de sa classe. Lorsqu'une méthode d'instance est invoquée, la référence (qui sera appelée this durant son déroulement) de l'instance traitée lui est passée implicitement. Un attribut ou une méthode d'instance ne pourront être invoqués, à l'extérieur de la définition de leur classe ou d'une classe héritée, qu'à partir d'une instance de classe (nomInstance.nomAttributOuMethode).

AWTAbstract Window Toolkit. C'est le nom du paquetage de java traitant de ce qui est nécessaire pour gérer des fenêtres, des interfaces graphiques, des opérations de dessin.

CHAMP Un champ d'une classe est un attribut ou une méthode de cette classe.

269/286

Page 270: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Glossaire

CLASSE Une classe est la description d'un ensemble d'attributs et de méthodes chargées de gérer ces attributs. Une classe comporte : des attributs et des méthodes d'instance des attributs et des méthodes de classe

CONSTRUCTEUR Toute classe contient au moins une méthode appelée constructeur. Cette méthode porte le même nom que la classe qui la contient et ne doit pas indiquer de valeur de retour ; l'objet retourné est implicitement une instance de la classe. Le constructeur sert à initialiser les attributs de la classe. La première ligne d'un constructeur est un appel au constructeur de la super-classe. Lorsque le programmeur n'a pas défini de constructeur, le compilateur en ajoute automatiquement un qui ne comporte qu'une seule instruction :

super();

Cette instruction est un appel à un constructeur sans paramètre de la super-classe. Si la super-classe n'a pas de constructeur sans paramètre, il y aura une erreur détectée à la compilation. Voici un exemple d'une classe possédant deux constructeurs.

class Essai { int milieu; int largeur;

Essai(int i) { milieu = i; } Essai(int a, int b) { this(a); //effectuera : milieu=a; largeur = b; }}

ENCAPSULATION On parlera de l'encapsulation de données. Il s'agit d'un principe fondamental d'un langage objet : la possibilité de masquer des données à l'intérieur d'une classe en ne permettant de les manipuler qu'au travers des méthodes de la classe. L'intérêt essentiel d'une telle opération est d'interdire que les données soient modifiées de façon contraire à l'usage attendu dans sa classe, et ainsi de maintenir l'ensemble des données de la classe dans un état cohérent. Un exemple simple est le cas d'une donnée numérique qui devrait rester positive pour garder sa signification : il est indispensable qu'un utilisateur ne puisse pas, par mégarde ou ignorance, lui attribuer une valeur négative. Pour encapsuler une donnée, il suffit de la déclarer privée, c'est-à-dire de lui attribuer le modificateur de visibilité private.

EXCEPTION Une exception correspond à un événement anormal ou inattendu. Les exceptions sont des instances de sous-classes des classes java.lang.Error (pour des erreurs graves, qui devront généralement conduire à l'arrêt du programme) et java.lang.Exception (pour des événements inattendus, qui seront souvent traités sans que cela provoque l'arrêt du programme). Un grand nombre d'exceptions sont définis dans l'API. Un mécanisme, utilisant le mot réservé throw, permet de « lancer une exception » :

if (ilYAUnProbleme) throw new MonException)();

270/286

Page 271: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Glossaire

où MonException serait ici une sous classe de java.lang.Exception définie par ailleurs. Quand une exception est lancée, toutes les instructions suivantes sont ignorées et on remonte la pile des appels des méthodes :on dit que l'exception se propage. Si, soit l'instruction qui a lancé l'exception, soit une méthode de la pile des appels, est situé dans un "bloc try" :

• suivi d'un "bloc finally", les instructions du "bloc finally" sont exécutées et la propagation se poursuit ;

• suivi d'un "bloc catch" attrapant les exceptions de la classe de l'exception qui se propage, les instructions du "bloc catch" sont exécutées puis l'exécution reprend son cours normal avec l'instruction qui suit le "bloc catch".

Remarquons qu'une méthode qui peut lancer une exception sans l'attraper durant son exécution doit le signaler dans son en-tête en utilisant le mot réservé throws. Par exemple :

void maFonction() throws MonException

signale qu'au cours de l'exécution de la méthode maFonction() une exception de type MonException peut être lancée sans être attrapée. On n'est néanmoins dispensé de signaler dans les en-têtes des méthodes le lancement éventuel des exceptions les plus classiques, comme une ArrayIndexOutOfBoundsException par exemple. Vous pouvez compter sur le compilateur pour vous dire si vous avez oublié de signaler qu'une méthode peut lancer une certaine exception. Beaucoup de méthode de l'API lancent des exceptions que l'on peut tenter d'attraper.

GESTIONNAIRE DE RÉPARTITION Un gestionnaire de répartition est une classe qui permet de gérer la répartition des sous-composants graphiques dans un composant graphique. La méthode setLayout() que l'on trouve dans la classe java.lang .Container dont hérite tout composant graphique qui peut en contenir d'autres, permet de choisir un gestionnaire de répartition. Vous pouvez utiliser :

• java.lang.BorderLayout : pour répartir les sous-composants au nord, au sud, à l'est, à l'ouest ou au centre du composant.

• java.lang.CardLayout : pour répartir un ensemble de sous-composants de telle sorte qu'un seul soit visible à la fois, comme s'il s'agissait d'un jeu de cartes.

• java.lang.FlowLayout : pour arranger les sous-composants en ligne, de la gauche vers la droite et du haut vers le bas au fur et à mesure de leurs insertions.

• java.lang.GridLayout : pour arranger les sous-composants dans une grille dont on peut éventuellement préciser le nombre de lignes et de colonnes.

• java.lang.GridBagLayout : pour arranger les sous-composants dans une grille, mais un sous-composant peut utiliser plusieurs lignes ou colonnes et on peut donner diverses informations sur la disposition des sous-composants.

Si on n'utilise pas de gestionnaire de répartition, il faut préciser directement les positions et les tailles des différents sous-composants graphiques. Des méthodes sont prévues à cet effet dans la classe java.awt.Component.

HÉRITAGE C'est un principe fondamental de la programmation objet. Les sous-classes d'une classe disposent de tous les attributs et méthodes de sa super-classe, moyennant néanmoins quelques nuances liées aux modificateurs de visibilité et au principe de la redéfinition des méthodes.

INSTANCE Une classe est une description abstraite d'un ensemble d'objets. Une instance de classe est un objet construit selon le modèle fourni par la classe. Un objet et une instance de classe peuvent être considérés comme synonyme.

271/286

Page 272: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Glossaire

MÉTHODE On appelle ainsi ce qu'on appellerait fonction ou procédure dans d'autres langages. Toute méthode fait partie d'une classe. Une méthode peut être soit d'instance, soit de classe.

MODIFICATEURS DE VISIBILITÉIl est possible d'indiquer pour une classe ou un champ (attribut ou méthode) d'une classe un certain degré d'accessibilité.Cela se fait avec les mots public, private ou protected situés au début de l'en-tête de la classe ou du champ en question. Ces mots sont les modificateurs de visibilité. Une classe possède deux degrés de visibilité : le degré par défaut et le degré publique indiqué par le mot réservé public. Une classe est toujours visible de tout son paquetage, une classe n'est visible d'un autre paquetage que si elle est publique. Un attribut ou une méthode possèdent quatre degrés de visibilité : les modes indiqués par les mots public, private ou protected auxquels s'ajoute le mode par défaut ; dans ce dernier cas, l'attribut ou la méthode en question sont visibles de partout à l'intérieur de son paquetage et de nulle part à l'extérieur de celui-ci.

OBJET On appelle ainsi une instance d'une classe.

POLYMORPHISME On dit d'un langage qui accepte la surcharge des méthodes qu'il permet le polymorphisme.

PRIVÉ Un attribut ou un méthode sont dits privés si on leur applique le modificateur de visibilité private. Exemples :

private int donnee;private boolean estRealisable(Graphe graphe) { // instructions....}

Un attribut ou une méthode privés ne sont pas visibles en-dehors de la définition de leur classe. Un attribut privé ne pourra donc pas être modifié par des instances de la classe qu'à travers des méthodes de la classe prévues à cet effet ; le programmeur de la classe peut ainsi contrôler le comportement des attributs privés (imaginer par exemple un attribut entier qui doit toujours être strictement positif pour qu'une instance de la classe se comporte correctement ; il serait dangereux qu'un tel attribut puisse être modifié par un utilisateur de la classe).

PROTÉGÉ On dit qu'un champ d'une classe (attribut ou méthode) est protégé si on lui a affecté le modificateur de visibilité protected. Un champ protégé d'une classe A est toujours visible à l'intérieur de son paquetage. A l'extérieur du paquetage de A, considérons une classe B qui hérite de A ; un champ protégé de A est hérité par B et visible directement dans B (au travers de la référence implicite this) et visible également au travers d'instances de B ou de sous-classes de B définies dans B ; il n'est pas visible dans d'autres conditions.

PROTOTYPEOn appelle ainsi la face externe d'une méthode, c'est-à-dire:

<typeDeRetour> nomMethode(liste d'arguments);

272/286

Page 273: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Glossaire

SUPER-CLASSE On appelle ainsi une classe qui est étendue par d'autres classes. Considérons une classe C. La super-classe de C est la classe dont hérite directement C. Cette super-classe peut être indiquée par le mot réservé extends au moment de la définition de la classe. Si ce n'est pas le cas, la classe C hérite directement de la classe java.lang.Object, qui est donc sa super-classe.

SURCHARGE Une classe peut définir plusieurs méthodes ayant un même nom du moment que les suites des types de paramètres de ces méthodes diffèrent (globalement, ou par leurs ordres). Une différence uniquement sur le type de la valeur retournée est interdite. Lorsqu'une méthode surchargée est invoquée, la différence sur les paramètres permet de déterminer la méthode à exécuter. Le fait que le langage utilise la surcharge s'énonce aussi : le langage permet le polymorphisme.

TYPES PRIMITIFSLes types primitifs sont :

• le type booléen boolean, qui n'est pas un type entier, et qui peut prendre les valeurs false et true

• le type caractère char • les types entiers byte, short, int et long • les types nombres à virgule flottante float et double

TYPE RÉFÉRENCE Toute variable qui n'est pas d'un type primitif est d'un type référence. Une référence est en fait une adresse en mémoire d'un objet.

273/286

Page 274: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Mots réservés du langage

MOTS RÉSERVÉS DU LANGAGE

abstract default goto operator synchronized

boolean do if outer this

break double implements package throw

byte else import private throws

byvalue extends inner protected transient

case false instanceof public true

cast final int rest try

catch finally interface return var

char float long short void

class for native static volatile

const future new super while

continue generic null switch

abstract Il s'agit d'un modificateur utilisé lors de la déclaration d'une classe ou d'une méthode. On dit alors que la classe ou la méthode est abstraite. Une méthode abstraite n'a que son prototype, c'est-à-dire son type de retour suivi, de son nom, suivi de la liste de ses paramètres entre des parenthèses, suivi d'un point-virgule. Une méthode abstraite ne peut pas être déclarée static ou private ou final. Dès qu'une classe contient une méthode abstraite, elle doit elle aussi être déclarée abstraite. Une classe abstraite ne peut pas être instanciée. Il faudra l'étendre et définir toutes les méthodes abstraites qu'elle contient pour pouvoir l'utiliser.

boolean Type de base booléen. Prend les valeurs true ou false. Exemple de déclaration d'une variable booléenne :

boolean estCoupable = false;

break L’instruction break permet de sortir d'une boucle en en stoppant immédiatement l'exécution.

byte Type de base défini sur 1 octet. Prend des valeurs comprises entre -128 à +127. Exemple de déclaration d'une variable de type byte :

byte unOctet = 45;

274/286

Page 275: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Mots réservés du langage

byvalue Mot non utilisé actuellement.

case (Voir switch).

catch Cette clause conduit à attraper une exception. On appelle "bloc catch" la clause catch et le bloc d'instructions qui la suit. La syntaxe est du type :

try{ // instructions effectuées; // instruction pouvant lancer une exception MonException; // instructions effectuées si l'exception n'a pas été lancée;}catch(MonException e){ // instructions effectuées si une instance de MonException, a été // lancée et donc attrapée;}

Un "bloc catch" suit nécessairement un "bloc try". Si un "bloc catch" attrape une exception qui a été lancée, le déroulement du programme se poursuit avec l'instruction qui suit le "bloc catch".

char Type de base caractère défini sur 2 octets. Prend des valeurs définies en codification Unicode. Exemple de déclaration d'une variable de type char :

char unCaractere = 'a';

class

const Mot non utilisé actuellement.

continue L’instruction continue redémarre immédiatement l'exécution de la boucle à sa prochaine itération.

default (Voir switch).

do

275/286

Page 276: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Mots réservés du langage

double Type de base réel à double précision, défini sur 8 octets. Prend les valeurs virgule flottante IEEE 754-1985. Exemple de déclaration d'une variable double :

double longueurTrait = 3.5e6;

else (Voir if)

extends Ce mot réservé peut être utilisé suivi d'un nom de classe lors de la définition d'une nouvelle classe. Exemple :

class MaNouvelleClasse extends ClasseGenerale { // ... }

MaNouvelleClasse est alors dite sous-classe de ClasseGenerale. On dit aussi que MaNouvelleClasse étend ClasseGenerale. MaNouvelleClasse hérite de toutes les méthodes de ClasseGenerale qui sont visibles (voir les modificateurs de visibilité). Une classe étend au plus une autre classe.

Une interface peut aussi étendre (grâce à extends) une autre ou plusieurs autres interfaces. Exemple :

interface Dessinable extends Coloriable, Tracable { // ... }

Dessinable hérite de toutes les constantes et méthodes abstraites contenues dans Coloriable et Tracable.

false Valeur booléenne FAUX. Voir boolean.

final Il s'agit d'un modificateur qui s'applique à une classe, une donnée ou une méthode. Pour une classe, final indique que la classe ne peut pas être étendue. Pour une méthode, final indique que la méthode ne peut pas être redéfinie. Une méthode qui est déclarée static ou private est automatiquement final. Pour une donnée, final indique qu'il s'agit d'une constante, d'instance s'il n'y a pas simultanément le modificateur static, et de classe si la donnée est final static. Une donnée final ne pourra être affectée qu'une seule fois.

finally La clause précède un bloc d'instructions. La clause et le bloc d'instructions constituent ce qu'on appelle un "bloc finally". Un "bloc finally" est en général utilisé pour effectuer des nettoyages (fermer des fichiers, libérer

276/286

Page 277: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Mots réservés du langage

des ressources...). Il suit : soit un "bloc try" soit un "bloc try" suivi d'un "bloc catch"

Dans les deux cas, quelque soit la façon dont on est sorti du "bloc try" (par une instruction break, ou continue, ou return, par une propagation d'exceptions, ou bien normalement), les instructions contenues dans le "bloc finally" sont exécutées. Si un "bloc catch" situé entre le "bloc try" et le "bloc finally" attrape une exception, les instructions du "bloc catch" sont faites avant celles du "bloc finally".

float Type de base réel à simple précision, défini sur 4 octets. Prend les valeurs simple précision IEEE 754-1985. Exemple de déclaration d'une variable float :

float longueurTrait = 22.53;

for Sa syntaxe est :

for (initialisation; condition; incrémentation) { // blocInstructions }

Chacun des trois champs peut être vide. Le premier champ constitue la (ou les) initialisation(s), séparé(es) par des virgules. La variable d'itération peut-être déclarée à cet endroit, sa portée sera limitée au bloc for. Le deuxième champ est la condition d'arrêt de l'itération. Le troisième champ est généralement une incrémentation ou une modification de l'un des composants de la condition d'arrêt. Exemple :

for (int i = 0; i ! 10; i++) { System.out.println("Bonjour");}

Exemple plus complexe :

for (int i = 1, j = i+10; i ! 5; i++, j = i*2) { System.out.println("i = " + i + " j = " + j); } /* Résultat : i = 1 j = 11 i = 2 j = 4 i = 3 j = 6 i = 4 j = 8 */

future Ce mot n'est pas utilisé actuellement.

277/286

Page 278: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Mots réservés du langage

generic Ce mot n'est pas utilisé actuellement.

goto Ce mot n'est pas utilisé actuellement et ne devrait jamais l'être.

if Permet un branchement conditionnel. Sa syntaxe :

if (expressionBooléenneVraie) // blocInstructions1 [else // blocInstructions2]

Les crochets signifient que la partie else est optionnelle. Les blocs d'instructions peuvent n'être constitués que d'une seule instruction (terminée par un point-virgule). Contrairement au C, le résultat de l'expression de condition est forcément de type booléen. Exemple :

if (couleur == blanc) { couleur = noir ;} else { couleur = blanc; System.out.println("C'est aux blancs de jouer.");}

implements Ce mot est employé dans l'en-tête de la déclaration d'une classe, suivi d'un nom d'interface ou de plusieurs noms d'interfaces séparées par des virgules. S'il y a une clause extends, la clause implements doit se trouver après la clause extends. Exemple :

class MaClasse extends AutreClasse implements UneInterface, UneAutreInterface

Si une classe non abstraite possède une clause implements, elle doit définir toutes les méthodes de l'interface indiquée.

importcette instruction permet d'utiliser une ou plusieurs classes d'un paquetage. On peut employer l'instruction import sous deux formes :Premier exemple :

import coursJava/monPaquet/DansPaquetage;

permet d'utiliser la classe DansPaquetage sans rappeler le nom complet. Autrement dit, lorsque l'on aurait écrit sans l'instruction import :

278/286

Page 279: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Mots réservés du langage

coursJava.monPaquet.DansPaquetage uneInstance = new coursJava.monPaquet.DansPaquetage();

pourra être écrit avec l'instruction import : DansPaquetage uneInstance = new DansPaquetage();

Deuxième exemple : import coursJava/monPaquet/*;

permet d'utiliser le nom de toute classe faisant partie du paquetage coursJava.monPaquet sans en rappeler le nom complet.

inner Ce mot n'est pas vraiment utilisé actuellement mais le sera sans doute bientôt dans le contexte de définition d'une classe interne.

instanceof C'est un opérateur qui prend à sa gauche une référence ref d'objet et à sa droite un nom C de classe ou un nom I d'interface. Cet opérateur retourne true si l'objet référencé appartient à une classe ou une sous-classe de la classe C ou bien à une classe qui implémente l'interface I. Il retourne false dans le cas contraire, et aussi si ref vaut null.

int Type de base entier, défini sur 4 octets. Prend les valeurs comprises entre -2 147 483 648 et 2 147 483 647. Exemple de déclaration d'une variable int :

int compteur = 0x234;

interface Ce mot débute la définition d'une interface. Une interface sert essentiellement à :• regrouper des constantes • permettre qu'un ensemble de classes, étendant éventuellement d'autres classes, puissent

implémenter une même interface. Cela ne serait pas utile si Java permettait l'héritage multiple. Un nom d'interface peut être utilisé comme un nom de classe. On pourra donc mettre dans une variable dont le type est une interface n'importe quel objet implémentant l'interface en question. Quel que soit l'objet référencé par la variable, on pourra lui appliquer une méthode déclarée dans l'interface.

Une interface peut contenir des attributs qui doivent être initialisés par des expressions constantes. Ces attributs sont automatiquement final et static, et sont donc des constantes. Une interface peut contenir des prototypes de méthodes. Celles-ci sont automatiquement publiques. Une interface ne contient pas de constructeur.

long Type de base entier long, défini sur 8 octets. Prend les valeurs comprises entre -9 223 372 036 854

279/286

Page 280: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Mots réservés du langage

775 808 à 9 223 372 036 854 775 807. Exemple de déclaration d'une variable long : long compteurDeBulles = 20E5;

native Ce modificateur est appliqué à la déclaration d'une méthode. Il indique que la méthode est implémentée en langage C, ou d'une façon dépendante de la plate-forme. Une méthode native est déclarée mais non définie, ce qui signifie qu'elle n'a pas de corps, mais un point virgule à la fin de sa ligne déclarative.

new Il s'agit d'un opérateur unaire qui crée un nouvel objet ou un nouveau tableau. Pour créer un nouvel objet, le mot new doit être suivi d'un constructeur. Exemple :

MaClasse obj; //... obj = new MaClasse(5, "bonjour");

si toutefois la classe MaClasse a un constructeur qui possède deux paramètres, un entier et une chaîne de caractères.

Pour créer un nouveau tableau, on peut par exemple écrire : String[] arguments = new String[10];

ou bien : int[][] uneMatrice = new int[4][5];

ou encore : int[][] quatreLignes = new int[4][];

null

operator Ce mot n'est pas utilisé actuellement.

outer Ce mot n'est pas utilisé actuellement.

package

private Ce mot réservé est un modificateur de visibilité qui s'applique à un champ (attribut ou méthode) d'une classe. On dit alors que le champ est privé. Un champ privé n'est visible que depuis sa propre classe. Elle n'est visible nulle part ailleurs et en particulier pas dans les sous-classes.

280/286

Page 281: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Mots réservés du langage

protected Ce mot réservé est un modificateur de visibilité qui s'applique à un champ (attribut ou méthode) d'une classe. On dit alors que le champ est protégé. Un champ protégé d'une classe A est toujours visible à l'intérieur de son paquetage. A l'extérieur de son paquetage, un champ protégé est hérité par une sous-classe B mais non visible au travers d'une instance de A ou de B (sauf pour une instance de B invoquée dans B ou dans une sous-classe de B).

public Ce mot réservé est un modificateur de visibilité qui s'applique à une classe, une interface ou à un champ (attribut ou méthode) d'une classe. On dit alors que la classe, l'interface ou le champ est publique. Une classe ou une interface publique est visible de partout, y compris les autres paquetages. Si ce modificateur n'est pas appliqué à une classe ou une interface, celle-ci n'est visible que de l'intérieur de son paquetage. Un champ publique est visible de partout du moment que sa classe est visible.

rest Ce mot n'est pas utilisé actuellement.

return

short Type de base entier court, défini sur 2 octets. Prend les valeurs comprises entre -32 768 à 32 767. Exemple de déclaration d'une variable short :

short compteur = 2;

static Ce modificateur s'applique aux attributs et aux méthodes ; lorsque ce modificateur est utilisé, on dit qu'il s'agit d'un attribut ou d'une méthode dits de classe. On rappelle qu'une méthode ou un attribut auxquels n'est pas appliqué le modificateur static sont dits d'instance.– Un attribut déclaré static existe dès que sa classe est évoquée, en dehors et indépendamment

de toute instanciation.– Quelque soit le nombre d'instanciations de la classe (0, 1, ou plus) un attribut de classe, i.e.

statique, existe en un et un seul exemplaire. Un tel attribut sera utilisé un peu comme une variable globale d'un programme non objet.

– Une méthode, pour être de classe (i.e. static) ne doit pas manipuler, directement ou indirectement, des attributs non statiques de sa classe. En conséquence, une méthode de classe ne peut utiliser directement dans son code aucun attribut ou aucune méthode non statique de sa classe ; une erreur serait alors détectée à la compilation. Autrement dit, une méthode qui utilise (en lecture ou en en écriture) des attributs d'instance ne peut être statique, et est donc nécessairement une méthode d'instance.

– De l'extérieur d'une classe ou d'une classe héritée, un attribut ou une méthode de classe pourront être utilisés, précédés du nom de leur classe : NomClasse.NomDeLaDonnéeOuMethodeDeClasse

281/286

Page 282: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Mots réservés du langage

ou bien du nom d'une instance de la classe. – Signalons enfin qu'une méthode static ne peut pas être redéfinie, ce qui signifie qu'elle est

automatiquement final.

super Ce mot réservé permet d'invoquer un champ (attribut ou méthode) de la super-classe de l'objet sur lequel on est en train de travailler (objet courant), et qui serait sinon caché par un champ de même nom de la classe de l'objet (soit que le champ en question soit une méthode qui a été redéfinie, soit que le champ soit un attribut qui a été masqué). Lorsque, dans une méthode, on invoque un champ de l'objet courant, on ne précise en général pas le nom de l'objet, ce que l'on peut néanmoins faire avec le mot this. Lorsqu'on emploie super, ce mot vient remplacer this qui figure implicitement. Employer super revient à remplacer la classe de l'objet courant par la super-classe. Remarquons qu'il est interdit de remonter de plusieurs étages en écrivant super.super.etc. On utilise aussi super(paramètres) en première ligne d'un constructeur. On invoque ainsi le constructeur de la super-classe ayant les paramètres correspondants.

switch Ce mot réservé permet de réaliser un branchement conditionnel. Il remplace l'imbrication de if. Sa syntaxe est :

switch (elementDeChoix) { case valeur1 : // blocInstructions1 break; case valeur2 : // blocInstructions2 break; ... [default : // blocInstructionsDefaut ]}

Le switch sélectionne le bloc d'instructions à exécuter dont la valeur du cas (case) est celle de son élément de choix. Si aucune correspondance n'est trouvée, le bloc d'instructions par défaut s’exécute. Remarquons que le code associé à chaque cas se termine par un break. Ceci dit, ce break est optionnel. Il indique que la dissociation de cas est terminée et qu'il est inutile de continuer à rechercher un cas correspondant à l'élément de choix. Vous pouvez l'omettre et l'exécution du code se poursuivra jusqu'au prochain break (ou jusqu'à la fin du switch). Il est également possible d'associer deux cas à l'exécution d'un bloc. Observons l'exemple suivant :

if (operateur == '+') ajouter(a, b); else if (operateur == '-') soustraire(a, b); else if ((operateur == '*') || (operateur == 'X')) multiplier(a, b);

peut s'écrire :

282/286

Page 283: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Mots réservés du langage

switch (operateur) { case '+' : ajouter(a, b); break; case '-' : soustraire(a, b); break; case '*' : case 'X' : multiplier(a, b); break; }

synchronized Cela peut être un modificateur pour une méthode ou bien une instruction. Lorsqu'une méthode synchronized est invoquée, celle-ci ne pourra s'exécuter que lorsqu'elle aura obtenu un verrou sur l'instance à laquelle elle s'applique ; elle gardera alors le verrou jusqu'à ce qu'elle soit totalement exécutée. L'instruction synchronized est utilisée de la manière suivante :

synchronized(identificateur){ // instruction1; // instruction2; // ...}

L'identificateur identificateur situé entre les parenthèses doit être un objet ou un tableau. Le bloc suivant l'instruction synchronized s'appelle section critique. Pour exécuter la section critique, le bloc doit obtenir un verrou sur l'objet ou le tableau représenté par identificateur.

this On peut utiliser this comme un objet ou bien this(paramètres) comme une méthode. Dans un constructeur ou une méthode d'instance, l'objet représenté par this est l'objet avec lequel on est en train de travailler. this est généralement utilisé pour accéder à un attribut d'instance masqué par un argument de la méthode ou une donnée locale. On peut aussi utiliser this pour passer l'objet sur lequel on est en train de travailler en paramètre à une méthode. Invoquer une méthode this(paramètres) ne peut se faire qu'en première ligne d'un constructeur. On invoque alors un autre constructeur dont la liste de paramètres correspond aux classes des paramètres indiqués dans la parenthèse. Exemple :

class Cercle { int x,y, rayon;

Cercle(int x, int y, int rayon) { this.x = x; this.y = y; this.rayon = rayon; }

Cercle(int x, int y) { this(x, y, 10); }}

throw C'est ce mot réservé qui permet de « lancer » une exception lorsqu'un événement exceptionnel s'est produit. On écrira par exemple :

283/286

Page 284: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Mots réservés du langage

if (ilYAUnProbleme) throw new MonException)();

où MonException serait ici une sous-classe de java.lang.Exception définie par ailleurs. Remarquons qu'on lance une instance d'une exception (présence du mot new). Quand une exception est lancée, toutes les instructions suivantes sont ignorées et on remonte la pile des appels des méthodes : on dit que l'exception se « propage ». Pendant la propagation si une méthode de la pile d'appel a été invoquée à l'intérieur d'un bloc try :

• suivi d'un bloc finally, les instructions du bloc finally sont exécutées et la propagation se poursuit ;

• suivi d'un bloc catch attrapant les exceptions de la classe de l'exception qui se propage, les instructions du bloc catch sont exécutées puis l'exécution reprend son cours normal avec l'instruction qui suit le bloc finally.

throws Lorsqu'une méthode contient une instruction (éventuellement à travers l'appel à une autre méthode) susceptible de lancer une certaine exception sans l'attraper (par un mécanisme try / catch), elle doit l'annoncer dans son en-tête. Exemple :

void maMethode throws NumberFormatException, monException{ // ... // instruction pouvant lancer une erreur de la classe // NumberFormatException ou monException; // ...}

transient Il s'agit d'un modificateur de visibilité applicable aux variables attributs d'instance d'une classe. Il désigne un attribut ne jouant pas de rôle dans la description de l'objet (on dira aussi qu'il ne fait pas partie de l'état persistant de l'objet) et qu'il n'est donc pas nécessaire de le sauvegarder lors d'une sérialisation.

true Valeur booléenne VRAI. Voir boolean.

try Ce mot doit être suivi d'un bloc d'instructions. On parlera alors d'un bloc try. En soit, il ne fait rien d'autre que de permettre l'utilisation de blocs catch ou/et finally qui peuvent éventuellement suivre le bloc try.

var Ce mot n'est pas utilisé actuellement.

284/286

Page 285: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Mots réservés du langage

void

volatilece mot est un modificateur applicable aux attributs. Il signifie qu'il est possible qu'il y ait changement asynchrone de l'attribut. Le compilateur ne peut pas effectuer d'optimisation sur cet attribut.

while

285/286

Page 286: Programmation Java - École nationale supérieure d ...alebret/_data/teaching/programmation/java/… · Le présent document est mis à disposition sous un contrat Creative Commons

Bibliographie

BIBLIOGRAPHIE

[1] CHAN, Patrick. Le dictionnaire officiel Java 2. Éditions Eyrolles, 2000, 871 p., ISBN 2212090897. Une des références Java.

[2] CHARON, Irène. Le langage Java : concepts et pratique, 3ème édition. Éditions Hermès, 2006, 473 p., ISBN 2746212129. Une référence Java malgré un prix un peu trop élevé.

[3] DEITEL, Harvey, DEITEL Paul. Comment programmer en Java, 4ème édition. Éditions Reynald Goulet, 2002, 1546 p., ISBN 2893772544. Un (trop imposant) ouvrage couvrant une bonne partie des applications réalisables en Langage Java. Un excellent chapitre 22 sur les files, piles, arbres.

[4] FORTIN, André. Analyse numérique pour ingénieurs, 2ème édition. Éditions Presses Internationales Polytechnique, 2001, 497 p., ISBN 2553009364.

[5] GRANET, Vincent. Algorithmique et programmation en Java : Cours et exercices corrigés, 2ème édition. Éditions Dunod, 2004, 388 p., ISBN 210048463X.

[6] MULLER, Pierre-Alain, GAERTNER, Nathalie. Modélisation objet avec UML, 2ème édition. Éditions Eyrolles, 2003, 514 p., ISBN 2212113978. Un ouvrage assez complet sur UML, avec à mon sens quelques bémols en ce qui concerne l'implémentation en C++ ou Java.

[7] PUYBARET, Emmanuel. Les cahiers du programmeur : Java 1.4 et 5.0, 3ème édition. Éditions Eyrolles, 2006, 367 p., ISBN 221211916X. Un ouvrage intéressant.

[8] RUMBAUGH, James, et al.. OMT, tome 1 : Modélisation et conception orientées objet. Éditions Dunod, 1997, 515 p., ISBN 2225846847. A mon avis, le meilleur ouvrage sur la modélisation et la conception par objets. Bien qu'il traite d'OMT, tous les concepts importants sont présents et agrémentés de très nombreux exercices.

[9] SEDGEWICK, Robert. Algorithmes en Java, 3ème édition. Éditions Pearson Education, 2004, 800 p., ISBN 2744070246. Existe aussi pour les langages C et C++. A posséder.

286/286