Vous êtes sur la page 1sur 303

Contents

1 Programmation Procédurale en Java 5


1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.1 Propriétés et origines . . . . . . . . . . . . . . . . . . . . . 5
1.1.2 JDK, JRE, et JVM . . . . . . . . . . . . . . . . . . . . . . 6
1.1.3 IDE (Integrated Development Environment) ou EDI (En-
vironnement de Développement Intégré) . . . . . . . . . . 7
1.1.4 Environnements de développement Java . . . . . . . . . . 7
1.1.5 Méthodes de traitement . . . . . . . . . . . . . . . . . . . 7
1.2 Types, variables et opérateurs . . . . . . . . . . . . . . . . . . . . 8
1.2.1 types primitifs . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.2 Entiers et bases . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.3 Commentaires : . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.4 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.5 Opérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.6 Type Casting . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.3 Les instructions conditionnelles . . . . . . . . . . . . . . . . . . . 12
1.3.1 If, else if, else . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3.2 Switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3.3 Expression ternaire . . . . . . . . . . . . . . . . . . . . . . 13
1.4 Les boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4.1 La boucle while . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4.2 La boucle do while . . . . . . . . . . . . . . . . . . . . . . 13
1.4.3 Les boucles for . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4.4 Instructions de rupture des boucles : . . . . . . . . . . . . 13
1.5 Les méthodes de classe (fonctions) . . . . . . . . . . . . . . . . . 14
1.6 Les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.7 TP - Conversion Celsius − Fahrenheit . . . . . . . . . . . . . . . 15

2 Programmation Orientée Objet en Java 17


2.1 Quelques classes utilitaires . . . . . . . . . . . . . . . . . . . . . . 17
2.1.1 La classe String . . . . . . . . . . . . . . . . . . . . . . . 17
2.1.2 La classe Math . . . . . . . . . . . . . . . . . . . . . . . . 18
2.1.3 La classe Random . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Notions de base pour la POO en Java . . . . . . . . . . . . . . . 22
2.2.1 Classes et Objets . . . . . . . . . . . . . . . . . . . . . . . 22
2.2.2 Attributs et Méthodes . . . . . . . . . . . . . . . . . . . . 22
2.2.3 Constructeurs et destructeur . . . . . . . . . . . . . . . . 25
2.2.4 Encapsulation et accesseurs . . . . . . . . . . . . . . . . . 27
2.3 Héritage et polymorphisme . . . . . . . . . . . . . . . . . . . . . 28
2.3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.3.2 Généralisation, Spécialisation et Héritage . . . . . . . . . 28
2.3.3 Modèles d’héritage et d’instanciation . . . . . . . . . . . . 30
2.3.4 Héritage en Java . . . . . . . . . . . . . . . . . . . . . . . 32
2.3.5 Polymorphisme en Java . . . . . . . . . . . . . . . . . . . 35

1
2.4 Les packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
2.4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 41
2.4.2 Packages en Java . . . . . . . . . . . . . . . . . . . . . . . 42
2.5 Les classes abstraites et interfaces . . . . . . . . . . . . . . . . . . 43
2.5.1 Classes et méthodes abstraites . . . . . . . . . . . . . . . 43
2.5.2 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.5.3 Exemple d’utilisation des interfaces . . . . . . . . . . . . . 46
2.6 Spécialisation naturelle et substituabilité de Liskov . . . . . . . . 50
2.6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.6.2 Spécialisation naturelle . . . . . . . . . . . . . . . . . . . 51
2.6.3 Principe de Substituabilité (ou Principe de Liskov) . . . . 51
2.7 Les assertions et les exceptions . . . . . . . . . . . . . . . . . . . 53
2.7.1 Conception par contrats . . . . . . . . . . . . . . . . . . . 53
2.7.2 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.7.3 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . 55
2.7.4 Bonnes pratiques . . . . . . . . . . . . . . . . . . . . . . . 61
2.8 Les énumérations . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
2.8.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 61
2.8.2 Énumérations en Java (Depuis Java 5) . . . . . . . . . . . 61
2.8.3 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
2.9 Les annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
2.9.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 63
2.9.2 Définition d’une annotation . . . . . . . . . . . . . . . . . 64
2.9.3 Composition d’annotations . . . . . . . . . . . . . . . . . 64
2.9.4 Annoter un élément de code . . . . . . . . . . . . . . . . . 65
2.9.5 Annotations de l’API Java . . . . . . . . . . . . . . . . . 66
2.9.6 Méta-annotations ou Annotations d’annotations . . . . . 67
2.9.7 Javadoc . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
2.10 Les Collections d’objets . . . . . . . . . . . . . . . . . . . . . . . 71
2.10.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 71
2.10.2 L’interface Collection<E> . . . . . . . . . . . . . . . . . 73
2.10.3 L’interface List . . . . . . . . . . . . . . . . . . . . . . . 73
2.10.4 L’interface Map . . . . . . . . . . . . . . . . . . . . . . . . 75
2.10.5 L’interface Set . . . . . . . . . . . . . . . . . . . . . . . . 78
2.10.6 Associations UML en Java . . . . . . . . . . . . . . . . . . 79
2.11 Les itérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
2.11.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 79
2.11.2 Les itérateurs et itérables de l’API Java . . . . . . . . . . 80
2.11.3 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
2.12 Tri, comparaison, et clonage d’objets . . . . . . . . . . . . . . . . 88
2.12.1 Tri et Comparaison . . . . . . . . . . . . . . . . . . . . . . 88
2.12.2 Clonage . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
2.13 La généricité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
2.13.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 90
2.13.2 Paramétrage des propriétés d’une classe générique . . . . 92
2.13.3 Généricité, héritage et instanciation . . . . . . . . . . . . 92

2
2.13.4 Généricité et sous-typage . . . . . . . . . . . . . . . . . . 93
2.13.5 Généricité bornée . . . . . . . . . . . . . . . . . . . . . . . 93
2.13.6 Le principe de l’effacement de type . . . . . . . . . . . . . 97
2.13.7 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
2.14 Les flux d’I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
2.14.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 102
2.14.2 Le package java.io . . . . . . . . . . . . . . . . . . . . . 105
2.14.3 Le package java.nio . . . . . . . . . . . . . . . . . . . . 126
2.14.4 Le package java.nio.charset . . . . . . . . . . . . . . . 132
2.14.5 Le package java.nio.file . . . . . . . . . . . . . . . . . 133
2.15 Les classes imbriquées (nested classes) . . . . . . . . . . . . . . . 143
2.15.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 143
2.15.2 Les classes imbriquées statiques (nested static classes) . . 143
2.15.3 Les classes internes (internal classes) . . . . . . . . . . . . 146
2.15.4 Les classes locales (local classes) . . . . . . . . . . . . . . 149
2.15.5 Les classes anonymes (anonymous classes) . . . . . . . . . 151
2.16 Interfaces fonctionnelles, lambda expressions et références de
méthodes/constructeurs (Depuis Java 8) . . . . . . . . . . . . . . 153
2.16.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 153
2.16.2 Interfaces fonctionnelles . . . . . . . . . . . . . . . . . . . 154
2.16.3 Lambda expressions . . . . . . . . . . . . . . . . . . . . . 162
2.16.4 Références de méthodes ou de constructeurs . . . . . . . . 165
2.17 Streams et collectors (Depuis Java 8) . . . . . . . . . . . . . . . 166
2.17.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 166
2.17.2 La classe Optional<T> . . . . . . . . . . . . . . . . . . . . 168
2.17.3 L’interface Stream<T> . . . . . . . . . . . . . . . . . . . . 169
2.17.4 Création de streams . . . . . . . . . . . . . . . . . . . . . 174
2.18 La réflexivité (l’introspection) . . . . . . . . . . . . . . . . . . . . 187
2.18.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 187
2.18.2 La classe Class<T> . . . . . . . . . . . . . . . . . . . . . . 188
2.18.3 La classe AccessibleObject . . . . . . . . . . . . . . . . 190
2.18.4 La classe Executable . . . . . . . . . . . . . . . . . . . . 190
2.18.5 La classe Field<T> . . . . . . . . . . . . . . . . . . . . . . 190
2.18.6 La classe Method . . . . . . . . . . . . . . . . . . . . . . . 191
2.18.7 La classe Constructor<T> . . . . . . . . . . . . . . . . . . 192
2.18.8 L’interface AnnotatedElement . . . . . . . . . . . . . . . 192
2.18.9 Exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
2.19 Les dates (Nouvelle API depuis Java 8) . . . . . . . . . . . . . . 203
2.19.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 203
2.19.2 L’interface Temporal . . . . . . . . . . . . . . . . . . . . . 203
2.19.3 L’interface TemporalUnit . . . . . . . . . . . . . . . . . . 203
2.19.4 L’interface TemporalAdjuster . . . . . . . . . . . . . . . 204
2.19.5 L’énumération ChronoUnit . . . . . . . . . . . . . . . . . 204
2.19.6 L’énumération DayOfWeek . . . . . . . . . . . . . . . . . . 204
2.19.7 L’énumération Month . . . . . . . . . . . . . . . . . . . . . 205
2.19.8 L’interface ChronoLocalDate . . . . . . . . . . . . . . . . 205

3
2.19.9 L’interface ChronoLocalDateTime<D extends ChronoLocalDate>206
2.19.10 L’interface ChronoZonedDateTime<D extends ChronoLocalDate>206
2.19.11 La classe Instant . . . . . . . . . . . . . . . . . . . . . . 207
2.19.12 La classe LocalDate . . . . . . . . . . . . . . . . . . . . . 207
2.19.13 La classe LocalTime . . . . . . . . . . . . . . . . . . . . . 211
2.19.14 La classe LocalDateTime . . . . . . . . . . . . . . . . . . 215
2.19.15 La classe ZonedDateTime . . . . . . . . . . . . . . . . . . 221
2.19.16 La classe TemporalAdjusters . . . . . . . . . . . . . . . . 222
2.19.17 La classe Period . . . . . . . . . . . . . . . . . . . . . . . 222
2.19.18 La classe Duration . . . . . . . . . . . . . . . . . . . . . . 223
2.19.19 La classe ZoneId . . . . . . . . . . . . . . . . . . . . . . . 223
2.19.20 La classe ZoneRules . . . . . . . . . . . . . . . . . . . . . 224
2.19.21 Exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
2.20 JVM et la modularité (Depuis Java 9) . . . . . . . . . . . . . . . 230
2.20.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 230
2.20.2 Contraintes de manipulation des modules . . . . . . . . . 230
2.20.3 Syntaxe de définition d’un module . . . . . . . . . . . . . 231
2.20.4 CLI ajoutée par JDK pour les modules (Depuis Java 9) . 231

3 Tests en Java 232


3.1 JUnit et les tests unitaires . . . . . . . . . . . . . . . . . . . . . . 232
3.1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 232
3.1.2 Les bases . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
3.1.3 Approfondissements . . . . . . . . . . . . . . . . . . . . . 236
3.1.4 Tests paramétrés . . . . . . . . . . . . . . . . . . . . . . . 238
3.1.5 Suites de tests . . . . . . . . . . . . . . . . . . . . . . . . 240

4 Programmation multi-threadée en Java 246


4.1 Rappels sur le parallélisme et la programmation multi-processus 246
4.1.1 Terminologie de Base . . . . . . . . . . . . . . . . . . . . 246
4.1.2 Avantage et inconvénient du parallélisme . . . . . . . . . . 247
4.1.3 Problème de la programmation multi-processus et le be-
soin de la programmation multithreadée . . . . . . . . . . 247
4.2 Introduction à la programmation multi-threadée . . . . . . . . . 247
4.2.1 Processus légers (Threads) . . . . . . . . . . . . . . . . . . 247
4.2.2 Synchronisation de threads . . . . . . . . . . . . . . . . . 248
4.2.3 Verrous (Mutex) . . . . . . . . . . . . . . . . . . . . . . . 248
4.2.4 Variables conditionnelles . . . . . . . . . . . . . . . . . . . 249
4.3 L’interface Runnable . . . . . . . . . . . . . . . . . . . . . . . . . 252
4.4 La classe Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
4.4.1 Propriétés . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
4.4.2 Quelques méthodes . . . . . . . . . . . . . . . . . . . . . . 253
4.4.3 L’énumération Thread.State . . . . . . . . . . . . . . . . 256
4.4.4 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
4.5 Le mot-clé synchronized . . . . . . . . . . . . . . . . . . . . . . 258
4.6 La classe Runtime . . . . . . . . . . . . . . . . . . . . . . . . . . 259

4
4.7 Le pattern Fork/Join . . . . . . . . . . . . . . . . . . . . . . . . 259
4.7.1 Pré-requis . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
4.7.2 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
4.7.3 Processus . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
4.7.4 Implémentation en Java (Depuis Java 7) . . . . . . . . . . 261

5 Programmation Événementielle en Java 269


5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
5.1.1 Programmation procédurale vs. programmation événe-
mentielle . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
5.1.2 Programmation événementielle en Java . . . . . . . . . . . 270
5.2 Les composants graphiques . . . . . . . . . . . . . . . . . . . . . 271
5.2.1 Les couleurs, polices, points, et dimensions . . . . . . . . 271
5.2.2 Les classes de base . . . . . . . . . . . . . . . . . . . . . . 273
5.2.3 Les fenêtres Swing . . . . . . . . . . . . . . . . . . . . . . 280
5.2.4 Les contextes graphiques . . . . . . . . . . . . . . . . . . . 283
5.2.5 Les images . . . . . . . . . . . . . . . . . . . . . . . . . . 286
5.2.6 Les formes et les paintures . . . . . . . . . . . . . . . . . . 287
5.2.7 Les gestionnaires de disposition (Layout Managers) . . . . 287
5.2.8 Les boutons . . . . . . . . . . . . . . . . . . . . . . . . . . 298
5.2.9 Les événements et leurs écouteurs . . . . . . . . . . . . . 299
5.2.10 Les formulaires . . . . . . . . . . . . . . . . . . . . . . . . 301
5.2.11 Les menus et boîtes de dialogue . . . . . . . . . . . . . . . 303

1 Programmation Procédurale en Java

1.1 Introduction

1.1.1 Propriétés et origines

1. description : langage orienté objet à typage statique, faiblement


typé, et multiparadigme (objet, fonctionnel, etc.) ;
2. origines :
• développé par Sun Microsystems (rachetée par Oracle) ;
• 1991 : naissance du langage Oak ;
• 1995 : renommage du langage en Java et sortie publique.
3. syntaxe : héritée de C++ avec modifications ;
4. caractéristiques :
• plus simple d’usage que C++, par l’abstraction de la gestion de
mémoire en introduisant un ramasse-miettes (garbage collector)
;
• assurant la portabilité (cross-platform) ;
5. traitements :
• compilation : un compilateur Java (e.g. javac)

5
• interprétation : la plateforme d’exécution Java JRE (Java
Run Environment) contenant la machine virtuelle Java JVM
(Java Virtual Machine) ;
• bytecode : code intermédiaire et portable obtenu par la com-
pilation du code source Java (i.e. le même quelle que soit la
plateforme de développement du code source Java) ;
• JRE : plateforme d’exécution Java propre à chaque plate-
forme et peut être intégrée au sein d’un navigateur web pour
exécuter des Java applets.
6. applications développées en Java :
• applications GUI ou CLI (clients lourds)
• applications web (front-end applets ou back-end applications)
(clients légers)
• applications mobiles (smartphone, PDA, etc.)

Figure 1: “Le processus de création, compilation, et d’exécution de programmes


en Java”

1.1.2 JDK, JRE, et JVM

1. fait : un programme Java est utilisable sur n’importe quel OS


hébergeant une plateforme d’exécution Java JRE.
2. processus d’exécution : code source Java –compilation–> bytecode
–JVM–> instructions machine :
• code source Java : fichier .java contenant du code Java.

6
• bytecode : fichier .class contenant du code précompilé illisible
par les humains et déchiffré uniquement par la JVM.
3. JRE : plateforme d’exécution Java propre à chaque plateforme
et peut être intégrée au sein d’un navigateur web pour exécuter des
Java applets.
4. JDK (Java Development Kit) : le kit de développement Java
contenant le JRE et des outils de développement, de compilation et
de débogage.

1.1.3 IDE (Integrated Development Environment) ou EDI (Environ-


nement de Développement Intégré)

1. description : un environnement de développement Java intégre les


outils suivants :
• un éditeur de texte : pour l’écriture du code source Java ;
• des outils pour la compilation et le débogage ;
• JRE pour l’exécution
2. exemples : Eclipse, Netbeans, etc.
3. Eclipse :
• description : un IDE libre écrit en Java pour plusieurs langages
de programmation (C++, Java, PHP, etc.)
• architecture : framework centré autour de la notion de plugin,
telle que toutes les fonctionnalités sont développées en tant
que plugins.

1.1.4 Environnements de développement Java

1. J2SE (Java 2 Standard Edition) : clients lourds ;


2. J2EE (Java 2 Enterprise Edition) : clients légers ;
3. J2ME (Java 2 Micro Edition) : applications pour les appareils
portables (smartphones, PDA, etc.)

1.1.5 Méthodes de traitement

1. installation du JRE et d’un IDE qui se chargera de compiler le code


source en bytecode et déléguer l’interprétation de celui-ci par la JVM
du JRE.
2. installation du JDK et utilisation des commandes CLI suivantes :
• javac : compilation de code source Java en bytecode ;
• java : interprétation du bytecode compilé et génération d’un
fichier exécutable.

7
1.2 Types, variables et opérateurs

1.2.1 types primitifs

type description stockage valeurs remarques


byte un entier 1 octet -128 → 127 -
signé
short un entier 2 octets -32768 → -
signé 32767
int un entier 4 octets -2^31 → -
signé 2^31 - 1
long un entier 8 octets -2^63 → suffixé par
signé 2^63 - 1 ‘L’
float un flottant 4 octets - jusqu’à 8
(précision chiffres après
simple) la virgule et
suffixé par ‘f’
double un flottant 8 octets - jusqu’à 16
(précision chiffres après
double) la virgule
char un caractère 1 octet -2^7 → 2^7 - circonscrit
1 par des ”
boolean un booléen 1 bit true ou -
false

1.2.2 Entiers et bases

1. base décimale : cas par défaut ;


2. base binaire : expression entière préfixée par 0b ;
3. base octale : expression entière préfixée par 0 ;
4. base hexadécimale : expression entière préfixée par 0x.

1.2.3 Commentaires :

// commentaire monoligne
/*
commentaire
multi-ligne
*/

/**
commentaires

8
javadoc
**/

1.2.4 Variables

1. conventions de nommage :
• camelCase pour les types primitifs ;
• CamelCase pour les types complexes ;
• SNAKE_CASE pour les constantes ;
2. contraintes de nommage :
• sensitive à la casse ;
• que des caractères alphanumériques ;
• sans caractères spéciaux ou blancs.
3. syntaxes :
// déclaration
type nomVariable;

// affectation
nomVariable = valeur;

// déclaration et initialisation
type nomVariable = valeur;

// déclaration de plusieurs variables de même type


type nomVariable1, nomVariable2;

// déclaration et initialisation de plusieurs variables de même type


type nomVariable1 = valeur1, nomVariable2 = valeur2;

1.2.5 Opérateurs

1.2.5.1 Opérateurs arithmétiques

symbol description utilisation remarques


+ addition a+b -
- soustraction a-b -
multiplication a*b -
/ division (entière ou réelle) a/b déterminé par les types des arguments
% modulo a%b uniquement avec les entiers
++ incrémentation ++a ou a++ pré/post-incrémentation
– décrémentation –a ou a– pré/post-décrémentation

9
1.2.5.2 Opérateurs d’affectation

symbol description utilisation remarques


= affectation a =b -
+= addition et affectation raccourcie a += b équivaut a = a +b
-= soustraction et affectation raccourcie a -= b équivaut a = a -b
= multiplication et affectation raccourcie a *= b équivaut a = a *b
/= division et affectation raccourcie a /= b équivaut a = a /b
%= modulo et affectation raccourcie a %= b équivaut a = a %b

1.2.5.3 Égalité physique et égalité logique


1. égalité physique : deux références pointant sur le même objet.
2. égalité logique : deux références pointant sur deux objets ayant le même
contenu.
3. la méthode equals() de la classe java.lang.Object :
• description : méthode définie pour tout objet et utilisée pour tester
son égalité logique à un autre objet.
• syntaxe
public boolean equals(Object object);

1.2.5.3.1 Opérateurs de comparaison

symbol description utilisation remarques


== égalité physique a == b deux références sur le même objet
!= différence physique a != b deux références sur deux objets différents
< inférieur a <b -
<= inférieur ou égal à a <= b -
> supérieur a >b -
>= supérieur ou égal à a >= b -

1.2.5.4 Opérateurs logiques

symbol description utilisation remarques


&& et logique a && b -
|| ou logique a || b -
! non logique !a -

1.2.5.5 Opérateurs bit à bit (bitwise)

10
symbol description utilisation remarques
& et binaire a &b -
| ou binaire a |b -
^ ou exclusif binaire (xor) a ^b -
<< décalage de bit à gauche a << b décalage de b bits à gauche
>> décalage de bit à droite a >> b décalage de b bits à droite

1.2.5.6 Opérateur de concaténation de strings

symbol description utilisation remarques


+ concaténation de strings string1 + string2 -

1.2.5.7 Opérateur de formatage d’expressions numériques


1. symbole : _
2. avantage : séparer les chiffres d’une longue expression numérique,
difficile à lire, afin d’améliorer sa visibilité.
3. contraintes d’utilisation :
• le nombre de _ entre les chiffres n’est pas important ;
• pas de _ :
1. au début/à la fin de l’expression ;
2. avant/après un .
4. exemples : 100_000_000, 100____000____00__0.0120____12_3f, etc.

1.2.6 Type Casting

1. définition : conversion de type d’un objet vers un autre type.


2. contraintes d’utilisation :
• utilisation avec les types primitifs compatibles.
• double ←→ float ←→ int ←→ char
• number → boolean
3. exemples :
int i = 123;
float f = (float)i; //conversion d'un int en un float
double d = (double)i; //conversion d'un int en un double

d = 2.99999999;
f = (float)d; //conversion d'un double en un float
f = 1.24567f;
d = (double)d; //conversion d'un float en un double

11
f = 1.23f;
d = 2.99999999;
i = (int)f; //conversion d'un float en un int
i = (int)d; //conversion d'un double en un int

char c = (char)i; //conversion d'un int en un char


c = 'A';
i = (int)c; //conversion d'un char en un int

boolean b = (boolean)i; //conversion d'un int en un boolean


b = false;
i = (int)b; //conversion d'un boolean en un int
4. remarques :
• il faut faire attention à la perte de précision en convertissant
d’un type primitif vers un autre type primitif compatible mais
moins précis.
• le type casting pour les objets de types complexes est traité plus
tard dans le cadre de la programmation orientée objet.

1.3 Les instructions conditionnelles

1.3.1 If, else if, else

if (expressionBooleene1){
/*code si <expressionBooleene1> vaut true*/
}

else if (<expressionBooleene2>){
/*code si <expressionBooleene2> vaut true*/
}
...
else {
/*code sinon*/
}

1.3.2 Switch

switch(expression){ // integer | string


case n:
/*code si expression == n*/
break;
case m:
/*code si expression == m*/

12
break;
...
default:
/*code sinon*/
}

1.3.3 Expression ternaire

expressionBooleene ? /*code expressionBooleene vaut true*/ : /*code sinon*/ ;

1.4 Les boucles

1.4.1 La boucle while

while(<expression_booléene>){
/*code*/
}

1.4.2 La boucle do while

do {
/*code*/
} while(expression_booléene);

1.4.3 Les boucles for

/*La boucle for généralisée*/


for (/*initialisation*/ ; /*condition d'arrêt*/; /*incrémentation*/ ){
/*code*/
}

/*La boucle for adaptée aux objets itérables*/


for (type element : objetIterable){
/*code*/
}

1.4.4 Instructions de rupture des boucles :

1. continue : sauter à la prochaine itération dans la boucle.


2. break : sortir de la boucle.

13
1.5 Les méthodes de classe (fonctions)

/*définition d'une méthode de classe*/


/*==================================*/
visibilite static typeRetour nomFonction(
[type1 p1[, type2 p2[, ...[, typeN pN]]]]){
/*code*/
[return expression;] // expression doit être de type "typeRetour",
// si typeRetour différent de "void"
}

/*invocation d'une méthode de classe*/


/*==================================*/
methodeClasse([args]); // au sein du code de définition de la classe
NomClasse.methodeClasse([args]); // en dehors du code de définition de la classe

1.6 Les tableaux

1. définition : une structure de données linéaire indexée à partir de 0


avec une taille fixe.
2. syntaxes :
// déclaration et initialisation d'un tableau
// (taille calculée à partir du contenu)
type nomTableau[] = {/*contenu séparé par des ,*/ };

// syntaxe alternative à la première


type[] nomTableau = {/*contenu séparé par des ,*/ };

// déclaration et initialisation d'un tableau de deux dimensions


type nomTableauMulti[][] = {
{/*contenu du premier tableau*/ },
{/*contenu du deuxième tableau*/ },
...
}

// syntaxe alternative à la première


type[][] nomTableauMulti = {
{/*contenu du premier tableau*/ },
{/*contenu du deuxième tableau*/ },
...
}

type nomTableau[] = new type[taille]; // tableau vide de taille "taille"


type[] nomTableau = new type[taille]; // syntaxe alternative à la première

14
// tableau vide à deux dimensions ayant "lignes" lignes et "colonnes" colonnes
type nomTableauMulti[][] = new type[lignes][colonnes];

// syntaxe alternative à la première


type[][] nomTableauMulti = new type[lignes][colonnes];

nomTableau[i]; // accès au ième élément du tableau en lecture


nomTableau[i] = valeurDeType; // accès au ième élément du tableau en écriture
3. taille d’un tableau : nom_tableau.length;
4. exception levée lors d’accès à un index invalide : java.lang.ArrayIndexOutOfBoundException.
5. parcours d’un tableau :
/*avec la for généralisée*/
for (int i = 0; i < nomTableau.length; i++){
/*code*/
// ième élément du tableau accessible via nomTableau[i]
}

/*avec la for adaptée aux objets itérables*/


for (type element: nomTableau){
/*code*/
// element est un élément du tableau
}

1.7 TP - Conversion Celsius − Fahrenheit

package generic.test;

import java.util.Scanner;

public class Test {

public static double arrondi(double A, int B){


return (double) ((int)(A * Math.pow(10, B) + .5))/Math.pow(10, B);
}

public static double celsius2Fahrenheit(double c){


return (9.0/5.0)*c + 32;
}

public static double fahrenheit2Celsius(double f){


return arrondi((f-32)*5.0/9.0, 2);
}

15
public static String modeConversion(Scanner scanner){
System.out.println("Veuillez choisir un mode de conversion :");
System.out.println("1- Celsius -> Fahrenheit");
System.out.println("2- Fahrenheit -> Celsius ");
int choix = scanner.nextInt();
scanner.nextLine();
return (choix == 1)? "Celsius" : "Fahrenheit";
}

public static double saisieTemperature(String unitTemperature,


Scanner scanner){
System.out.println("Veuillez saisir une température en " + unitTemperature);
double temp = scanner.nextDouble();
scanner.nextLine();
return temp;
}

public static boolean nouvelleConversion(Scanner scanner){


System.out.println("Voulez-vous convertir une nouvelle température ? (O/N)");
char reponse = scanner.nextLine().charAt(0);
return reponse == 'O';
}

public static void main(String[] args) {


Scanner scanner = new Scanner(System.in);
boolean cont = true;
String unitTemperature;
double temperature;
do {
unitTemperature = modeConversion(scanner);
temperature = saisieTemperature(unitTemperature, scanner);
if (unitTemperature.equals("Celsius"))
System.out.println(temperature + "(C) = " +
celsius2Fahrenheit(temperature) + "(F)");
else
System.out.println(temperature + "(F) = " +
fahrenheit2Celsius(temperature) + "(C)");
cont = nouvelleConversion(scanner);

} while(cont);
scanner.close();
}
}

16
2 Programmation Orientée Objet en Java

2.1 Quelques classes utilitaires

2.1.1 La classe String

1. package : java.lang (importée automatiquement)


2. description : une chaîne de caractères.
3. quelques méthodes :
/*
* description : renvoie le nombre de caractères du string (i.e. sa longueur)
*/
public int length();

/*
* description : renvoie un nouveau string désignant le même string mais
avec des caractères minuscules (avec aucun effet sur les chiffres)
*/
public String toLowerCase();

/*
* description : renvoie un nouveau string désignant le même string mais
avec des caractères majuscules (avec aucun effet sur les chiffres)
*/
public String toUpperCase();

/*
* description : renvoie le caractère à "index" si celui-ci est valide
(i.e. ne dépasse pas la longueur du string), sinon renvoie -1
*/
public char charAt(int index);

/*
* description : renvoie le sous-string du string en commençant par l'indice
"beginIndex" [et se terminant à l'indice "end - 1"]
tant que les indices sont valides,
sinon renvoie un string vide
*/
public String substring(int beginIndex[, int endIndex]);

/*
* description : renvoie l'indexe de la première occurrence du
caractère "character" ou du premier caractère du string "string"
en commençant la recherche depuis le début du string appelant

17
(i.e. depuis l'indice 0)
*/
public int indexOf(String string | char character);

/*
* description : renvoie l'indexe de la première occurrence du
caractère "character" ou du premier caractère du string "string"
en commençant la recherche depuis la fin du string appelant
(i.e. depuis l'indice à l'indice string.length() - 1)
*/
public int lastIndexOf(String string | char character);

/*
* description : retourne la représentation String de l'objet de type
générique "object"
(équivalent de object.toString())
*/
public static String valueOf(T object);

2.1.2 La classe Math

1. package : java.lang (importée automatiquement)


2. description : classe finale dont toutes les méthodes sont statiques
et dédiées à effectuer des opérations mathématiques élémentaires.
3. quelques méthodes :
/*
* description : la valeur absolue du nombre de type T "a"
*/
public static T abs(T a);

/*
* description : base^exponent
*/
public static double pow(double base, double exponent);

/*
* description : une valeur pseudo-aléatoire entre 0 et 1
*/
public static double random();

/*
* description : le sinus de l'angle en radians a
*/
public static double sin(double a);

18
/*
* description : le cosinus de l'angle en radians a
*/
public static double cos(double a);

/*
* description : la tangente de l'angle en radians a
*/
public static double tan(double a);

2.1.3 La classe Random

1. package : java.util (à importer manuellement)


2. description : classe permettant la génération d’une séquence de nombres
pseudo-aléatoires à partir d’un seed que l’on peut récupérer sous différents
types.
3. quelques méthodes :
/**
* Retourne un générateur de nombres pseudo-aléatoires.
*/
public Random()

/**
* Retourne un générateur de nombres pseudo-aléatoires
* en utilisant "seed".
*/
public Random(long seed)

/**
* Retourne un stream de valeurs doubles pseudo-aléatoires
* entre 0 (inclus) et 1 (exclus).
*/
public DoubleStream doubles()

/**
* Retourne un stream de valeurs doubles pseudo-aléatoires
* entre randomNumberOrigin (inclus) et randomNumberBound (exclus).
*/
public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound)

/**
* Retourne un stream de "streamSize" valeurs doubles pseudo-aléatoires
* entre 0 (inclus) et 1 (exclus).

19
*/
public DoubleStream doubles(long streamSize)

/**
* Retourne un stream de "streamSize" valeurs doubles pseudo-aléatoires
* entre randomNumberOrigin (inclus) et randomNumberBound (exclus).
*/
public DoubleStream doubles(long streamSize, double randomNumberOrigin, double randomNumberB

/**
* Retourne un stream de valeurs entières pseudo-aléatoires
*/
public IntStream ints()

/**
* Retourne un stream de valeurs entières pseudo-aléatoires
* entre randomNumberOrigin (inclus) et randomNumberBound (exclus).
*/
public IntStream ints(double randomNumberOrigin, double randomNumberBound)

/**
* Retourne un stream de "streamSize" valeurs entières pseudo-aléatoires
*/
public IntStream ints(long streamSize)

/**
* Retourne un stream de "streamSize" valeurs entières pseudo-aléatoires
* entre randomNumberOrigin (inclus) et randomNumberBound (exclus).
*/
public IntStream ints(long streamSize, double randomNumberOrigin, double randomNumberBound)

/**
* Retourne un stream de valeurs entières longues pseudo-aléatoires
*/
public LongStream longs()

/**
* Retourne un stream de valeurs entières longues pseudo-aléatoires
* entre randomNumberOrigin (inclus) et randomNumberBound (exclus).
*/
public LongStream longs(double randomNumberOrigin, double randomNumberBound)

/**
* Retourne un stream de "streamSize" valeurs entières longues pseudo-aléatoires
*/
public LongStream longs(long streamSize)

20
/**
* Retourne un stream de "streamSize" valeurs entières longues pseudo-aléatoires
* entre randomNumberOrigin (inclus) et randomNumberBound (exclus).
*/
public LongStream longs(long streamSize, double randomNumberOrigin, double randomNumberBound

/**
* Retourne la prochaine valeur booléen pseudo-aléatoire générée
*/
public boolean nextBoolean()

/**
* Retourne le prochain tableaux de bytes pseudo-aléatoires généré
*/
Generates random bytes and places them into a user-supplied byte array.
public void nextBytes(byte[] bytes)

/**
* Retourne la prochaine valeur double pseudo-aléatoire générée
*/
public double nextDouble()

/**
* Retourne la prochaine valeur float pseudo-aléatoire générée
*/
public float nextFloat()

/**
* Retourne la prochaine valeur entière pseudo-aléatoire générée
*/
public int nextInt()

/**
* Retourne la prochaine valeur entière pseudo-aléatoire générée
* entre 0 (inclus) et bound (exclus).
*/
public int nextInt(int bound)

/**
* Retourne la prochaine valeur entière longue pseudo-aléatoire générée
*/
public long nextLong()

/**
* Affecter "seed" comme seed de ce générateur

21
*/
public void setSeed(long seed)

2.2 Notions de base pour la POO en Java

2.2.1 Classes et Objets

2.2.1.1 Classes
1. définition : une abstraction d’un ensemble d’éléments partageant
des propriétés communes (i.e. des caractéristiques et des comporte-
ments)
2. syntaxe de déclaration rudimentaire :
visibilite class NomClass{
/*attributes*/
...
/*methods*/
...
}

2.2.1.2 Objets/Instances
1. définition : un élément d’une classe obtenu en attribuant des valeurs
aux caractéristiques définies dans la classe.
2. opérateur d’accès aux propriétés d’un objet : objet.propriete

2.2.2 Attributs et Méthodes

2.2.2.1 Attributs
1. définition : caractéristiques structurelles d’une classe désignant son
état (i.e. variables de types primitifs ou complexes définies au sein de la
classe)
2. types : attributs d’instance et attributs de classe (statiques)

2.2.2.1.1 Attributs d’instance


1. définition : caractéristiques dont les valeurs peuvent changer d’une
instance à une autre.
2. syntaxes :
// définition au sein d'une classe
visibilite type nomAttribut[= valeurParDefaut];

// utilisation au sein de la classe

22
nomAttribut;
this.nomAttribut;

// utilisation en dehors de la classe si la visibilite le permet


objet.nomAttribut

2.2.2.1.2 Attributs de classe


1. définition : caractéristiques statiques dont les valeurs restent les
mêmes pour toutes les instances d’une classe (i.e. partagées par
toutes les instances de la classe).
2. syntaxes :
// définition au sein d'une classe
visibilite static type nomAttribut[= valeurParDefaut];

// utilisation au sein de la classe


nomAttribut;
NomClass.nomAttribut

// utilisation en dehors de la classe si la visibilite le permet


NomClass.nomAttribut
3. remarque : les attributs de classe ne peuvent être manipulés que par
des méthodes de classe.

2.2.2.2 Méthodes
1. définition : comportements dynamiques d’une classe (i.e. fonctions
définies au sein de la classe)
2. types : méthodes d’instance et méthodes de classe (statiques)
3. passage de paramètres :
• paramètre de type primitif : passage par valeur.
• paramètre de type complexe : passage par référence.
• tous les paramètres d’une méthode/fonction en Java sont con-
sidérés comme des paramètres ayant la direction “in” par défaut.
• les paramètres d’une méthode/fonction sont initialisés par les
arguments passés lors de l’invocation de la méthode/fonction.
4. variables locales :
• définition : des variables déclarées au sein d’une méth-
ode/fonction
• portée : le corps de la méthode dans lequelle elles étaient
déclarées
• visibilité : aucune visibilité explicitée.
• caractéristique : non initialisées par défaut.

23
2.2.2.2.1 Méthodes d’instance
1. définition : comportements permettant de manipuler les attributs
d’instance.
2. syntaxes :
/*définition d'une méthode d'instance*/
/*==================================*/
visibilite typeRetour nomMethode([type1 p1[, type2 p2[, ...[, typeN pN]]]]){
/*code*/
[return expression;] // expression doit être de type "typeRetour", si typeRetour différent
}

/*invocation d'une méthode d'instance*/


/*==================================*/
this.methodeInstance([args]); // au sein du code de définition de la classe
objet.methodeInstance([args]); // en dehors du code de définition de la classe

2.2.2.2.2 La méthode statique main()


1. description : le point de départ de tout projet Java, appelée par le
compilateur avec éventuellement des arguments passés en ligne de
commande et récupérés sous forme de chaînes de caractères.
2. caractéristique : il peut avoir plusieurs classes contenant des méth-
odes main() dans un projet, mais on ne peut choisir qu’une seule
comme point de départ d’un projet.
3. syntaxe :
public static void main (String[] args){
/*code*/
}

2.2.2.2.3 La méthode toString()


1. description : une méthode de la classe java.lang.Object, héritée
par toutes les classes en Java avec un comportement par défaut
retournant une représentation lisible prédéfinie de l’instance sous
forme d’un String (cf. Héritage et polymorphisme)
2. syntaxe :
public String toString();
3. remarques :
• par convention, il faut toujours fournir une implémentation
de la méthode toString() dans une classe afin de retourner une
désignation claire décrivant ses instances.
• si aucune méthode toString() n’est explicitée, c’est la méthode
héritée qui sera utilisée lors de l’invocation de celle-ci.

24
2.2.2.3 Initialisations par défaut
1. pour les attributs :

type valeur par défaut


boolean false
byte, short, ou int 0
long 0L
char '0'
float 0.0f
double 0.0
tout objet de type complexe null

2. pour les variables locales : aucune initialisation par défaut.

2.2.2.4 Le mot-clé this :


1. définition : this est une référence sur l’objet courant (i.e. l’objet
appelant la méthode dans laquelle on est) en lecture uniquement créée
automatiquement par le compilateur.
2. utilités :
• accéder aux propriétés de l’objet courant quand on a besoin de
les utiliser ;
• passer l’instance courante en argument d’une méthode.

2.2.2.5 Le mot-clé final :


1. avec une variable : une constante dont la valeur ne peut changer une
fois initialisée.
2. avec une méthode : une méthode qui ne peut être ni surchargée ni
redéfinie ailleurs (cf. Héritage et polymorphisme).
3. avec une classe : une classe qui ne peut être étendue par d’autres
classes (cf. Héritage et polymorphisme).

2.2.3 Constructeurs et destructeur

2.2.3.1 Constructeurs
1. définition : une méthode particulière dont le rôle consiste à con-
struire des instances d’une classe.
2. rôle :
• constuire des instances d’une classe en initialisant les attributs,
éventuellement via des arguments passés.
• signaler à la JVM qu’il faut réserver de l’espace mémoire pour
l’objet que l’on veut construire.

25
3. syntaxes :
// définition d'un constructeur
NomClass([params]){
/*initialisation des attributs et d'autres instructions*/
}

// invocation d'un constructeur en utilisant l'opérateur "new"


NomClass objet = new NomClass([args]);
4. remarque : plusieurs constructeurs ayant des signatures dif-
férentes peuvent être définis pour une même classe en exploitant le
principe de surcharge de méthodes.

2.2.3.1.1 Constructeur par défaut


1. création :
• si aucun constructeur n’est défini dans la classe, il sera créé
automatiquement par le compilateur.
• sinon (i.e. d’autres constructeurs sont définis dans la classe), il doit
être défini.
2. syntaxes :
// définition d'un constructeur par défaut
NomClass(){
/*initialisation des attributs et d'autres instructions*/
}

// invocation d'un constructeur par défaut


NomClass objet = new NomClass([args]);

2.2.3.1.2 Constructeur paramétré


1. création : facultative et manuelle.
2. syntaxes :
// définition d'un constructeur paramétré
NomClass(type1 param1[, ...]){
/*initialisation des attributs et d'autres instructions*/
}

// invocation d'un constructeur paramétré


NomClass objet = new NomClass(arg1[, ...]); // arg1 est de type "type1"

2.2.3.2 Destructeur

26
1. définition : une méthode particulière permettant de détruire une
instance d’une classe.
2. remarque : en Java, un destructeur n’est pas explicité, mais une
méthode finalize() est appelée implicitement quand le garbage col-
lector détruit un objet qui n’est plus référencé.

2.2.4 Encapsulation et accesseurs

2.2.4.1 Encapsulation
1. définition : le principe d’encapsulation consiste à rendre la visibil-
ité des attributs d’une classe private, qui ne seront accessibles qu’à
travers des méthodes d’accès en lecture/écriture.
2. utilités :
• une interface de manipulation simplifiée et abstraction de
l’implémentation interne des classes.
• une mesure de sécurité permettant d’empêcher le monde ex-
térieur de modifier la structure interne des classes.

2.2.4.2 Getters/Setters
1. définition : pour chaque attribut d’une classe, une paire de méthodes
publiques spéciales sont utilisées pour y accéder en accord avec le
principe d’encapsulation :
• getter (accesseur) : accès en lecture.
• setter (mutateur) : accès en écriture.
2. syntaxes :
// définition rudimentaire d'un getter
public type getAtt() {return att; }

// définition rudimentaire d'un setter


public void setAtt(type valeur) { att = valeur; }
3. remarques :
• selon la sémantique, un setter peut ne pas être défini (e.g. un
attribut qui est accessible en lecture uniquement).
• un setter est souvent utilisé pour contrôler la manière dont un objet
peut être modifié et d’autres événements résultant de cette mod-
ification (e.g. modification d’autres attributs, invocation de certains
méthodes, etc.)
• il faut privilégier l’utilisation des setters pour initialiser les at-
tributs dans les constructeurs au cas où cette initialisation en-
traînera le déclenchement d’autres événements.

27
2.3 Héritage et polymorphisme

2.3.1 Introduction

1. principe de la classification en objets : fournir une représentation


simplifiée du monde réel en se basant sur :
• la notion de “concept” vu en :
1. extension : l’ensemble des objets couverts par le concept.
2. intension : l’ensemble des prédicats vérifiés par les objets
couverts par le concept.
• la concrétisation dans des langages de programmation :
classes et interfaces en Java ; classes en C++.
• la classification/organisation des concepts selon les principes
suivants :
1. généralisation : l’inclusion des extensions d’un concept
spécialisé dans un autre plus généralisé.
2. spécialisation : raffinement des intensions d’un concept
généralisé dans un autre plus spécialisé.
• la concrétisation des principes de spécialisation/généralisation
dans les langages de modélisation/programmation par
l’héritage.
2. implémentation de la classification en objets :
• une arborescence : obtenue par héritage simple (Smalltalk, Java
(classes), etc.).
• un graphe sans circuit : obtenu par héritage multiple (Eiffel,
C++, Java (interfaces), etc.).
3. avantages :
• pas de redondance d’attributs communs et de certaines parties de
code dans les corps des méthodes communes
• pas de risque d’incohérence sur les parties communes entre
les différentes sortes de sous-classes.
• facilité d’étendre une superclasse à de nouvelles sous-classes.
• facilité de maintenance du code.

2.3.2 Généralisation, Spécialisation et Héritage

2.3.2.1 Généralisation
1. définition : mécanisme consistant à réunir les objets possédant des
caractéristiques communes dans une nouvelle classe plus générale.
2. superclasse ou classe mère : la classe la plus générale obtenue.
3. sous-classes ou classes filles : les classes à partir desquelles la super-
classe est généralisée.
4. exemple : une classe Bateau et une classe Voiture peuvent se
généraliser en une superclasse Vehicule dont ils deviennent sous-

28
classes.

2.3.2.2 Spécialisation
1. définition : mécanisme consistant à différencier à partir d’une classe
(superclasse) plusieurs sous-classes partageant des caractéristiques
communes.
2. propriétés :
• ces caractéristiques peuvent être éventuellement redéfinies par
les sous-classes selon leur point de vue.
• de nouvelles caractéristiques peuvent être éventuellement inté-
grées au sein des sous-classes, afin de les distinguer de la super-
classe.
3. exemple : une classe Bateau qui peut se spécialiser en deux sous-
classes BateauAVoile et BateauAMoteur.

2.3.2.3 Héritage
1. définition : la relation d’héritage entre une superclasse A et une
sous-classe B peut être exprimée par l’expression : « B est A mais A
n’est pas forcément B ».
2. concrétisation : lors d’une généralisation/spécialisation, on dit que
les sous-classes héritent d’une superclasse ou étendent une super-
classe.
3. propriétés :
• les propriétés d’une superclasse (i.e. attributs et méthodes) sont
héritées par les sous-classes.
• les sous-classes peuvent éventuellement redéfinir certaines pro-
priétés et en ajouter davantage.

2.3.2.4 Discriminants
1. définition :
• critère utilisé pour la classification des objets dans des sous-
classes à partir d’une superclasse.
• étiquette les relations de spécialisation/généralisation.
2. exemple :

2.3.2.5 Contraintes
1. définition : annotations circonscrites par des accolades décrivant la
relation entre un ensemble de sous-classes et leur superclasse.
2. types : il existe quatre types classés en deux paires complémen-
taires :

29
Figure 2: “Exemple d’utilisation de discriminants”

• complet/incomplet (complete/incomplete) : l’union des objets


des sous-classes couvrent (resp. ne couvrent pas) l’ensemble des
objets de la superclasse.
• disjoint/chevauchement (disjoint/overlap) : les sous-classes ne
peuvent pas (resp. peuvent) avoir d’instances communes.
3. exemple :

2.3.2.5.1 Analyse
1. il existe d’autres types d’appartement que “luxes” et “normaux” → con-
trainte incomplet.
2. un appartement ne peut pas être luxe et normal en même temps → con-
trainte disjoint.
3. un appartement est soit privé, soit pour une entreprise, et il n’existe pas
d’autre cas → contrainte complet.
4. un appartement peut être utilisé à la fois pour un usage privé et pour un
usage entreprise → contrainte chevauchement.

2.3.3 Modèles d’héritage et d’instanciation

2.3.3.1 Modèles d’héritage


Il existe deux grandes catégories de modèles d’héritage :
1. héritage simple :
• principe : un concept n’a qu’un seul super-concept direct.

30
Figure 3: “Exemple d’utilisation de contraintes”

• hiérarchie obtenue : une arborescence.


• exemple : modèle choisi par Java pour les classes.
2. héritage multiple :
• principe : un concept peut avoir plusieurs super-concepts di-
rects.
• hiérarchie obtenue : un graphe sans circuits.
• exemple : modèle choisi par UML et C++ pour les classes et
par Java pour les interfaces.

2.3.3.2 Modèles d’instanciation


Il existe deux grandes catégories de modèles d’instanciation :
1. la mono-instanciation :
• principe : une instance est rattachée directement à un seul
concept.
• exemple : modèle choisi par Java : dans une expression
d’instanciation d’un objet, on ne va trouver qu’une classe derrière
l’opérateur new : Carre c = new Carre(...);
2. la multi-instanciation :
• principe : une instance peut être rattachée directement à
plusieurs concepts.
• exemple : modèle choisi par UML

31
2.3.4 Héritage en Java

1. héritage dans les langages à objets :


• approche : un programmeur définit des hiérarchies de classes
provenant d’une conception dans laquelle il a utilisé le principe
de spécialisation/généralisation et éventuellement les discrimi-
nants et les contraintes.
• avantage : le code obtenu est réutilisable → modifier
une/plusieurs classes sans avoir à les réécrire entièrement,
grâce aux relations de spécilisation/généralisation.
2. héritage en Java :
• approche : quand une sous-classe hérite d’une superclasse, une in-
stance d’une sous-classe :
1. possèdera la partie structurelle définie dans la superclasse
ainsi qu’une partie structurelle définie dans la sous-classe
elle-même.
2. héritera du comportement défini dans la superclasse avec
quelques possibilités de variation.
• propriété : toutes les classes héritent de la classe
java.lang.Object selon un modèle d’héritage simple de
racine Object
• syntaxe :
visibilite class SousClasse extends SuperClasse{/*code*/ }

2.3.4.1 Le mot-clé super


1. définition : super est une référence sur l’instance de la superclasse
directe de l’objet courant en lecture uniquement.
2. utilités :
• accéder aux constructeurs de la superclasse directe ;
• accéder aux méthodes de l’objet courant telles qu’elles sont
définies au sein de la superclasse directe ;
• stabilité et réutilisabilité : la modification de la méthode dans
la superclasse entraîne la modification automatique du com-
portement des méthodes le référençant via super.

2.3.4.2 Héritage et constructeurs

2.3.4.2.1 Principes
1. single responsibility : pour chaque classe désignant un concept, sa
seule responsabilité est de s’occuper des propriétés qu’elle définit.
2. delegation : les propriétés héritées sont déléguées aux superclasses
correspondantes pour leur gestion.

32
2.3.4.2.2 Conventions pour la superclasse
1. toujours écrire un constructeur par défaut.
2. remarque : toutes les classes en Java héritant de la classe Object, le
constructeur par défaut de celle-ci est le premier à être appelé lors
de l’invocation d’un constructeur de n’importe quelle classe pendant
l’instanciation d’un objet.

2.3.4.2.3 Conventions pour les sous-classes


1. constructeur par défaut :
• première instruction : invoquer le constructeur par défaut de
la superclasse pour initialiser par défaut les attributs hérités,
qui :
1. se fait implicitement si aucune instruction d’invocation d’un
autre constructeur n’est faite dans le constructeur de la
sous-classe.
2. peut être explicitée en appelant le constructeur par défaut
de la superclasse via super();
3. remarque : la superclasse doit admettre un constructeur
par défaut, sinon il y aura une erreur de compilation.
• autres instructions : initialiser par défaut les attributs pro-
pres à la sous-classe.
2. constructeur paramétré :
• première instruction :
1. initialiser les attributs hérités par délégation à la super-
classe, invocant explicitement le constructeur paramétré
de la superclasse adapté pour la tâche via super(params).
2. remarque : la superclasse doit disposer d’un constructeur
paramétré admettant ces paramétres, sinon il y aura une
erreur de compilation.
3. constructeur passeur de paramètres :
• besoin : faire transiter les attributs hérités d’une superclasse
Css plus haut dans la hiérarchie que la superclasse directe Cs de
la class courante C.
• principe : définir un constructeur paramétré passeur dans
la superclasse directe Cs qui déléguera l’initialisation des at-
tributs hérités de Css à celle-ci.

2.3.4.3 Contrôle d’accès statique (visibilité)

2.3.4.3.1 Portée d’une classe


1. public : la classe est accessible par toutes les autres classes
partout.

33
2. Package private ou default (i.e. aucune valeur n’est explicitée) : la
classe est accessible uniquement par les éléments du même pack-
age la contenant.

2.3.4.3.2 Portée d’une propriété d’un objet


1. public : la propriété est accessible par tout le monde sur une in-
stance de la classe.
2. private : la propriété est uniquement accessible par les éléments
de la classe sur une instance de la classe elle-même.
3. Package private ou default (i.e. aucune valeur n’est explicitée) : la pro-
priété est uniquement accessible par les éléments du même package
la contenant sur une instance de la classe elle-même.
4. protected : la propriété est accessible :
• par tous les éléments du même package la contenant sur une
instance de la classe elle-même.
• par toutes ses sous-classe depuis d’autres packages, mais
uniquement sur une instance de la sous-classe ou une de ses
sous-classes et jamais en remontant.

2.3.4.3.3 Inclusion des portées (de la plus fermée à la plus ouverte)

(la moins ouverte) private < Package private < protected < public (la
plus ouverte)

2.3.4.4 Masquage (ou redéfinition), spécialisation et surcharge sta-


tique de méthodes

2.3.4.4.1 Signature d’une méthode


1. signature d’une méthode en Java : nom de la classe dans laquelle
elle est déclarée + le nom de la méthode + son type de retour + le
nombre et les types de ses paramètres.
2. problème : dans une hiérarchie de classes deux méthodes de même
signature sont interdites.
3. solutions possibles : surcharge, masquage spécialisation des
méthodes héritées.

2.3.4.4.2 Masquage de méthodes


1. définition : redéfinir la méthode héritée entièrement au niveau des
sous-classes.
2. remarques :

34
• il est toujours possible d’accéder à l’implémentation
de la méthode masquée par la superclasse directe via
super.methode().
• on n’utilise généralement super.methode() que dans la nouvelle
définition de la méthode methode() au niveau de la sous-classe.

2.3.4.4.3 Spécialisation de méthodes


1. définition : redéfinir une méthode héritée partiellement au niveau
des sous-classes. Autrement dit, en gardant le comportement hérité
mais en ajoutant des modifications.

2.3.4.4.4 Surcharge statique de méthodes


1. définition : définir une méthode ayant le même nom qu’une autre
méthode déjà définie dans la classe mais ayant des éléments de sig-
nature différents (i.e. types de retours différents ou différents nombre
de paramètres et/ou types de paramètres)

2.3.4.4.5 Redéfinition vs. surcharge statique d’une méthode


Une méthode m_r() redéfinie une méthode m_i() si et seulement si :
1. m_r() et m_i() possèdent le même nom.
2. m_r() et m_i() possèdent la même liste de paramètres (types et nom-
bre) → invariance de la liste des paramètres.
3. m_r() possède le même type de retour de m_i() ou une spécialisation
de celui-ci → variance des types de retour.
4. m_r() possède la même visibilité que m_i() ou une visibilité plus
ouverte que celle-ci.
5. pour les exceptions (cf. Les assertions et les exceptions) :
• m_r() spécialise ou retire depuis sa signature une/des déclara-
tions d’exceptions héritées depuis la signature de m_i().
• remarque : m_r() ne peut déclarer dans sa signature que des
exceptions déjà héritées ou des spécialisations de celles-ci.
6. dans les autres cas on parle de surcharge statique.

2.3.5 Polymorphisme en Java

2.3.5.1 Introduction
1. définition : la capacité d’un même morceau de code d’avoir des
comportements différents selon le contexte.
2. étymologie : en grec, poly : plusieurs ; morphe : forme.

35
2.3.5.2 Les résolutions des liens statique et dynamique

2.3.5.2.1 Types d’un objet en C


1. statique : identifié à la compilation (i.e. le type utilisé pour la déclara-
tion).
2. dynamique : identifié à l’exécution (i.e. le type utilisé pour
l’instanciation).

2.3.5.2.2 Résolution des liens


1. définition : indiquer le mécanisme de choix d’une méthode lors de son
invocation.
2. liaison statique :
• principe : le choix d’une méthode est connu à la compilation en
cherchant dans la classe correspondant au type statique de l’objet.
• contexte : les applications sensibles aux coûts de mémoire et de
temps d’exécution, non destinées à des extensions basées sur les mé-
canismes objets.
3. liaison dynamique :
• principe : le choix initial d’une méthode se fait à la compilation
et sera précisé davantage lors de l’exécution en cherchant dans la
classe correspondant au type dynamique de l’objet.
• contexte : les applications basées sur les mécanismes objets.

2.3.5.3 Polymorphisme et liaison dynamique


1. la liaison dynamique permet de définir des méthodes portant le
même nom dans plusieurs classes : le code de la méthode qui sera
réalisé à l’exécution n’est pas figé à la compilation et sera différent
selon le processus de la liaison dynamique.
2. polymorphisme : la capacité d’une même méthode de correspon-
dre à plusieurs formes de traitements.
3. méthode polymorphe : méthode permettant le polymorphisme.

2.3.5.4 Transtypage (coercition de type) et tests de type :


1. transtypage : convertir à la compilation une instance “objet” d’un
type A en une instance d’un autre type B (i.e. le compilateur considérera
que le type statique de “objet” est B)
2. test de type : un test booléen permettant d’indiquer si une instance
donnée est une instance d’une classe donnée.
3. remarque : en général, on n’utilise le transtypage et les tests de type
sauf dans certains contextes, car ils révèlent souvent une mauvaise
conception.

36
4. syntaxes :
// transtypage
type1 nomVar;
type2 autreVar = (type2) nomVar; // conversion du type de "nomVar" en "type2"

// test de type
/*
* à l'exécution, si le type dynamique de "instance" désigne "NomClasse" ou
l'une de ses sous-classes, retourne "true", sinon retourner "false"
*/
instance instanceof NomClasse; // instanceof est un opérateur de test de type

2.3.5.5 Upcast
1. définition : affecter une instance d’une sous-classe à une variable
d’une superclasse, où le transtypage est effectué implicitement.
2. principe : ce comportement est toujours possible vu la nature de
la relation d’héritage, indiquant qu’un objet d’une sous-classe est
forcément inclus dans l’extension de la superclasse.
3. conséquence : la variable de la superclasse aura accès uniquement
aux propriétés héritées par la sous-classe de la superclasse.
4. syntaxe :
SuperClasse sup = new SousClass();
4. exemples :
// upcast possible car tout étudiant est forcément une personne
Personne p = new Etudiant();

Personne pers;
Etudiant e = new Etudiant();
pers = e;

2.3.5.6 Downcast
1. définition : l’inverse du Upcast : affecter une instance d’une super-
classe à une variable d’une sous-classe, où le transtypage doit être
effectué explicitement.
2. principe : ce comportement est parfois possible vu la nature de la
relation d’héritage, indiquant qu’un objet d’une sous-classe est for-
cément inclus dans l’extension de la superclasse, mais l’inverse n’est
pas toujours vrai.
3. conséquence : le transtypage est possible uniquement si l’instance
de la superclasse est obtenue par instanciation de la sous-classe ou
de l’une de ces sous-classes (i.e. via un Upcast)

37
4. remarque : le Downcast ne doit se faire qu’en cas de nécessité absolue.
5. syntaxe :
SuperClasse sup = new SousClasse();
SousClasse sous = (SousClasse) sup;
6. exemple :
// downcast possible car p contient une instance de type statique Etudiant
Personne p = new Etudiant();
Etudiant e = (Etudiant) p;

2.3.5.7 Affectation polymorphe


1. définition : le comportement polymorphe de l’opérateur
d’affectation quand celui-ci est utilisé lors d’un transtypage normal,
d’un Upcast ou d’un Downcast.

2.3.5.8 La classe Object


1. package : java.lang (importée automatiquement)
2. description : superclasse de toutes les classes en Java, définissant les
propriétés générales des objets en Java qui peuvent être spécialisées
par les sous-classes.
3. quelques méthodes :
/*
* description : tester l'égalité logique de l'objet courant à un autre objet
*/
public boolean equals(Object obj);

/*
* description : retourner une représentation lisible de l'objet
sous forme d'un String
*/
public String toString();

/*
* description : retourner un code de hachage entier identifiant l'objet
* remarque : deux objets ayant le même code de hachage ne sont pas forcément
identiques (collision possible des codes de hachage)
*/
public int hashCode();

/*
* description : retourner le type de la classe de l'objet à l'exécution
*/

38
public final Class<?> getClass();

/*
* description : cloner l'objet
* remarque : une classe souhaitant redéfinir le cloneage d'un objet
doit implémenter l'interface "Cloneable" et redéfinir la méthode avec
une visibilité "public"
*/
protected Object clone();

2.3.5.9 La classe Objects


1. package : java.util (à importer manuellement)
2. description : une classe permettant (entre autres) de simplifier la
redéfinition des méthodes de la classe java.lang.Object pour une
classe donnée.
3. quelques méthodes :
/*
* description : tester l'égalité logique des objets "o1" et "o2"
*/
public static boolean equals(Object o1, Object o2);

/*
* description : retourner le code de hachage des attributs d'un objet
*/
public static int hash(Object att1[, ...]);

2.3.5.9.1 Redéfinition de méthodes de Object pour une classe


avant Java 7
/* equals() */
@Override
public boolean equals (Object o) {
/*si l'objet courant et o référencent le même objet en mémoire
renvoyer true*/
if (this == o) return true;

/*si o est nul ou n'appartient pas à la classe de l'objet courant,


renvoyer false*/
if (o == null || this.getClass() != o.getClass()) return false;

/*coercition de o en "type"*/
type transtypedO = (type) o;

39
/*si tous les attributs de type primitif de l'objet courant
ne correspondent pas aux attributs de type primitif de transtypedO,
renvoyer false*/
if (!(this.attSimple1 == transtypedO.attSimple1
&& this.attSimple2 == transtypedO.attSimple2
&& ...
&& this.attSimpleN == transtypedO.attSimpleN))
return false;

/*pour chaque attribut de type objet de l'objet courant,


si celui-ci est nul et son attribut correspondant dans
transtypedO ne l'est pas,
renvoyer false*/
if (this.attObjet1 == null && transtypedO.attObjet1 != null) return false;

/*sinon, si l'attribut objet de l'objet courant n'est pas logiquement égal


à l'attribut objet correspondant dans transtypedO,
renvoyer false*/
else if (!this.attObjet1.equals(transtypedO.attObjet1)) return false;

if (this.attObjet2 == null && transtypedO.attObjet2 != null) return false;


else if (!this.attObjet2.equals(transtypedO.attObjet2)) return false;
...

if (this.attObjetN == null && transtypedO.attObjetN != null) return false;


else if (!this.attObjetN.equals(transtypedO.attObjetN)) return false;

/*sinon renvoyer true*/


return true;
}

/* hashCode() */
@Override
public int hashCode(){
int prime = premier; //choix d'un nombre premier
int result = 1; //initialisation du résultat

/*conversion des attributs de type primitif "boolean"


en type objet "Boolean"*/
attBoolean1 = (Boolean) attBoolean1;
attBoolean2 = (Boolean) attBoolean2;
...
attBooleanN = (Boolean) attBooleanN;

/*pour tout attribut de type primitif, multiplier le résultat par


le nombre premier et ajouter la valeur de l'attribut au résultat*/

40
result = prime*result + attSimple1;
result = prime*result + attSimple2;
...
result = prime*result + attSimpleK;

/*pour tout attribut de type objet, multiplier le résultat par


le nombre premier et ajouter le nombre de hachage de l'attribut objet
au résultat s'il est non nul, sinon on n'ajoute rien*/
result = prime*result + (attObjet1 == null)? : 0 + attObjet1.hashCode();
result = prime*result + (attObjet2 == null)? : 0 + attObjet2.hashCode();
...
result = prime*result + (attObjetM == null)? : 0 + attObjetM.hashCode();

/*retour du résultat*/
return result;
}
Depuis Java 7 (en utilisant la classe Objects)
/* equals() */
public boolean equals (Object o){
if (this == o) return true;
if (o == null || this.getClass() != o.getClass()) return false;

type transtypedO = (type) o;

return Objects.equals(this.att1, transtypedO.att1)


&& Objects.equals(this.att2, transtypedO.att2)
&&
...
&& Objects.equals(this.attN, transtypedO.attN);
}

/* hashCode() */
public int hashCode(){
return Objects.hash(this.att1, this.att2, ..., this.attN);
}

2.4 Les packages

2.4.1 Introduction

2.4.1.1 Définition
1. regroupement logique de classes assurant un classement thématique
sous forme d’une hiérarchie d’inclusion de dossiers.

41
2. les packages sont reliés par des relations de dépendance.

2.4.1.2 Intérêts
1. meilleure structuration, traçabilité et lisibilité d’une application.
2. portabilité des classes d’une application à une autre.

2.4.2 Packages en Java

2.4.2.1 Convention de nommage


regex de la convention : /^(extension|ccTLD)([a-z0-9]+\.)+[a-z0-9]+$/
:
1. extension : {com, gov, edu, net, mil, etc.}
2. ccTLD :
• description : un code consistant des deux lettres identifiant un
pays selon le standard ISO 3166, 1981.
• exemples : fr : France ; en : Etats-unis.

2.4.2.2 Le package java.lang


1. description : le package importé automatiquement par tout pro-
gramme Java.
2. contenu : les classes les plus couramment utilisées telles que :
• Object : la superclasse de toutes les classes en Java.
• String : pour utiliser les chaînes de caractères.
• System : pour utiliser l’entrée et la sortie standard à travers les
objets out et in, entre autres fonctionnalités.
• Math : pour utiliser des fonctions mathématiques élémentaires.

2.4.2.3 Importation des packages


1. besoin : les classes ne se trouvant pas dans le package java.lang
doivent être incluses manuellement dans un projet : on parle
d’importation d’éléments ou de paquets.
2. nom complet d’une classe : paquet1.paquet2.….paquetK.NomClasse.
3. syntaxes :
/*
* première instruction à inclure contenant le chemin du paquet
* remarque : aucun mot-clé Java ne doit être présent dans le nom d'un paquet, sauf s'il est
*/
package name.package_projet;

// instruction d'importation des éléments et des paquets utilisés dans le projet

42
import paquet.NomClasse; // importer la classe NomCLasse du paquet "paquet"
import paquet.*; // importer tout le contenu du paquet "paquet"
• remarque : raccourci en Eclipse pour inclure les importations
manquantes automatiquement et les organizer : ctrl + shift +
o.

2.5 Les classes abstraites et interfaces

2.5.1 Classes et méthodes abstraites

2.5.1.1 Classes abstraites


1. définition : plus une classe occupe un rang élevé, plus elle est générale,
plus elle est abstraite et ne peut être instanciée.
2. utilité : regroupement de méthodes :
• méthodes classiques : comportements commun implémen-
tés par défaut qui seront hérités par toutes ses sous-classes et
peuvent être surchargés ou redéfinis.
• méthodes abstraites : éventuellement des comportements
non implémentés qui seront hérités et doivent être implémen-
tés par les sous-classes non abstraites.

2.5.1.2 Méthodes abstraites


1. définition :
• méthode déclarée mais non définie dans la classe abstraite
(i.e. signature sans implémentation).
• doit être définie obligatoirement dans les sous-classes non ab-
straites de la classe abstraite chacune selon son point de vue.
2. remarque : une classe abstraite peut ne pas inclure aucune méthode
abstraite; en général elle inclut au moins une.
// définition d'une classe abstraite
visibilite abstract class NomClasseAbstraite {
// déclaration d'une méthode abstraite
visibilite abstract typeRetour nomMethode([params]);
}

2.5.2 Interfaces

2.5.2.1 Introduction
1. définition : une interface se limite à présenter des services fournis sous
forme d’une classe complétement abstraite implémentée par des

43
classes concrètes dans une relation de sous-typage (i.e. la classe con-
crète est un sous-type d’une interface) pour assurer le polymorphisme
des comportements implémentés.
2. utilité : les interfaces :
• sont plus abstraites que les classes abstraites et peuvent assurer
une meilleure réutilisation.
• permettent de découpler le fonctionnement (implémentation)
d’un comportement de son interfaçage (interface).
3. services fournis par une interface :
• avant Java 7 :
1. méthodes d’instance publiques et abstraites : public et
abstract.
2. attributs de classe publiques et constants : public,
static, et final.
• depuis Java 8 : les services supplémentaires suivants :
1. méthodes d’instance publiques ayant un comportement
par défaut : public et default.
2. méthodes de classe publiques : public et static.
3. des types internes : classes imbriqueés statiques ou non
statiques (cf. Les classes imbriquées).
4. une interface ne contient pas :
• de constructeurs ;
• d’attributs privés ;
• d’attributs d’instance ;
• d’implémentations des méthodes publiques abstraites
(hormis les méthodes statiques et celles ayant un comportement par
défaut)
5. remarques :
• les modifieurs public, abstract, final et static peuvent être
omis lorsqu’il n’y a pas d’ambiguïté.
• il existe une relation de sous-typage interne entre toute inter-
face et la classe java.lang.Object.
// définition d'une interface
public interface NomInterface {
/*attributs de classe publiques et statiques*/
type1 att1;
type2 att2;

typek attN;

/*méthodes*/
//méthodes d'instance publiques et abstraites
typeRetour1 m1([params]);
typeRetour2 m2([params]);

44
typeRetourK mK([params]);

//méthodes d'instance ayant des comportements par défaut


default typeRetour1 md1([params]){/*code*/ }
default typeRetour2 md2([params]){/*code*/ }

default typeRetourL mdL([params]){/*code*/ }

//méthodes de classe publiques


static typeRetour1 ms1([params]){/*code*/ }
static typeRetour2 ms2([params]){/*code*/ }

static typeRetourM msM([params]){/*code*/ }
}

// création d'une variable désignant un objet défini par une interface


NomInterface i;

/*
* affectation polymorphe possible en vue de la relation de sous-typage
entre "Object" et toute interface définie
*/
Object o = i;

2.5.2.2 Héritage des interfaces


1. type : héritage multiple entre les interfaces : une interface peut
avoir plusieurs super-interfaces.
2. contrainte : si une interface hérite une méthode ayant la même
signature dans deux super-interfaces, on aura un conflit de noms et
il faudra redéfinir la méthode concernée dans l’interface pour éviter
l’ambiguïté lors de son invocation.
public interface NomInterface extends I1[, I2[, ...]]{/*code*/ }

2.5.2.3 Implémentation d’une interface par une classe


1. définition : une classe concrète permet d’utiliser une interface offrant
des comportements en concrétisant le fonctionnement des services
offerts par l’interface : On dit que la classe implémente l’interface.
2. contraintes et propriétés :
• une classe concrète :
1. doit implémenter les méthodes abstraites ;
2. peut redéfinir les méthodes publiques statiques d’une in-
terface ;

45
3. peut implémenter plusieurs interfaces → dépasser les
limites de l’héritage simple entre les classes et assurer une
meilleure classification des concepts.
• si la classe :
1. n’implémente pas toutes les méthodes abstraites de
l’interface, alors elle doit être déclarée abstraite ;
2. hérite d’une classe et implémente une interface, l’expression
d’héritage doit précéder celle d’implémentation lors de la
définition de la classe.
public class NomClasse [extends SuperClasse,] implements NomInterface[, I1[, I2[, ...]]{/*co

2.5.3 Exemple d’utilisation des interfaces

// la classe abstraite Animal


package openclassrooms.animaux;

public abstract class Animal {


/*attributes*/
protected String couleur;
protected int poids;

/*methods*/
//default methods
protected void manger(){
System.out.println("Je mange de la viande");
}

protected void boire(){


System.out.println("Je bois de l'eau");
}

public String toString(){


return "Je suis un objet de la classe " + this.getClass().getName()
+ ", je suis " + this.couleur + ", je pèse " + this.poids
+ " kilos";
}

//abstract methods
public abstract void deplacement();
public abstract void crier();

// la classe abstraite Canin

46
package openclassrooms.animaux;

public abstract class Canin extends Animal {


/*methods*/
//default methods
public void deplacement(){
System.out.println("Je me déplace en meute !");
}
}

// la classe abstraite Félin


package openclassrooms.animaux;

public abstract class Felin extends Animal {


/*methods*/
//default methods
public void deplacement(){
System.out.println("Je me déplace seul !");
}
}

// l'interface Rintintin
package openclassrooms.animaux;

public interface Rintintin {


void faireCalin();
void faireLechouille();
void faireLeBeau();
}

// la classe concrète Chat


package openclassrooms.animaux;

public class Chat extends Felin {


/*constructors*/
public Chat(){}

public Chat(String couleur, int poids){


this.couleur = couleur;
this.poids = poids;
}

/*methods*/
//inherited abstract methods
public void crier(){
System.out.println("Je miaule sur les toits !");

47
}
}

// la classe concrète Lion


package openclassrooms.animaux;

public class Lion extends Felin {


/*constructors*/
public Lion(){}

public Lion(String couleur, int poids){


this.couleur = couleur;
this.poids = poids;
}

/*methods*/
//inherited abstract methods
public void crier(){
System.out.println("Je rugis dans la savane !");
}
}

// la classe concrète Tigre


package openclassrooms.animaux;

public class Tigre extends Felin {


/*constructors*/
public Tigre(){}

public Tigre(String couleur, int poids){


this.couleur = couleur;
this.poids = poids;
}

/*methods*/
//inherited abstract methods
public void crier(){
System.out.println("Je grogne très fort !");
}
}

// la classe concrète Loup


package openclassrooms.animaux;

public class Loup extends Canin {


/*constructors*/

48
public Loup(){}

public Loup(String couleur, int poids){


this.couleur = couleur;
this.poids = poids;
}

/*methods*/
//inherited abstract methods
public void crier(){
System.out.println("Je hurle à la lune !");
}
}

// la classe concrète Chien


package openclassrooms.animaux;

public class Chien extends Canin implements Rintintin {


/*constructors*/
public Chien(){}

public Chien(String couleur, int poids){


this.couleur = couleur;
this.poids = poids;
}

/*methods*/
//inherited abstract methods from Canin
public void crier(){
System.out.println("J'aboie sans raison !");
}

//inherited abstract methods from Rintintin


public void faireCalin(){
System.out.println("Je te fais un gros calin");
}

public void faireLechouille(){


System.out.println("Je fais de grosser léchouilles");
}

public void faireLeBeau(){


System.out.println("Je fais le beau !");
}
}

49
// la classe concrète Test
package openclassrooms.animaux;

public class Test {

public static void main(String[] args) {


Animal tableau[] = {
new Chien("Brun", 10),
new Chat("Blanc", 5),
new Lion("Marron", 200),
new Tigre("Raillé", 200),
new Loup("Gris bleuté", 30)
};
for (int i=0; i<tableau.length; i++){
tableau[i].manger();
tableau[i].boire();
tableau[i].deplacement();
tableau[i].crier();
System.out.println(tableau[i] + "\n");
}

Chien c = new Chien("Gris bleuté", 20);


c.manger();
c.boire();
c.deplacement();
c.crier();
System.out.println(c);
c.faireCalin();
c.faireLechouille();
c.faireLeBeau();
System.out.println();

Rintintin r = new Chien();


r.faireCalin();
r.faireLechouille();
r.faireLeBeau();
}
}

2.6 Spécialisation naturelle et substituabilité de Liskov

2.6.1 Introduction

La spécialisation naturelle et la substituabilité de Liskov sont deux


principes sous-jacents à la modélisation et programmation par objets

50
qui peuvent être parfois antagonistes.

2.6.2 Spécialisation naturelle

1. définition : un concept S spécialise naturellement un concept T


si et seulement si l’ensemble des instances de S est inclus dans
l’ensemble des instances de T.
2. description : principe de modélisation :
• qui consiste à modéliser la sémantique naturelle d’un domaine
sans se préocupper des vérifications.
• adopté par UML
3. utilités :
• structurer la pensée et organiser les connaissances ;
• ne se préoccupe pas de :
1. dire ce qui va se passer pour un sous-groupe partciulier
(e.g. aliments frais) d’un groupe plus général (e.g. aliments) ;
2. faire des vérifications d’expressions.
4. exemples :
• les aliments frais sont des aliments.
• les carrés sont des rectangles.
• etc.

2.6.2.1 Contraintes de redéfinition d’une méthode héritée en UML

1. les paramètres peuvent varier dans le même sens de la redéfinition


: possibilité de spécialiser les types des paramètres (covariance des
types de paramètres).
2. les types de retour peuvent varier dans le même sens de la redéfini-
tion : possibilité de spécialiser les types de retour (covariance des
types de retour).

2.6.3 Principe de Substituabilité (ou Principe de Liskov)

1. définition : un type S est un sous-type d’un autre type T si et seule-


ment si tout objet de type S peut remplacer une expression de
type T (susbtituabilité comportementale) sans que le programme n’ait
de comportement imprévu.
2. description : principe de programmation :
• qui consiste à anticiper au maximum les erreurs de program-
mation (surtout liées au typage) et de les limiter à l’exécution.
• introduit par Jeannette Wing et Barbara Liskov.
• adopté par les langages orientés-objets à typage statiques tels
que Java.

51
3. utilités :
• vérifier un programme en limitant les erreurs à l’exécution
(i.e. le programme compile bien et pas d’erreurs de compilation) mais
demande le fait d’anticiper.
• imposer :
1. des restrictions sur les signatures lors de la redéfinition des
méthodes héritées.
2. des conditions comportementales apparentes notamment sur
les contrats (cf. Les assertions et les exceptions)
• appliquer avec un risque réduit des upcasts via des affectations
polymorphes.
4. exemple : les aliments frais ne sont pas des aliments dans le contexte
du comportement :
• on ne peut pas ranger les aliments frais dans tous les lieux où
on range des aliments.
• il faut un lieu réfrigéré aux aliments frais.
• ainsi les aliments et les aliments frais ne se comportent pas de
la même manière.

2.6.3.1 Restrictions sur les signatures des méthodes lors d’une redéf-
inition
1. les paramètres peuvent varier dans le sens inverse de la redéfinition
: contravariance des types de paramètres.
2. les types de retour peuvent varier dans le même sens de la redéfini-
tion : covariance des types de retour.
3. les pré-conditions sont affaiblies (cf. Les assertions et les exceptions)
4. les post-conditions sont renforcées (cf. Les assertions et les exceptions)

2.6.3.2 Le principe de substituabilité de Liskov et Java


1. invariance des types de paramètres : la contravariance sur les
types de paramètres imposée par le principe de substitution de
Liskov n’est pas en accord avec la spécialisation naturelle d’UML,
ainsi Java choisit l’invariance des types des paramètres lors de la
redéfinition d’une méthode héritée pour éviter les incohérences.
2. covariance du type de retour : la covariance du type de retour
conforme au principe de substitution de Liskov est en accord avec
la spécialisation naturelle d’UML, ainsi Java choist la covariance
des types de retour lors de la redéfinition d’une méthode héritée en
accord avec les deux philosophies.

52
Figure 4: “Contravariance des types de paramètres selon le principe de Liskov”

2.7 Les assertions et les exceptions

2.7.1 Conception par contrats

1. définition :
• la mise en place de règles sur les classes et les méthodes pour
leur vérification.
• les règles établissent des contrats entre les objets (en matière
de leurs responsabilités respectives) qui collaborent pendant
l’exécution d’un programme.
2. portée :
• les invariants des classes :
1. définition : règles sur des objets.
2. exemples : une voiture a 4 roues, l’âge d’une personne est
positif, etc.
• les invariants des algorithmes :
1. définition : des propriétés vérifiées en certains points in-
ternes d’une méthode.
2. exemple : à la fin de l’itération i dans la recherche d’un maxi-
mum d’un tableau, la variable max contient la plus grande valeur
trouvée entre les itérations 0 et i.
• les invariants de flux :

53
1. définition : des propriétés vérifiés en certains points in-
ternes d’un flux de données.
2. exemple : ce point du programme ne peut être atteint.
• les axiomes des types abstraits de données :
1. définition : des propriétés qui doivent être vérifiées sur les
méthodes d’un type abstrait de données.
2. exemple : après avoir empilé un élément dans une pile, celui-ci
apparaît au sommet de la pile.
• les pré-conditions :
1. définition : les propriétés vérifiées à l’entrée d’une méth-
ode (i.e. avant l’invocation).
2. exemple : une méthode qui est appelée en dehors de son domaine
de définition (e.g. dépiler une pile vide, renseigner une mauvaise
valeur d’un paramètre, etc.)
3. remarque : la responsabilité du client de la méthode.
• les post-conditions :
1. définition : les propriétés vérifiées à la sortie d’une méth-
ode (i.e. après l’invocation).
2. exemple : à la fin d’un tri d’un tableau, le tableau est trié.
3. remarque : la responsabilité de la méthode (i.e. du concep-
teur de la méthode) envers son client.
3. historique :
• approche à objets : Bertrand Meyer.
• mise en oeuvre dans un langage de programmation : Eiffel
(d’une manière native).
4. mise en oeuvre en Java :
• les assertions ;
• les exceptions ;
• autres méthodes : spécifications, preuves formelles, conception et
réalisation des tests unitaires, etc.

2.7.2 Assertions

1. définition : vérifier des prédicats sur les classes et les méthodes


telles que :
• les invariants des classes ;
• les invariants d’algorithmes ;
• les axiomes des types abstraits de données;
• les invariants de flux;
• les post-conditions.
2. utilité : mise au point et débogage d’un programme.
3. contraintes d’utilisation :
• un programme ne respectant pas une assertion sera inter-
rompu par une erreur de type java.lang.AssertionError.
• les assertions peuvent être activées ou inhibées dans un pro-

54
gramme.
• une fois que le programme est mis au point, on doit se
débarasser des assertions dans le code final avant sa mise en
production.
4. syntaxes :
assert expressionBooleene; // expression représentant le prédicat à vérifier

/*
* en cas de non respect de du prédicat,
* le résultat retourné par l'erreur contiendra le
* résultat de objet.toString()
*/
assert expressionBooleene : objet;

2.7.3 Exceptions

2.7.3.1 Introduction
1. erreur : une erreur liée à la JVM : manque des ressources, assertions
non respectées, etc.
2. exception : mécanisme de gestion des erreurs consistant à :
• signaler les erreurs pouvant se produire pendant l’exécution
d’un programme et les capturer.
• revenir à un fonctionnement normal ou arrêter le programme
proprement, afin d’éviter les arrêts brutaux d’un programme.

2.7.3.2 Exceptions en Java


mise en oeuvre : des objets représentant des erreurs à l’exécution corre-
spondant à :
1. des invariants de classe (parfois) ;
2. des pré-conditions.
3. certaines post-conditions traitées à l’exécution, par exemple :
• faute de saisie : renseigner un type de données T quand on
attend un type de données S.
• faute de programmation : division par 0, sortie des bornes d’un
tableau, etc.
• problème materiel : e.g. plus de place mémoire lors de l’écriture
d’un fichier.

2.7.3.2.1 La classe Throwable


1. package : java.lang (importée automatiquement)

55
2. description : la superclasse de toutes les exceptions et erreurs en
Java.
3. quelques attributs :
• un message d’erreur.
• l’état de la pile des appels des méthodes.
4. quelques méthodes :
// constructors
Throwable();
Throwable(String message);
...

/*
* description : renvoie le message d'erreur
*/
public String getMessage();

/*
* description : imprimer l'état de la pile des appels des méthodes
dans la sortie des erreurs standard
*/
public void printStackTrace();

2.7.3.2.2 La classe Error


1. package : java.lang (importée automatiquement)
2. description : la superclasse de toutes les erreurs en Java.
3. remarque : les erreurs ne doivent pas être capturées en général.

2.7.3.2.3 La classe Exception


1. package : java.lang (importée automatiquement)
2. description : la superclasse de toutes les exceptions prédéfinies en
Java et de toutes les classes d’exception personnelles créées
3. remarques :
• les instances de la hiérarchie des classes enracinée par
java.lang.RunTimeException n’ont pas besoin d’être déclarées
explicitement dans les signatures des méthodes
• convention de nommage des exceptions en Java : nom valide
en CamelCase suffixé par Exception;

classe description
NullPointerException l’invocation d’une propriété sur un
objet null
ArrayIndexOutOfBoundException la sortie des bornes d’un tableau

56
classe description
ArithmeticException la division par zéro par exemple
NumberFormatException la conversion d’un String en un
nombre n’ayant pas le format
approprié
NegativeArraySizeException la création d’un tableau avec une
taille négative

2.7.3.2.4 Définition d’une classe d’exception


On définit une classe pour le type de notre exception :
1. héritant de la classe java.lang.Exception → bénéficier du mécan-
isme des exceptions en Java.
2. éventuellement ayant des attributs → décrire la localisation de
l’exception ou l’état des objets au moment de son occurrence.
3. éventuellement ayant des méthodes → traiter les exceptions capturées.
// définir une classe d'exception
public class nameException extends Exception {
[attributes]
[constructors]
[methodes]
}

2.7.3.2.5 Déclaration et levée d’une exception


1. déclaration d’une exception dans la signature d’une méthode
: indiquer que cette méthode peut lever cette exception lors de son
exécution.
2. levée d’une exception dans le corps d’une méthode : lever une
exception quand l’état du programme correspond à un état définis-
sant une erreur que nous avions défini et terminer l’exécution de la
méthode.
// définir une méthode déclarant et levant une exception
visibilite [static] nomMethode([params]) throws NomException[, OtherException[, ...]]{
/*code*/
throw new NomException([params]); // lancement de l'exception dans le corps d'une méthode
/*code*/
}

2.7.3.2.6 Capturation ou transmission d’une exception


définition : dans un programme utilisant une méthode m() pouvant lever
une exception, on peut :

57
Figure 5: “Hiérarchie des classes d’erreur de Throwable”

Figure 6: “Hiérarchie des classes d’exception de Throwable”

58
1. soit la capturer et la traiter dans un bloc de contrôle particulier
dans le corps du programme : le bloc try-catch[-finally]
2. soit la transmettre à la classe d’exception correspondante déclarée
dans la signature de la méthode m() : approche à priviliégier si
l’exception correspond à une exception de bas niveau (i.e. prédéfinie
dans l’API Java).
// définir une méthode capturant et traitant une exception
visibilite [static] nomMethode([params]) [throws NomException1[, ...]]{
...
/*
* description : quand une exception est levée dans un bloc try (Bloc 0),
l'exécution de celui-ci s'interrompt.
*/
try{/*Bloc 0*/ }
/*
* description : les clauses catch sont examinées par ordre de leur déclaration
jusqu'à trouver une classe nomExceptionI désignant celle de l'instance
de l'exception levée ou l'une de ses superclasses dans l'hiérarchie
d'héritage des exceptions :
a. si on en trouve une, alors le bloc I de la clause catch capturant
l'exception sera exécuté et le programme reprend
son exécution après les clauses catch suivantes.
b. si on n'en trouve aucune alors : l'exception ne sera pas capturée,
mais elle sera transmise à la méthode et l'exécution de celle-ci termine.
*
* remarque : (il faut que la méthode déclare la classe de l'exception dans la
clause "throws" de sa signature, sinon il y aura
une erreur de compilation)
*/
catch(NomException1 e1){/*Bloc 1*/ }
catch(NomException2 e2){/*Bloc 2*/ }
...
catch(NomExceptionN eN){/*Bloc N*/ }
/*
* description : si le bloc finally est fournis, il sera exécuté en tout cas
qu'il s'agit d'une exception capturée ou pas
* utilité : désallouer des ressources allouées, etc.
*/
[finally{/*Bloc N+1*/ }]
...
}

2.7.3.2.7 L’interface AutoCloseable


1. package : java.lang (importée automatiquement)

59
2. description : une interface spécifiant les méthodes à implémenter
par des classes désignant des ressources en Java qui souhaitent être
refermées automatiquement et proprement en cas d’erreur.
3. possède une seule méthode :
/*
* description : appelée automatiquement à la fin d'un bloc try
sur chaque ressource pouvant lever une exception de type Exception
*/
void close() throws Exception;

2.7.3.2.8 try avec ressources rattachées (Depuis Java 7)


1. description : une structure de contrôle try auquelle est rattachée
des ressources qui seront automatiquement fermées à la fin nor-
male/anormale du bloc try.
2. syntaxe :
try(expressionDeclarationRessource1[; expressionDeclarationRessource2][; ...]){

} catch(){/*code*/ }
...
3. remarques :
• l’ordre de fermeture des ressources déclarées est inverse à celui
de leur déclaration dans l’expression attachée à l’instruction
try.
• les ressources instanciées d’une manière imbriquée au sein
d’autres ressources (e.g. FileInputStream et BufferedInputStream)
ne sont pas fermées. Il faut les instancier à part avant de les
passer comme arguments aux autres ressources, afin d’être
fermées.

2.7.3.2.9 capture de plusieurs exceptions par la même clause catch


(Depuis Java 7)
description : les classes d’exception sont examinées par ordre de leur
déclaration jusqu’à trouver une classe désignant celle de l’instance de
l’exception levée ou l’une de ses superclasses dans l’hiérarchie d’héritage
des exceptions.
try{

} catch(Exception1 | Exception2 | ... | ExceptionN e){


/*code*/
}

60
2.7.4 Bonnes pratiques

1. il y a un certain recouvrement entre ce qu’on peut traiter avec les


exceptions et ce qu’on peut traiter avec les assertions (choix de con-
ception et de programmation).
2. utilisation des assertions :
• lors de la mise au point et débogage d’un programme ;
• exprimer les propriétés :
1. invariants de classe ;
2. invariants d’algorithmes, de flux ;
3. axiomes de types abstraits de données ;
4. post-conditions.
3. utilisation des exceptions :
• signaler les erreurs pouvant se produire pendant l’exécution
d’un programme et les capturer.
• revenir à un fonctionnement normal ou arrêter le programme
proprement afin d’éviter les arrêts brutaux d’un programme.
• exprimer les propriétés :
1. pré-conditions : en particulier les paramètres d’une méth-
ode.
2. quelques post-conditions : résultats d’interactions avec
l’utilisateur.
3. parfois des invariants de classes.
4. il faut opter souvent pour une conception hiérarchisée des exceptions
pour avoir une meilleure gestion des erreurs.

2.8 Les énumérations

2.8.1 Introduction

1. définition : un type de données dont on peut énumérer toutes les


valeurs possibles.
2. exemple : Civilité = {Mr, Mme, Mlle} est une énumération.
3. utilité :
• réduire les erreurs de type : une méthode qui attend un type
d’énumération ne peut prendre aucune autre valeur que celles
définies par l’énumération.
• meilleure organisation du code.

2.8.2 Énumérations en Java (Depuis Java 5)

1. toutes les énumérations héritent de la classe java.lang.Enum<E>, elle-


même héritant de la classe java.lang.Object.

61
2. les valeurs des énumérations sont des objets et s’utilisant comme des
attributs publiques statiques et constants.
3. conventions de nommage :
• des énumérations : idem que les classes.
• des valeurs des énumérations : /^([A-Z]_)+$/.
// définition d'une énumérations
visibilite enum NomEnum {
VALEUR1,
VALEUR2,
...
VALEURN
}

// méthodes
/*
* description : retourne l'ensemble des valeurs stockées dans l'énumération
sous forme d'un tableau d'éléments
*/
public static T[] values;

/*
* description : le nom de la valeur de l'énumération
*/
public String toString();
4. remarques :
• possibilité d’ajouter des attributs, des constructeurs et des
méthodes pour personnaliser la création des valeurs de
l’énumération et leurs comportements ;
• le constructeur n’aura aucun modificateur de visibilité et sera
déclaré privé par défaut pour préserver les valeurs définies
dans l’énumération.

2.8.3 Exemple

// l'énumération Langage
package generic.test;

public enum Langage {

/*values*/
JAVA("Langage JAVA", "Eclipse"),
C("Langage C", "Code Blocks"),
C_PLUS_PLUS("Langage CPlusPlus", "Visual Code"),
PHP("Langage PHP", "Visual Studio");

62
/*attributes*/
private String name = "";
private String editor = "";

/*constructor*/
Langage(String name, String editor){
this.name = name;
this.editor = editor;
}

/*methods*/
public void getEditor(){
System.out.println("Editeur " + this.editor);
}

//toString()
@Override
public String toString(){
return this.name;
}
}

// Classe de test
package generic.test;

public class Test {


public static void main(String[] args) {
for (Langage langage : Langage.values()){
if(langage.equals(Langage.JAVA))
System.out.println("J'aime le " + langage);
else
System.out.println(langage);
}
}
}

2.9 Les annotations

2.9.1 Introduction

1. définition :
• les annotations sont des méta-données relatives à un pro-
gramme.

63
• méta-données : des informations qui peuvent être associées à des
éléments de code : classes, interfaces, variables, méthodes, pack-
ages
2. utilités :
• à la compilation ou au moment du déploiment :
1. le compilateur s’en sert pour détecter des erreurs et/ou pour
supprimer des warnings.
2. l’outil JDK javadoc s’en sert pour générer de la documen-
tation.
3. possibilité d’exploiter les annotations pour générer du
code, des fichiers XML de documentation, vérifier des
propriétés, etc.
• à l’exécution : certaines annotations peuvent être examinées
par introspection (cf. La réflexivité)

2.9.2 Définition d’une annotation

1. définir une annotation consiste à définir une interface avec une syn-
taxe particulière :
• préfixer interface par @ ;
• définir des méthodes :
1. définissant des sortes d’attributs avec possiblement des
valeurs par défaut ;
2. sans paramètres ;
3. sans une clause throws ;
4. ayant un type de retour vérifiant l’expression régulière :
/^(<type_primitif>|<type_complexe>|<enumeration>|annotation)(\[\])?$
2. syntaxe :
// définition d'une annotation
visibilite @interface NomAnnotation{
typeRetour1 methodeAnnotation1()[ default valeurDefaut1]
typeRetour2 methodeAnnotation2()[ default valeurDefaut2]
...
typeRetourN methodeAnnotationN()[ default valeurDefautN]
}
3. remarque : si l’annotation ne contient qu’une seule méthode, on
peut l’appeler value() ce qui simplifiera l’écriture des valeurs lors
de l’utilisation de l’annotation.

2.9.3 Composition d’annotations

1. problème : on ne peut pas avoir une relation d’héritage entre les


annotations.

64
2. solution : composition d’annotations : une annotation peut se
composer d’autres annotations.

2.9.4 Annoter un élément de code

1. mode d’utilisation :
• placer l’annotation juste au dessus de l’élément concerné ;
• affecter une valeur à chaque méthode de l’annotation en sup-
primant ses parenthèses ;
• possiblement :
1. n’affecter aucune valeur à une méthode d’annotation pos-
sédant une valeur par défaut ;
2. omettre le nom de la méthode lors de l’affectation d’une
valeur si l’annotation ne contient qu’une seule méthode
nommée value().
2. syntaxe :
@<annotation>(
//affectation d'une valeur à toute méthode de l'annotation
methodeAnnotation1 = valeur1,
methodeAnnotation2 = valeur2,
...
methodeAnnotationN = valeurN
//éventuellement affecter une valeur à une méthode ayant une valeur par défaut
[, methodeAnnotationParDefaut1 = nouvelleValeur]
[, ...]
//éventuellement affecter une annotation à une méthode ayant
//comme type de retour annotation en adoptant le même mode d'utilisation
//ci-dessus pour affecter les valeurs à ses méthodes
[, methodeAnnotationRetourAnnotation = NomAnnotation(/*affectation de valeurs*/ )]
[, ...]
)
element[{/*code*/ }]
3. remarques :
• si un élément est annoté par plusieurs annotations, l’ordre de
leur utilisation n’est pas important.
• si une annotation est composée d’autres annotations, le mode
d’utilisation ci-dessus des annotations s’utilise récursivement
sur chacune d’elles (i.e. affecter une valeur à chaque méthode de
chaque annotation composante).
• si l’élément annoté désigne un package il faut :
1. créer un fichier package-info.java, placé automatiquement à
la racine du package à annoter ;
2. au sein du fichier créé, préfixer l’instruction package
nom.package; par le mode d’utilisation des annotations

65
ci-dessus pour annoter le package.

2.9.5 Annotations de l’API Java

2.9.5.1 L’interface Annotation


1. package : java.lang.annotation (à importer manuellement)
2. description : une interface fournissant les méthodes à implémenter
par toutes les classes désignant des annotations
3. quelques méthodes :
/*
* description : retourne le type à l'exécution de l'annotation
*/
Class<? extends Annotation> annotationType();

/*
* description : retourne true si l'annotation courante est
logiquement équivalent à l'annotation désigné par "obj",
false sinon
*/
boolean equals(Object obj);

/*
* description : retourne le code de hachage de l'annotation courante
*/
int hashCode();

/*
* description : retourne une description en String de l'annotation courante
*/
String toString();

2.9.5.2 L’annotation @Deprecated


1. package : java.lang (importée automatiquement)
2. description : annoter un élément pour indiquer qu’il est obsolète.
3. éléments pouvant être annotés : constructeurs, attributs, variables
locales, méthodes, packages, paramètres, et classes.
4. effet : apparition d’un warning à l’exécution.

2.9.5.3 L’annotation @Override


1. package : java.lang (importée automatiquement)
2. description : annoter une méthode pour indiquer qu’elle est héritée
et redéfinie dans la classe courante.

66
3. effet : à la compilation :
• le compilateur vérifie qu’une méthode de signature compatible
(au sens de la redéfinition) existe dans une superclasse de la classe
courante.
• si la méthode annotée n’est héritée d’aucune superclasse, alors
on aura une erreur de compilation.
• si la méthode annotée est bien héritée mais n’a pas une signa-
ture compatible alors on aura une erreur de compilation.

2.9.5.4 L’annotation @SuppressWarnings


1. package : java.lang (importée automatiquement)
2. description : annoter un élément pour supprimer les warnings du
compilateur qui lui sont associées.
3. éléments pouvant être annotés : constructeurs, attributs, variables
locales, méthodes, paramètres et classes.
4. méthode :
/*
* description : lors de l'utilisation de cette méthode d'annotation,
il faut affecter les strings désignant les warnings du compilateur
qu'on souhaite supprimer associées à l'élément annoté
*/
public abstract String[] value();
5. effet : disparition à la compilation des warnings spécifiées du com-
pilateur, associés à l’élément annoté.

2.9.6 Méta-annotations ou Annotations d’annotations

1. définition : une annotation qui annote une autre annotation.


2. package : toutes les méta-annotations sont définies au sein du pack-
age java.lang.annotation (importée automatiquement).

2.9.6.1 La méta-annotation @Inherited


1. package : java.lang.annotation (importée automatiquement)
2. description : l’annotation A portée par @Inherited sera héritée par
tous les sous-concepts héritant l’élément annoté par A.
3. effet : à l’exécution.

2.9.6.2 La méta-annotation @Documented


1. package : java.lang.annotation (importée automatiquement)

67
2. description : l’annotation A portée par @Documented apparaîtra
dans la documentation générée par les outils de génération de doc-
umentation tels que Javadoc.
3. effet : à l’exécution.

2.9.6.3 La méta-annotation @Retention


1. package : java.lang.annotation (importée automatiquement)
2. description : indiquer la portée et le moment d’utilisation de
l’annotation A portée par @Retention.
3. méthode :
/*
* description : lors de l'utilisation de cette méthode d'annotation,
il faut affecter la valeur énumérée souhaitée de RetentionPolicy
pour indiquer la portée et le moment d'utilisation de
l'annotation annotée
*/
public abstract RetentionPolicy value();
4. effet : à l’exécution.

2.9.6.4 L’énumération RetentionPolicy


1. package : java.lang.annotation (importée automatiquement)
2. description : un type énuméré désignant la portée et le moment
d’utilisation d’une annotation annotée par @Retention.
3. valeurs :
• CLASS : l’annotation annotée par @Retention est compilée en
bytecode mais non prise en compte par la JVM à l’exécution.
(valeur par défaut)
• SOURCE : l’annotation annotée par @Retention n’est pas com-
pilée en bytecode (e.g. les annotations @Override, @SuppressWarnings,
etc.)
• RUNTIME : l’annotation annotée par @Retention est compilée en
bytecode et prise en compte par la JVM à l’exécution pour as-
surer la réflexivité (cf. La réflexivité).

2.9.6.5 La méta-annotation @Target


1. package : java.lang.annotation (importée automatiquement)
2. description : restreindre la portée de l’annotation A portée par
@Target à un ensemble d’éléments de code bien défini.
3. méthode :
/*
* description : lors de l'utilisation de cette méthode d'annotation,

68
il faut affecter les valeurs énumérées souhaitée de ElementType
pour indiquer les éléments de code qui peuvent être annotés par
l'annotation annotée
*/
public abstract ElementType[] value();
4. effet : à l’exécution.

2.9.6.6 L’énumération ElementType


1. package : java.lang.annotation (importée automatiquement)
2. description : un type énuméré désignant les types d’éléments de
code pouvant être portés par une annotation annotée par @Target.
3. valeurs :
• ANNOTATION_TYPE : l’annotation annotée par @Target est une
méta-annotation.
• TYPE : l’annotation annotée par @Target annote des classes, in-
terfaces, annotations, ou énumérations.
• FIELD : l’annotation annotée par @Target annote des attributs.
• METHOD : l’annotation annotée par @Target annote des méth-
odes.
• CONSTRUCTOR : l’annotation annotée par @Target annote des con-
structeurs.
• LOCAL_VARIABLE : l’annotation annotée par @Target annote des
variables locales.
• PACKAGE : l’annotation annotée par @Target annote des packages.

2.9.6.7 La méta-annotation @Repeatable


1. package : java.lang.annotation (importée automatiquement)
2. description : utiliser l’annotation A portée par @Repeatable
plusieurs fois su l’élément annoté par A.
3. mode d’utilisation :
// définition de l'annotation A que l'on souhaite répéter puis
// annotation de A par @Repeatable en lui affectant le fichier bytecode
// de l'annotation conteneur de A
@Repeatable(Aconteneur.class)
visibilite @interface A{/*code*/ }

// définition de l'annotation conteneur de A


// pour répéter l'utilisation de l'annotation A
visibilite @interface AConteneur{
A[] value();
}

69
// répéter l'annotation d'un élément annoté par A avec des
// valeurs différentes
A(valeur1)
A(valeur2)
...
A(valeurN)
element{/*code*/ }
4. méthode :
/*
* description : lors de l'utilisation de cette méthode d'annotation,
il faut affecter le fichier bytecode de l'annotation conteneur
de l'annotation A que l'on utilisera pour répéter l'utilisation
de l'annotation A sur un élément annoté par cette dernière
*/
public abstract Class<? extends Annotation> value();
5. effet : à l’exécution.

2.9.7 Javadoc

1. définition : un outil de génération de documentation HTML pour


du code source Java, inclus dans tous les JDK et SDK de Java, à
travers des annotations javadoc (javadoc tags) sous forme de blocs de
commentaires particuliers.
2. origines : développé par Sun Microsystems.
3. syntaxe :
/**
* tag_javadoc1
* tag_javadoc2
* ...
* tag_javadocN
*/
element

// remarque : on peut aussi inclure du code HTML dans les commentaires javadoc.

2.9.7.1 Les tags javadoc les plus courantes


/**
* @author nom de l'auteur d'un concept (classes/ interfaces)
* @version numéro de version d'un concept (classes/ interfaces)
* @param nomParametre description d'un paramètre
* d'un(e) méthode/ constructeur
* répété pour chaque paramètre de chaque méthode

70
* mais non renseigné si la méthode n'admet pas de paramètres
* @return description du résultat retourné par
* un(e) méthode/ constructeur non renseigné si la méthode
* n'admet pas de type de retour
* @throws listeClassesException la liste des exceptions transmises par
* un(e) méthode/ constructeur quand elles sont levées, avec une
* description de la raison pour lever l'execption pour chaque exception
* @see Element un lien hypertext vers la documentation
* de l'élément référencé (une classe, méthode, etc.)
* @since classeVersion la version de la classe depuis laquelle
* l'élément documenté existe
* @deprecated indiquer que l'élément documenté est obsolète
* depuis une version spécifique et renseigner les éventuels
* éléments alternatives pouvant être utilisés.
**/

2.9.7.2 Conventions d’écriture des commentaires javadoc


1. commencer par une courte description des éléments documentés
en utilisant des phrases relativement courtes en 3-ième personne ;
2. donner des détails sur les algorithmes des méthodes si nécessaires ;
3. utiliser les tags javadoc @param, @return et @throws systématique-
ment pour les méthodes.

2.10 Les Collections d’objets

2.10.1 Introduction

1. définition : une collection Java est une classe définissant une struc-
ture de données destinée à regrouper plusieurs objets (e.g. une pile,
une file, une liste chaînée, un ensemble, un tableau associatif, etc.)
2. package : toutes les collections sont définies au sein du package
java.util (à importer manuellement).
3. description des méthodes : pour chaque type de collections sont
définies des méthodes permettant de :
• manipuler les éléments stockés : ajout/suppresion d’éléments.
• fournir des informations sur la collection manipulée : taille, si
la collection est vide, etc.
4. hiérarchie des collections :
• des interfaces :
1. rôle :
– fournir des méthodes communes à tous les types de
collection ;
– fournir des méthodes plus spécialisées à des collections
plus spécifiques.

71
2. exemples : java.util.Collection, java.util.Map,
java.util.List, java.util.Set, java.util.SortedSet,
java.util.SortedMap, etc.
• des classes abstraites :
1. rôle :
– factoriser du code ;
– implémenter quelques méthodes de quelques interfaces
qui seront communes à toutes leurs sous-classes con-
crètes ;
– proposer des méthodes abstraites supplémentaires.
2. exemples : java.util.AbstractMap, java.util.AbstractList,
etc.
• des classes concrètes :
1. rôle : implémenter les méthodes abstraites héritées depuis
des classes abstraites ou des interfaces relatives aux collec-
tions.
2. exemples : java.util.Vector, java.util.ArrayList,
java.util.Hashtable, etc.
5. remarque : depuis Java 5, les collections en Java implémentent
l’interface Iterable<T> qui permet de parcourir leurs éléments :
• via des itérateurs (cf. Les itérateurs) ;
• via des boucles for généralisées ou adaptées aux objets
itérables.

Figure 7: “Hiérarchie des collections en Java”

72
2.10.2 L’interface Collection<E>

1. package : java.util (à importer manuellement)


2. description : une interface générique proposant les méthodes à im-
plémenter par n’importe quelle collection d’éléments de type T.
3. quelques méthodes :
/*
* retourne un stream séquentiel sur les éléments de la collection courante
*/
default Stream<E> stream();

/*
* retourne possiblement un stream parallèle sur les éléments de la
collection courante
* remarque : l'API des streams s'occupera de décomposer la requête
pour qu'elle soit exécutée en parallèle si
l'architecture de la machine le permet
*/
default Stream<E> parallelStream();

2.10.3 L’interface List

1. package : java.util (à importer manuellement)


2. description : une interface proposant les méthodes à implémenter par
n’importe quelle liste :
• de taille dynamique ;
• pouvant stocker des valeurs nulles et
• offrant la possibilité d’un accès indexé aux éléments.

2.10.3.1 La classe LinkedList<E>


1. package : java.util (à importer manuellement)
2. description : une classe modélisant une liste doublement chaînée.
3. avantages : efficacité des opérations d’ajout/suppression
d’éléments au milieu de la liste.
4. inconvénients : lourdeur en raison des références sur les éléments
suivant et précédent dans chaque noeud.
5. quelques méthodes :
/*
* description : retourne la taille de la liste
*/
public int size();

/*

73
* description : retourne true si "object" est contenu dans la liste,
false sinon
*/
public boolean contains(Object object);

/*
* description : retourne l'élément à l'index "index" de la liste
si "index" est valide, sinon lève l'exception IndexOutOfBoundException
*/
public E get(int index);

/*
* description : ajoute l'élément "element" à la fin de la liste et
retourne true en cas de succès, false sinon
*/
public boolean add(E element);

/*
* description : supprime l'élément à l'index "index" de la liste
si "index" est valide, sinon lève l'exception IndexOutOfBoundException
*/
public E remove(int index);

/*
* description : vide tout le contenu de la liste
*/
public void clear();

/*
* description : retourne un itérateur de listes sur la liste
*/
public ListIterator<E> listIterator();

2.10.3.2 La classe ArrayList<E>


1. package : java.util (à importer manuellement)
2. description : une classe implantant une liste en tant qu’un tableau
dynamique non thread-safe.
3. avantages :
• pas de limites sur la taille de la liste ;
• possibilité d’ajouter n’importe quelle valeur, y inclus les valeurs
nulles ;
• efficacité de l’opération d’accès en lecture aux éléments.
4. inconvénients : inefficacité des opérations d’ajout/suppression
d’éléments au milieu de la liste.

74
5. quelques méthodes :
// mêmes méthodes que LinkedList<E>, avec les opérations suivantes

/*
* description : supprime "object" de la liste
s'il est contenu dedans et renvoie true,
sinon renvoie false
*/
public boolean remove(Object object);

/*
* description : retourne true si la liste est vide, false sinon
*/
public boolean isEmpty();

2.10.3.3 La classe Vector<E>


1. package : java.util (à importer manuellement)
2. description : une classe désignant un tableau dynamique et thread-
safe.
3. avantages : idem que ArrayList<E>
4. inconvénients : idem que ArrayList<E>
5. quelques méthodes : idem que ArrayList<E>

2.10.4 L’interface Map

1. package : java.util (à importer manuellement)


2. description : une interface proposant les méthodes à implémenter par
n’importe quel tableau associatif (appelé aussi dictionnaire ou table
de hachage) :
• de taille dynamique ;
• offrant la possibilité d’un accès par clé aux valeurs ;
• devenant lourds à gérer en grandissant.

2.10.4.1 L’interface Map.Entry<K, V>


1. package : java.util (à importer manuellement)
2. description : une interface imbriquée dans l’interface Map, offrant les
méthodes à implémenter par une classe modélisant une entrée d’un
dictionnaire (i.e. une paire clé/valeur).
3. quelques méthodes :
/*
* description : retourne la clé de la paire
*/

75
K getKey();

/*
* description : retourne la valeur de la paire
*/
V getValue();

/*
* description : écrase la valeur ancienne "old" de la paire par "value"
et retourne "old"
*/
V setValue(V value);

2.10.4.2 La classe Hashtable<K, V>


1. package : java.util (à importer manuellement)
2. description : une classe implémentant un dictionnaire thread-safe
n’acceptant pas de clés/valeurs nulles.
3. quelques méthodes :
/*
* description : retourne la taille du dictionnaire
*/
public int size();

/*
* description : retourne true si le dictionnaire est vide, false sinon
*/
public boolean isEmpty();

/*
* description : retourne true si "key" est une clé du dictionnaire,
false sinon
*/
public boolean containsKey(Object key);

/*
* description : retourne true si "value" est une valeur associée
à une/plusieurs clés du dictionnaire, false sinon
*/
public boolean containsValue(Object value);

/*
* description : retourne la valeur indexée par la clé "key" dans le
dictionnaire, si "key" est une clé du dictionnaire,
sinon retourne null

76
*/
public V get(Object key);

/*
* description :
- si "key" est déjà une clé du dictionnaire,
écrase la valeur déjà présente par "value" et retourne la valeur écrasée
- sinon, ajoute la paire "key"/"value" au dictionnaire et retourne null
*/
public V put(K key, V value);

/*
* description :
- si "key" est une clé du dictionnaire, la supprime et
retourne la valeur qu'elle indexe
- sinon retourne null
*/
public V remove(Object key);

/*
* description : vide tout le contenu du dictionnaire
*/
public void clear();

/*
* description : retourne une énumération des clés du dictionnaire
*/
public Enumeration<K> keys();

/*
* description : retourne une énumération des valeurs indexées du dictionnaire
*/
public Enumeration<V> elements();

/*
* description : retourne une collection des valeurs indexées du dictionnaire
*/
public Collection<V> values();

2.10.4.3 La classe HashMap<K, V>


1. package : java.util (à importer manuellement)
2. description : une classe implémentant un dictionnaire non thread-safe
mais acceptant des clés/valeurs nulles.
3. quelques méthodes : idem que Hashtable<K, V>.

77
2.10.5 L’interface Set

1. package : java.util (à importer manuellement)


2. description : une interface proposant les méthodes à implémenter par
n’importe quelle ensemble (i.e. une collection n’acceptant pas les valeurs
dupliquées, y compris les valeurs nulles) :
• permettant de manipuler une grande quantité de données.
• inefficacité des opérations d’insertion.

2.10.5.1 La classe HashSet<E>


1. package : java.util (à importer manuellement)
2. description : une classe implantant un ensemble en utilisant un
HashMap.
3. avantages :
• efficacité des opérations de base (ajout, suppression, taille,
recherche) ;
• acceptation des valeurs nulles.
4. inconvénients : non thread-safe.
5. quelques méthodes :
/*
* description : retourne la taille de l'ensemble
*/
public int size();

/*
* description : retourne true si l'ensemble est vide, false sinon
*/
public boolean isEmpty();

/*
* description : retourne true si "object" est contenu dans l'ensemble,
false sinon
*/
public boolean contains(Object object);

/*
* description : ajoute l'élément "element" à l'ensemble et
retourne true si l'ensemble contient déjà l'élément, false sinon
*/
public boolean add(E element);

/*
* description : supprime "object" de l'ensemble
s'il est contenu dedans et renvoie true,

78
sinon renvoie false
*/
public boolean remove(Object object);

/*
* description : vide tout le contenu de l'ensemble
*/
public void clear();

/*
* description : retourne un itérateur sur l'ensemble
*/
public Iterator<E> iterator();

/*
* description : renvoie un tableau contenant les éléments
de l'ensemble
*/
public Object[] toArray();

2.10.6 Associations UML en Java

1. on ne traduit que les extrémités navigables.


2. multiplicité :
• � 1 => attribut en Java.
• > 1 => collections de l’API Java (listes, tableaux, ensembles, etc.)
3. association bidirectionnelle => faire la mise à jour des deux côtés.
4. composition => respecter la contrainte d’exclusivité entre com-
posite et composants.
5. classe d’association ou association n-aire => implémentation en
tant qu’une classes normale.

2.11 Les itérateurs

2.11.1 Introduction

1. itérateur : un objet permettant de parcourir les éléments d’une col-


lection ou plus généralement d’un objet complexe agrégat (e.g. col-
lection, objet composite, etc.) sans exposer les détails de son implé-
mentation, en utilisant une sorte de curseur implicite.
2. itérable : un objet dont les éléments peuvent être parcouru par un
itérateur.
3. origines : le patron de conception “Iterator”.
4. avantages :

79
• abstraction des détails d’implémentation du parcours d’un ob-
jet itérable par itérateur ;
• la structure interne de l’objet itérable peut changer, sans af-
fecter son parcours via un itérateur.
5. approche :
• l’objet itérable donne accès à un/plusieurs itérateurs via des
méthodes ;
• chaque itérateur possède des méthodes divers permettant de ma-
nipuler les éléments d’un objet itérable telles que :
1. savoir le premier élément, l’élément courant ou le prochain élé-
ment ;
2. savoir s’il existe un prochain élément ;
3. détruire l’élément courant ;
4. etc.
6. remarque : toutes les collections sont itérables.

Figure 8: “Le design pattern Iterator”

2.11.2 Les itérateurs et itérables de l’API Java

2.11.2.1 L’interface Iterator<E>


1. package : java.util (à importer manuellement)

80
2. description : une interface proposant les méthodes à implémenter par
n’importe quelle itérateur, paramétrée par le type des objets à itérer.
3. méthodes :
public interface Iterator<E>{
/*méthodes abstraites*/

/*
* description : renvoie true s'il existe encore des éléments à parcourir,
false sinon
*/
boolean hastNext();

/*
* description : renvoie le prochain élément à parcourir s'il en existe
sinon, lève l'exception NoSuchElementException
*/
E next(); //renvoie le prochain élément itéré

/*méthodes par défaut*/

/*
* description : supprimer le dernier élément parcouru par l'itérateur
*/
default void remove();

/*
* description : appliquer le traitement "action" sur chaque élément
qu'il reste à parcourir jusqu'à la fin du parcours
ou jusqu'à ce que "action" lève une exception
*/
default void forEachRemaining(Consumer<? super E> action){
while(this.hastNext())
action.accept(this.next());
}
}

2.11.2.2 L’interface Iterable<T>


1. package : java.lang (importée automatiquement)
2. description :
• une interface proposant les méthodes à implémenter par n’importe
quelle classe modélisant des objets itérables, paramétrée par le
type des éléments à retourner par un itérateur.
• tout objet itérable modélisé par une classe implémentant
l’interface pourra être parcouru par une boucle for adaptée

81
aux objets itérables, utilisant implicitement un itérateur
généré automatiquement pour ce faire.
3. méthodes :
public interface Iterable<T>{
/*méthodes abstraites*/

/*
* description : retourner un itérateur sur l'objet itérable
*/
Iterator<T> iterator();

/*méthodes par défaut*/

/*
* description : appliquer le traitement "action" sur chaque élément
jusqu'à la fin du parcours ou jusqu'à ce que "action" lève une exception
*/
default void forEach(Consumer<? super E> action){
for (E element : this)
action.accept(element);
}

/*
* description : retourner un "spliterator" sur
les éléments de l'objet itérable
*/
default SplIterator<E> spliterator();
}
4. remarques :
• il est conseillé de supprimer un élément d’une collection
itérable pendant son parcours via la méthode remove()
de Iterator<E> et non pas via la méthode remove() de
Collection<E> qui pourra provoquer un comportement in-
défini.
• toutes les modifications nécessaires pour s’adapter à la sup-
pression d’un élément d’une collection itérable pendant son
parcours (i.e. changement de la taille de la liste, changement des in-
dexes, etc.) par la méthode remove() de Iterator<E> sont prises
en compte automatiquement par l’itérateur.

2.11.2.3 L’interface ListIterator<E>


1. package : java.util (à importer manuellement)
2. description : une interface spécialisant Iterator<E>, offrant des

82
méthodes de parcours et de manipulation des éléments d’une liste
itérable plus efficaces et s’appliquant à des endroits précis de la liste.
3. quelques méthodes :
public interface ListIterator<E> extends Iterator<E>{
/*méthodes abstraites*/

/*
* description : renvoie true s'il existe encore des éléments à parcourir,
false sinon
*/
boolean hastNext();

/*
* description : renvoie le prochain élément à parcourir s'il en existe
sinon, lève l'exception NoSuchElementException
*/
E next(); //renvoie le prochain élément itéré

/*
* description : renvoie l'index du prochain élément à parcourir
s'il en existe, sinon retourne la taille de la liste
*/
int nextIndex();

/*
* description : renvoie true s'il existe encore des éléments à parcourir
dans le sens inverse, false sinon
*/
boolean hasPrevious();

/*
* description : renvoie le prochain élément à parcourir dans le sens inverse
s'il en existe, sinon lève l'exception NoSuchElementException
*/
E previous();

/*
* description : renvoie l'index du prochain élément à parcourir
dans le sens inverse, s'il en existe,
sinon retourne -1
*/
int previousIndex();

/*
* description : supprimer le dernier élément parcouru par l'itérateur

83
*/
void remove();

/*
* description : remplace le dernier élément parcouru par l'itérateur
par "object"
*/
void set(E object);

/*
* description :
- si l'élément suivant à parcourir est retourné par next(),
insère "object" juste avant l'élément
- si l'élément suivant à parcourir est retourné par previous(),
insère "object" juste après l'élément
- si la liste est vide, insère l'élément juste avant le curseur
implicite utilisé par l'itérateur
*/
void add(E object);
}

2.11.2.4 L’interface Enumeration<E>


1. package : java.util (à importer manuellement)
2. description : une interface offrant des méthodes permettant
d’énumérer les éléments de n’importe quelle classe conteneur souhai-
tant l’implémenter, mais contraitement à un itérateur, ne permet pas
de supprimer les éléments pendant leur énumération.
3. méthodes :
/*
* description : retourne true s'il existe un prochain élément à énumérer
dans l'énumération
*/
boolean hasMoreElements()

/*
* description : retourne le prochain élément à énumérer, s'il existe,
sinon lève l'exception NoSuchElementException
*/
E nextElement();

84
2.11.3 Exemple

// La classe BonbonPez
package iterators;

public class BonbonPez {


/*attributes*/
private String marque;
private double prix;

/*constructors*/
public BonbonPez() {}
public BonbonPez(String marque, double prix) {
this.marque = marque;
this.prix = prix;
}

/*methods*/
//Getters & setters
public String getMarque() {return this.marque;}
public void setMarque(String marque) {this.marque = marque;}

public double getPrix() {return this.prix;}


public void setPrix(double prix) {this.prix = prix;}

//toString
@Override
public String toString() {
return this.marque+" ("+this.prix+")";
}
}

// La classe DistributeurBonbonPez
package iterators;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class DistributeurBonbonPez implements Iterable<BonbonPez> {


/*attributes*/
private String couleur = "Pink";
private String formeBouchon = "Minnie";
private BonbonPez[] tube = new BonbonPez[12];

85
/*methods*/
public void remplit(HashMap<String, Double> bonbons) {
int i = 0;
for (Map.Entry<String, Double> entry: bonbons.entrySet()) {
if (i < 12) {
tube[i] = new BonbonPez(entry.getKey(), entry.getValue());
i++;
}
}
}

//implemented Iterable<BonbonPez> methods


@Override
public Iterator<BonbonPez> iterator() {
return new IterateurPez(tube);
}

//toString
@Override
public String toString() {
String output = "";
for (BonbonPez bonbon : this.tube)
output += bonbon + " | ";

return output;
}
}

// La classe IterateurPez
package iterators;

import java.util.Iterator;

public class IterateurPez implements Iterator<BonbonPez> {


/*attributes*/
private BonbonPez[] tube;
private int curseur = 0;

/*constructors*/
public IterateurPez(BonbonPez[] tube) {
this.tube = tube;
}

/*methods*/
//implemented Iterator<BonbonPez> methods
@Override

86
public boolean hasNext() {
return curseur < 12;
}

@Override
public BonbonPez next() {
BonbonPez bonbon = tube[curseur];
tube[curseur] = null;
curseur++;

return bonbon;
}
}

// La classe Test
package iterators;

import java.util.HashMap;

public class Test {


public static void main(String[] args) {
HashMap<String, Double> bonbons = new HashMap<>();

bonbons.put("Chocolat", 1.0);
bonbons.put("Framboise", 2.0);
bonbons.put("Cerise", 2.0);
bonbons.put("Vanille", 1.0);
bonbons.put("Menthe", 2.5);
bonbons.put("Ananas", 1.5);
bonbons.put("Pomme", 1.5);
bonbons.put("Lait", 2.0);
bonbons.put("Fraise", 1.5);
bonbons.put("Banane", 1.0);
bonbons.put("Figue", 1.8);
bonbons.put("Citron", 2.0);

DistributeurBonbonPez dist = new DistributeurBonbonPez();


dist.remplit(bonbons);
System.out.println("Avant tout retrait : " + dist);
System.out.println();

for (BonbonPez bonbon: dist) {


System.out.println("Bonbon obtenu : " + bonbon);
System.out.println("Après retrait : " + dist);
System.out.println();
}

87
}
}

2.12 Tri, comparaison, et clonage d’objets

2.12.1 Tri et Comparaison

2.12.1.1 L’interface Comparable<T>


1. package : java.lang (importée automatiquement)
2. description : interface à implémenter par n’importe quelle classe
souhaitant définir un ordre naturel sur ses instances.
3. méthode :
/*
* description : compare l'objet appelant à l'objet de type T "o"
retourne :
- -1 : si this < o
- 0 : si this = o
- 1 : si this > o
* à redéfinir par toute classe souhaitant
définir un ordre naturel sur ses instances
*/
int compareTo(T o);

2.12.1.2 L’interface Comparator<T>


1. package : java.util (à importer manuellement)
2. description : une interface fournissant les méthodes à implémenter
par n’importe quelle classe jouant le rôle d’un comparateur (i.e. une
classe utilisée pour comparer les objets d’une autre classe selon un ordre
qu’elle spécifie).
3. quelques méthodes :
/*
* description : compare les objets o1 et o2 et
retourne :
- -1 : si this < o
- 0 : si this = o
- 1 : si this > o
* à redéfinir par toute classe souhaitant
définir un ordre sur les instances
d'une classe de type T
*/
public int compare(T o1, T o2)

88
2.12.1.3 La classe Collections
1. package : java.util (à importer manuellement)
2. description : une classe fournissant des méthodes statiques pour
manipuler des collections d’objets.
3. quelques méthodes :
/*
* description : méthode statique permettant de trier une liste
selon l'ordre naturel défini sur ses éléments de type T
(i.e. l'ordre défini via la méthode "compareTo()"
de l'interface "Comparable<T>")
*/
public static <T extends Comparable<? super T>> void sort(List<T> list);

/*
* description : méthode statique permettant de trier une liste
selon l'ordre défini par le comparateur "c" sur ses éléments de type T
(i.e. l'ordre défini via la méthode "compare()"
de l'interface "Comparator<T>")
*/
public static <T> void sort(List<T> list, Comparator<? super T> c);

2.12.2 Clonage

2.12.2.1 Interfaces marqueurs


1. définition : une interface vide (i.e. ne contenant ni attributs ni
méthodes ni types internes).
2. utilité : préciser la sémantique d’une classe l’implémentant et le
contexte d’utilisation de ses objets.

2.12.2.2 L’interface Cloneable


1. package : java.lang (importée automatiquement)
2. description : interface marqueur indiquant que les objets d’une
classe l’implémentant peuvent être clonés.
3. clonage : copier le contenu d’un objet dans un autre objet ayant un
espace mémoire distinct.
4. utilisation : redéfinir la méthode protected Object clone() de la
classe java.lang.Object dans la classe implémentant Cloneable en
changeant sa visibilité à public, afin de définir les critères du clonage.

89
2.13 La généricité

2.13.1 Introduction

1. généricité : la généricité (appelée aussi le polymorphisme


paramétrique) consiste à définir des modèles de concepts/méthodes
(appelés aussi des templates ou des concepts/méthodes génériques)
dans lesquels certains paramétres restent formels (i.e. quelles que
soient les valeurs réelles liées aux paramètres formels, le traitement global
reste identique).
2. paramètres formels : les types génériques d’un modèle générique
désignant des types complexes et ne pouvant désigner des types
primitifs.
3. {instanciation ou spécialisation | invocation} d’un(e) {concept |
méthode} générique :
• définition : lier les paramétres formels d’un modèle générique
à des paramètres réels afin d’obtenir un modèle concret.
• remarque : à la compilation, si un modèle générique est instan-
cié par un type réel non respectant les contraintes de liaison
spécifiées, il y aura une erreur de compilation.
4. avantages :
• économiser l’écriture du code.
• réduire considérablement les erreurs liées aux :
1. contrôle des types à l’exécution (i.e. tests de type)
2. coercitions (i.e. transtypage)
5. exemple de langages supportant la généricité :

Langage Type Remarques


Ada Programmation -
Eiffel Programmation -
C++ Programmation -
Java Programmation depuis Java 5
Haskell Programmation -
UML Modélisation -

6. exemples :
• classe générique : pile paramétrée par le type T des éléments
stockés (Integer, String, Voiture, etc.) : Pile<T>.
• méthode générique : fonction de recherche paramétrée par
le type T des éléments à rechercher dans un tableau : boolean
recherche(T[] tableau, int taille, T element).

2.13.1.1 Historique de la généricité en Java


1. approche avant Java 5 :

90
• principe : une stratégie “homogène” pour représenter la
généricité en utilisant une instance de la classe Object pour
désigner n’importe quel type possible et paramétrer le
concept ou la méthode.
• inconvénients :
1. faire des tests de type :
– code non extensible, vu qu’il faut faire un test de type
pour chaque type possible ;
– difficulté de contrôler les types.
2. faire des coercitions de type pour invoquer des méthodes
spécifiques au type réel sur un objet générique de type
Object.
2. approche depuis Java 5 :
• principe : une stratégie “hétérogène” pour représenter la
généricité en utilisant un type formel qui peut être lié à un
type réel parmis une gamme de types réels existants (ou
à venir) bien définie par des contraintes de liaison, et qui peut
paramétrer un concept ou une méthode.
• avantages :
1. permettre la généricité des classes, interfaces et méthodes
(d’instance ou de classe) ;
2. rendre l’API des collections générique ;
3. exemple :
/* instanciation d'une pile d'entiers, en utilisant le type complex "Integer"
* et non pas le type primitif "int"
*/
- Pile<Integer> pile = new Pile<Integer>();

/*
* (Depuis Java 7)
* syntaxe alternative en utilisant l'inférence automatique du type
* concret lié au paramètre formel d'un modèle générique
*/
- Pile<Integer> pile = new Pile<>();

2.13.1.2 Autoboxing en Java


1. classe wrapper : une classe de type complex permettant d’ajouter
à une classe de type primitif équivalente :
• les méthodes de la classe Object (e.g. equals(), hashCode(),
getClass(), etc.) ;
• les méthodes permettant de caster leurs valeurs.
2. depuis Java 5 : le autoboxing est introduit et permet les transforma-
tions automatiques suivantes :
• boxing : un type primitif en une classe wrapper équivalente ;

91
• unboxing : une classe wrapper en un type primitif équivalent.
3. exemple :
int i = new Integer(12); // \equiv int i = 12; (unboxing)
double d = new Double(12.25); // \equiv double d = 12.25 (unboxing)
Double D = 12.0; // \equiv Double D = new Double(12.0) (boxing)
Character C = 'C'; // \equiv Character = new Character('C') (boxing)

2.13.2 Paramétrage des propriétés d’une classe générique

Dans une classe générique :


1. les paramètres génériques portent sur les attributs et méthodes
d’instance génériques uniquement ;
2. les attributs et méthodes génériques ont leurs propres paramètres
génériques (i.e. différents de ceux de la classe générique) ;
3. les attributs et méthodes d’instance génériques peuvent avoir leurs
propres paramètres génériques.

2.13.3 Généricité, héritage et instanciation

1. une classe générique peut hériter :


• d’une classe concrète ;
• d’une autre classe générique ou d’une instanciation partielle
de celle-ci.
2. une classe concrète peut hériter d’une instanciation complète d’une
classe générique.
3. héritage et généricité :
• soit les classes génériques GC et GS ;
• soit la classe concrète CCT obtenue par la liaison du paramètre
générique de GC à un type concret T ;
• soit la classe concrète CST obtenue par la liaison du paramètre
générique de GS au même type concret T ;
• si GC hérite de GS alors CCT hérite de CST.
4. exemples :
// une classe générique héritant d'une classe concrète
public class GrapheEtiquete<TypeEtiquette> extends Graphe{/*code*/ }

// une classe générique héritant d'une autre classe générique


public class TableHash<K, V> extends Dictionnaire<K, V>{/*code*/ }

// une classe générique héritant d'une classe générique partiellement instanciée


public class Agenda<TypeEvent> extends Dictionnaire<Date, TypeEvent>{/*code*/ }

92
Figure 9: “Généricité et Héritage en Java”

// une classe concrète héritant d'une instanciation complète d'une classe générique
public class Agenda extends Dictionnaire<Date, String>{/*code*/ }

// exemples de l'API de Java


public Collection<E> extends Iterable<E>{/*code*/ }
public Vector<E> extends AbstractList<E>{/*code*/ }
public HashMap<K, V> extends AbstractMap<K, V>{/*code*/ }

2.13.4 Généricité et sous-typage

1. principe :
• soient la classe générique Classe<T>, les classes concrètes A, B
, et les classes concrètes Classe<B> et Classe<A> obtenues par
l’instanciation de Classe<T> en liant le paramètre générique T
respectivement aux types concrets A et B.
• en supposant que B hérite de A, ceci n’implique pas que Classe<B>
hérite de Classe<A>.
2. justification : deux classes concrètes obtenues par l’instanciation
complète d’une même classe générique sont indépendantes du point
de vue de typage, quelles que soient les relations (e.g. héritage) qui
existent entre les types de leurs paramètres réels liés au paramètre
générique de la classe générique instanciée.

2.13.5 Généricité bornée

2.13.5.1 Introduction

93
Figure 10: “Généricité et Sous-typage en Java”

1. besoin : lors de la définition d’une classe générique, on attend parfois


que le type générique respecte certaines contraintes :
• les objets du type générique doivent fournir certains services
(méthodes, attributs) ;
• les objets du type générique correspondent à une abstraction, un
rôle, etc.
2. approche : généricité bornée par une/des contrainte(s) de liaison,
où une contrainte de liaison est représentée sous forme d’une expres-
sion contenant des mots-clé spécifiant la relation du type générique
avec des concepts specifiques (i.e. classe, classe abstraite, et interface)

2.13.5.2 Généricité bornée et le mot-clé extends


1. syntaxe :
/*
* - si Concept est une classe [abstraite] alors
* T être Concept ou l'une de ses sous-classes
* - si Concept est une interface, alors T
* doit être Concept ou l'une de ses sous-interfaces
ou toute classe les implémentant
*
* remarque : Concept peut désigner un concept générique dont le type générique
est le même que celui de "ClasseGenerique" (contrainte récursive)
*/
visibilite class ClasseGenerique<T extends Concept>{/*code*/ }

// généralisation de la première syntaxe mais sur plusieurs concepts


visibilite class ClasseGenerique<T extends Concept1 & Concept2 & ...>{/*code*/ }
2. exemples de l’API Java :

94
public interface Comparable<A>{/*code*/ }
public OrderedSet<A extends Comparable<A>>{/*code*/ }

2.13.5.3 Généricité bornée, le caractère wildcard et le mot-clé super

1. besoin : raffiner les contraintes de liaison d’un paramètre


générique lors de :
• l’instanciation d’une classe concrète à partir d’une classe
générique.
• la définition des paramètres ou variables locales d’une méth-
ode générique paramétrée par le type générique de sa classe.
2. approche : utilisation du caractère wildcard ? et/ou le mot-clé super.
3. remarque : les éléments d’une méthode générique raffinée davan-
tage seront vérouillés par la JVM et accessibles en lecture unique-
ment.
4. syntaxe :
/*
* Utilisation 1
*==============
* utilisation de "?" tout seul pour indiquer que n'importe quel
* type concret peut être utilisé
* et qu'il n'a pas besoin d'être explicité
*/

/*
* lors de l'instanciation d'une classe générique
*/
ClasseGenerique<?> cg = new ClasseGenerique<TypeConcret>();

/*
* lors de la définition d'une méthode dont le type générique
* ne figure pas dans son corps
*/
portee [static] typeRetour nomMethode(ClasseGenerique<?,?> instance){
// instructions ne contenant pas le paramètre générique de la méthode
}

/*
*- remarque : avec cette utilisation, on ne peut pas
utiliser des expressions nécessitant de connaître le type du paramètre
générique à la compilation
*/
Paire<?, ?> paire = new Paire<Integer, String>();
// pas d'erreur parce que toString() ne dépend pas du type générique

95
System.out.println(paire);
// erreur de compilation, parce que setFirst() dépend du type générique
paire.setFirst(12);

/*
* Utilisation 2
*==============
* utilisation de "?" avec le mot-clé "extends" pour rendre
* le type générique instanciable
* par n'importe quel type concret et :
1. toutes ses sous-classes (si type concret = classe (abstraite))
2. toutes ses sous-interfaces ou classes l'implémentant
(si type concret = interface)
*/

/*
*lors de l'instanciation d'une classe générique
*/
ClasseGenerique<? extends TypeConcret> instanceConcrete = new ClasseGenerique<>([params]);
/*

/*
* lors de la définition d'une méthode dont le type générique
* ne figure pas dans son corps
*/
visibilite [static] typeRetour nomMethodeGenerique(? extends TypeConcret[, params]){/*code*/

/*
* Utilisation 3
*==============
* utilisation de "?" avec le mot-clé "super" pour rendre
* le type générique d'une classe générique instanciable
* par n'importe quel type concret et :
1. toutes ses superclasses (si type concret = classe (abstraite))
2. toutes ses super-interfaces (si type concret = interface)
*/

/*
*lors de l'instanciation d'une classe générique
*/
ClasseGenerique<? super TypeConcret> instanceConcrete = new ClasseGenerique<>([params]);
/*

/*
* lors de la définition d'une méthode dont le type générique
* ne figure pas dans son corps

96
*/
visibilite [static] typeRetour nomMethodeGenerique(? super TypeConcret[, params]){/*code*/ }
5. exemples de l’API Java :
class AbstractCollection<E>{
...
public boolean addAll(Collection<? extends E> c){/*code*/ }
...
}

class LinkedBlockingQueue<E>{
public int drainTo(Collection<? super E> c){/*code*/ }
}

2.13.6 Le principe de l’effacement de type

1. définition : à partir de Java 5, la généricité est mise en oeuvre via le


principe d’effacement de type qui permet de revenir à la stratégie
homogène de l’implémentation de la généricité en Java.
2. type brut d’une classe générique :
• définition : le type matérialisé par le nom de la classe
générique pourvu de ses paramètres génériques (e.g. ArrayList
l, Collection c, etc.)
• avantage : assurer la rétrocompatibilité (versions de Java <=
4).
• inconvénient : le compilateur ne fera pas de vérification sur
une instance déclarée de type brut d’une classe générique et
on aura un warning.
3. approche : lors de la compilation :
• l’expression de déclaration de généricité et de ses contraintes
liées, circonscrite par <> est supprimée ;
• les occurrences des paramètres génériques dans la définition du
modèle générque en question seront remplacées par les bornes
supérieures définies par les contraintes de liaison correspondant
(Object en abscence de contraintes) ;
• certaines instructions auront besoin d’une coercition de type qui
sera ainsi faite ;
4. conséquences :
• une seule classe (la classe générique) est partagée par toutes
ses instanciations concrètes ;
• les paramètres génériques de la classe générique ne portent pas
sur ses propriétés statiques ;
• une propriété statique est une propriété de la classe générique
et non pas de chacune de ses instanciations concrètes.
5. exemple :

97
// avant l'effacement de type
class Paire<A, B>{
private A first;
private B second;
...
}

Paire<Integer, String> p = new Paire<>(9, "plus grand chiffres");


Integer i = p.getFirst();

// après l'effacement de type


class Paire{
private Object first;
private Object second;
...
}

Paire p = new Paire(9, "plus grand chiffres");


Integer i = (Integer)p.getFirst();

2.13.7 Exemple

// La classe générique Paire<A, B>


package genericity.test;

import java.util.Collection;
import java.util.List;

public class Paire<A,B> {


/*attributes*/
private A first;
private B second;

/*constructors*/
public Paire(){}
public Paire(A first, B second){
this.first = first;
this.second = second;
}

/*methods*/
//getters
public A getFirst(){return first;}
public B getSecond(){return second;}

98
//setters
public void setFirst(A first){this.first = first;}
public void setSecond(B second){this.second = second;}

//toString
@Override
public String toString(){
return "(" + first + ", " + second + ")";
}

//other methods
public static <X> void copyFirstTab(Paire<X, ?> p, X tab[], int i){
if (i>=0 && i<tab.length)
tab[i] = p.getFirst();
}

public boolean memeFirst(Paire<A,?> p){


return this.first.equals(p.getFirst());
}

public void prendreListFirst(List<? extends A> c){


this.setFirst(c.get(0));
}

public void copyFirstColl(Collection<? super A> c){


c.add(this.getFirst());
}
}

// L'interface Saisissable
package genericity.test;

import java.util.Scanner;

public interface Saisissable {


/*abstract methods*/
void saisie(Scanner sc);
}

// La classe StringSaisissable
package genericity.test;

import java.util.Scanner;

public class StringSaisissable implements Saisissable {


/*attributes*/

99
private String string;

/*constructors*/
public StringSaisissable(String string){this.string = string;}

/*methods*/
//toString
@Override
public String toString(){
return this.string;
}

//implemented Saisissable methods


@Override
public void saisie(Scanner sc){
this.string = sc.next();
}
}

// La classe PaireSaisissable
package genericity.test;

import java.util.Scanner;

public class PaireSaisissable<A extends Saisissable, B extends Saisissable>


implements Saisissable{
/*attributes*/
private A first;
private B second;

/*constructors*/
public PaireSaisissable(){}
public PaireSaisissable(A first, B second){
this.first = first;
this.second = second;
}

/*methods*/
//getters
public A getFirst(){return first;}
public B getSecond(){return second;}

//setters
public void setFirst(A first){this.first = first;}
public void setSecond(B second){this.second = second;}

100
//toString
@Override
public String toString(){
return "(" + first + ", " + second + ")";
}

//implemented Saisissable methods


@Override
public void saisie(Scanner sc){
System.out.println("Valeur first : ");
first.saisie(sc);
System.out.println("Valeur second : ");
second.saisie(sc);
}
}

// La classe Test
package generics;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;

public class Test {

public static void main(String[] args){


Paire<Integer, String> p1 = new Paire<>(9, "plus grand chiffre");
System.out.println("first : " + p1.getFirst());
System.out.println("second : " + p1.getSecond());
Paire<Integer, Integer> p2 = new Paire<>(9, 10);

Integer tab[] = new Integer[2];


Paire.copyFirstTab(p1, tab, 0);
System.out.println(p1.sameFirst(p2));

Scanner sc = new Scanner(System.in);


StringSaisissable s1 = new StringSaisissable(""),
s2 = new StringSaisissable("");
PaireSaisissable<StringSaisissable, StringSaisissable> ps =
new PaireSaisissable<>(s1, s2);
ps.saisie(sc);
System.out.println(ps);

Paire<Object, String> p3 = new Paire<>();


List<Object> lo = new LinkedList<>();

101
List<Integer> li = new LinkedList<>();
lo.add(5);
li.add(5);

p3.takeListFirst(lo);

/*
* marche bien parce que Integer est une sous-classe d'Object
* et on a spécifié ? extends A pour le paramètre générique
* utilisé comme paramètre de la méthode prendreListFirst()
*/
p3.takeListFirst(li);
Paire<Integer, Integer> p4 = new Paire<Integer, Integer>(9, 10);
Collection<Object> co = new LinkedList<Object>();

/*
* marche bien parce que Object est une super-classe d'Integer
* et on a specifié ? super A pour le paramètre générique
* utilisé comme paramètre de la méthode copyFirstColl(
*/
p4.copyFirstToCollection(co);
}
}

2.14 Les flux d’I/O

2.14.1 Introduction

1. entrée/sortie ou E/S (input/output ou I/O) : une opération


d’échange de données entre un programme et une autre source de
données (e.g. mémoire, fichier, etc.).
2. flux (stream) : un médiateur entre un programme et une autre source
de données utilisé pour effectuer des E/S.
3. schéma des E/S :
• ouverture d’un flux ;
• opération E/S : lecture/écriture d’/dans un flux ;
• fermeture du flux.
4. catégories des flux :
• in : pour la lecture du flux ;
• out : pour l’écriture dans un flux.

2.14.1.1 Les consoles standards pour les E/S


1. System.in : entrée standard (par défaut, le clavier) ;
2. System.out : sortie standard (par défaut, l’écran) ;

102
3. System.err : sortie standard des erreurs (par défaut, l’écran).

2.14.1.2 La classe Scanner


1. package : java.util (à importer manuellement)
2. description : un scanner permettant de parser des types primitifs et
des strings en utilisant des expressions régulières.
3. quelques méthodes :
/*
* description : récupérer une ligne parsée par le scanner sous forme d'un String
et repositionner la tête de lecture au début de la ligne suivante
*/
public String nextLine();

/*
* description : récupérer la ligne parsée par le scanner sous forme d'un int
sans positionner la tête de lecture au début de la ligne suivante
(doit être suivie d'un appel à nextLine() pour ce faire)
* remarques :
- des méthodes ayant la signature nextType() sont définies pour
tous les autres types primitifs (byte, short, long, float, double, boolean)
sauf "char", qui peut être récupéré à partir de nextLine()
*
*/
public int nextInt();

2.14.1.3 La classe File


1. package : java.io (à importer manuellement)
2. description : une classe modélisant une représentation abstraite
d’un chemin d’un fichier/dossier (UNIX ou Windows) indépendate
de la plateforme utilisée.
3. quelques méthodes :
/*
* description : retourne true si le chemin désigne un fichier qui existe,
false sinon
*/
public boolean exists();

/*
* description : retourne true si le chemin désigne un fichier,
false sinon
*/
public boolean isFile();

103
/*
* description : retourne true si le chemin désigne un dossier,
false sinon
*/
public boolean isDirectory();

/*
* description : retourne le nom du fichier désigné par le chemin
*/
public String getName();

/*
* description : retourne le chemin absolu du fichier désigné par le chemin
*/
public String getAbsolutePath();

/*
* description : retourne le nom du dossier parent du
fichier désigné par le chemin,
*/
public String getParent();

/*
* description : retourne la liste des fichiers/dossiers contenus
dans le dossier désigné par le chemin
*/
public File[] listFiles();

/*
* description : retourne la liste des dossiers racines
du système de fichier de la machine
*/
public static File[] listRoots();

/*
* description : supprime le fichier/dossier désigné par le chemin
et retourne true si le fichier/dossier est supprimé, false sinon
*/
public boolean delete();

/*
* description : créer un dossier désigné par le chemin
et retourne true si le dossier est créé, false sinon
*/
public boolean mkdir();

104
2.14.1.3.1 Exemple
package iostreams;

import java.io.File;

public class Test {

public static void main(String[] args) {


File f = new File("test.txt");
System.out.println("Chemin absolu du fichier : " + f.getAbsolutePath());
System.out.println("Nom du fichier : " + f.getName());
System.out.println("Est-ce qu'il existe ? " + f.exists());
System.out.println("Est-ce un fichier ? " + f.isFile());
System.out.println("Est-ce un dossier ? " + f.isDirectory());

System.out.println("Affichage des lecteurs à la racine du PC : ");


// Parcours des racines du système de fichier
for (File root: File.listRoots()) {
System.out.println(root.getAbsolutePath());

try {
int i = 1;

// Parcours de la liste des fichiers/dossiers d'une racine

for (File file: root.listFiles()) {


System.out.println("\t\t" + file.getName() +
(file.isDirectory()? "/" : ""));

if (i%4 == 0)
System.out.println("\n");

i++;
}
} catch (NullPointerException e) {
continue;
}
}
}
}

2.14.2 Le package java.io

2.14.2.1 Introduction

105
Description : le premier package de Java pour les E/S. Il permet de créer des
flux de lecture/d’écriture d’octets/de caractères pour plusieurs types
de sources de données, et propose des mécanismes permettant de pré-
traiter les données avant d’effectuer les opérations d’E/S.

Figure 11: “Hiérarchie des classes InputStream et OutputStream de java.io en


Java”

2.14.2.2 La classe InputStream


1. package : java.io (à importer manuellement)
2. description : une classe abstraite, superclasse de toutes les classes
modélisant des flux de lecture d’octets depuis une source de données.
3. quelques méthodes :
/*
* description : lecture de b.length octets d'un flux de lecture
et retourner le nombre d'octets lus ou
-1 en cas de fin du stream
*/
public int read(byte[] b) throws IOException;

/*
* description : fermer le flux et libérer les ressources associées
*/
public void close() throws IOException;

106
Figure 12: “Hiérarchie des classes Reader et Writer de java.io en Java”

107
2.14.2.3 La classe OutputStream
1. package : java.io (à importer manuellement)
2. description : une classe abstraite, superclasse de toutes les classes
modélisant des flux d’écriture d’octets dans une destination.
3. quelques méthodes :
/*
* description : écriture de b.length octets dans un flux d'écriture
*/
public void write(byte[] b) throws IOException;

/*
* description : fermer le flux et libérer les ressources associées
*/
public void close() throws IOException;

2.14.2.4 La classe FileInputStream


1. package : java.io (à importer manuellement)
2. description : une classe modélisant des flux de lecture d’octets
depuis des fichiers.
3. quelques méthodes :
/*
* description : récupérer un canal unique sur le flux
*/
public FileChannel getChannel();
4. remarque : si un fichier ouvert est non existant, l’exception
FileNotFoundException sera levée.

2.14.2.5 La classe FileOutputStream


1. package : java.io (à importer manuellement)
2. description : une classe modélisant des flux d’écriture d’octets dans
des fichiers.
3. quelques méthodes :
/*
* description : récupérer un canal unique sur le flux
*/
public FileChannel getChannel();
4. remarque : si un fichier ouvert est non existant, un fichier du nom
indiqué sera créé.

108
2.14.2.5.1 Exemple
package iostreams;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test {

public static void main(String[] args) {

try (FileInputStream fis =


new FileInputStream(new File("test.txt"));
FileOutputStream fos =
new FileOutputStream(new File("test_copy.txt"))) {
// buffer pour la lecture du fichier à chaque itération
byte[] buffer = new byte[8];

while (fis.read(buffer) != -1) {


fos.write(buffer);

for (byte octet: buffer)


System.out.print("\t" + octet + " (" + (char) octet + ")");

System.out.println();
buffer = new byte[8];
}

System.out.println("Copie terminée");

} catch(IOException e) {
e.printStackTrace();
}
}
}

2.14.2.6 La classe FilterInputStream


1. package : java.io (à importer manuellement)
2. description : une classe abstraite, superclasse de toutes les classes
modélisant des fonctionnalités pour traiter et/ou transformer les
octets lues à partir d’un flux de lecture de base encapsulé avant de
les passer au programme.

109
2.14.2.7 La classe FilterOutputStream
1. package : java.io (à importer manuellement)
2. description : une classe abstraite, superclasse de toutes les classes
modélisant des fonctionnalités pour traiter et/ou transformer les
octets à écrire à partir d’un flux d’écriture de base encapsulé avant
de les écrire dans la destination.

2.14.2.8 La classe BufferedInputStream


1. package : java.io (à importer manuellement)
2. description : une classe ajoutant un tampon interne à un flux de
lecture qui sert d’intermédiaire entre le flux et le programme. Lors
de la lecture, plusieurs octets lues sont stockées à la fois dans le
tampon et seront flushées vers le programme une fois le tampon
rempli.

2.14.2.9 La classe BufferedOutputStream


1. package : java.io (à importer manuellement)
2. description : une classe ajoutant un tampon interne à un flux
d’écriture qui sert d’intermédiaire entre le flux et le programme.
Lors de l’écriture, plusieurs octets écrites sont stockées à la fois
dans le tampon et seront flushées vers le stream une fois le tampon
rempli.

2.14.2.9.1 Exemple
package iostreams;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test {

public static void main(String[] args) {

try(FileInputStream fis = new FileInputStream("test.txt");


FileOutputStream fos = new FileOutputStream("test_copy.txt");
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("test.txt"));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("test_copy_buffered.txt"))) {

110
/*préparation d'un buffer pour la lecture*/
byte[] buffer = new byte[8];

/*mesurer le temps d'exécution de FileInputStream*/


long startTime = System.currentTimeMillis();
while(fis.read(buffer) != -1)
fos.write(buffer);

System.out.println("Temps de lecture + écriture d'un fichier " +


" avec FileInputStream et FileOutputStream : " +
(System.currentTimeMillis() - startTime) + " millisecondes");

/*mesurer le temps d'exécution de BufferedInputStream*/


startTime = System.currentTimeMillis();
while(bis.read(buffer) != -1)
bos.write(buffer);
System.out.println("Temps de lecture + écriture d'un fichier " +
" avec BufferedInputStream et BufferedOutputStream : " +
(System.currentTimeMillis() - startTime) + " millisecondes");

} catch (IOException e) {
e.printStackTrace();
}
}
}

2.14.2.10 La classe PushBackInputStream


1. package : java.io (à importer manuellement)
2. description : une classe permettant de remettre un octet déjà lu
dans le flux de lecture encapsulé.

2.14.2.11 La classe DataInputStream


1. package : java.io (à importer manuellement)
2. description : une classe permettant de lire directement des types
primitifs (i.e. byte, short, int, long, float, double, etc.) depuis le
flux de lecture encapsulé.
3. quelques méthodes :
/*
* description : lecture des octets en tant qu'un "boolean"
du flux de lecture
*/
public final boolean readBoolean() throws IOException;

111
/*
* description : lecture des octets en tant qu'un "char"
du flux de lecture
*/
public final char readChar() throws IOException;

/*
* description : lecture des octets en tant qu'un "byte"
du flux de lecture
*/
public final byte readByte() throws IOException;

/*
* description : lecture des octets en tant qu'un "short"
du flux de lecture
*/
public final short readShort() throws IOException;

/*
* description : lecture des octets en tant qu'un "int"
du flux de lecture
*/
public final int readInt() throws IOException;

/*
* description : lecture des octets en tant qu'un "long"
du flux de lecture
*/
public final long readLong() throws IOException;

/*
* description : lecture des octets en tant qu'un "float"
du flux de lecture
*/
public final float readFloat() throws IOException;

/*
* description : lecture des octets en tant qu'un "double"
du flux de lecture
*/
public final int readDouble() throws IOException;

2.14.2.12 La classe DataOutputStream

112
1. package : java.io (à importer manuellement)
2. description : une classe permettant d’écrire directement des types
primitifs dans le flux d’écriture encapsulé.
3. quelques méthodes :
/*
* description : écriture d'un booléen "v" dans le flux d'écriture
*/
public final void writeBoolean(boolean v) throws IOException;

/*
* description : écriture d'un entier "v" en tant que "char"
* dans le flux d'écriture
*/
public final void writeChar(int v) throws IOException;

/*
* description : écriture d'un String "v" dans le flux d'écriture
le flux
*/
public final void writeChars(String v) throws IOException;

/*
* description : écriture d'un entier "v" en tant que "byte"
* dans le flux d'écriture
*/
public final void writeByte(int v) throws IOException;

/*
* description : écriture d'un entier "v" en tant que "short"
* dans le flux d'écriture
*/
public final void writeShort(int v) throws IOException;

/*
* description : écriture d'un entier "v"
* dans le flux d'écriture
*/
public final void writeInt(int v) throws IOException;

/*
* description : écriture d'un long "v"
* dans le flux d'écriture
*/
public final void writeLong(long v) throws IOException;

113
/*
* description : écriture d'un float "v"
* dans le flux d'écriture
*/
public final void writeFloat(float v) throws IOException;

/*
* description : écriture d'un double "v"
* dans le flux d'écriture
*/
public final void writeDouble(int v) throws IOException;

2.14.2.12.1 Exemple
package iostreams;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test {

public static void main(String[] args) {


DataInputStream dis = null;
DataOutputStream dos = null;

try {
//création du fichier et écriture de types primitifs dedans
dos = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("sdz.txt")));
dos.writeBoolean(true);
dos.writeChar('C');
dos.writeByte(100);
dos.writeShort(2);
dos.writeInt(1024);
dos.writeLong(123456789L);
dos.writeFloat(100.52f);
dos.writeDouble(12.05);

dos.close();

114
//lecture des types primitifs du fichier
dis = new DataInputStream(
new BufferedInputStream(
new FileInputStream("sdz.txt")));

System.out.println(dis.readBoolean());
System.out.println(dis.readChar());
System.out.println(dis.readByte());
System.out.println(dis.readShort());
System.out.println(dis.readInt());
System.out.println(dis.readLong());
System.out.println(dis.readFloat());
System.out.println(dis.readDouble());

dis.close();

} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(dis != null)
dis.close();

if(dos != null)
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

2.14.2.13 L’interface Serializable


1. package : java.io (à importer manuellement)
2. description : interface marqueur indiquant que les objets d’une
classe l’implémentant peuvent être sérialisés.
3. sérialisation : écrire des objets sous forme d’octets dans un flux
d’écriture de données.
4. désérialisation : lire des objets sérialisés préalablement dans un
fichier en utilisant un flux de lecture de données.
5. remarque : si on essaye de sérialiser un objet d’une classe non im-
plémentant cette interface, l’exception NotSerializableException
sera levée.

115
2.14.2.14 Le mot-clé transient
Un attribut précédé par transient sera ignoré lors de la sérialisation.

2.14.2.15 La classe ObjectInputStream


1. package : java.io (à importer manuellement)
2. description : une classe permettant de lire des objets sérialisés à
partir d’un flux de lecture encapsulé et de les désérialiser.
3. quelques méthodes :
/*
* description : désérialiser un objet déjà sérialisé depuis
le flux de lecture
*/
public final Object readObject() throws IOException, ClassNotFoundException;

2.14.2.16 La classe ObjectOutputStream


1. package : java.io (à importer manuellement)
2. description : une classe permettant de sérialiser des objets dans un
flux d’écriture encapsulé.
3. quelques méthodes :
/*
* description : sérialiser un objet dans le flux d'écriture
*/
public final void writeObject(Object object) throws IOException;

2.14.2.16.1 Exemple
// La classe Notice dont les objets ne seront pas sérialisés
package iostreams;

public class Notice {


/*attributes*/
private String langue;

/*constructors*/
public Notice() {
this.langue = "Français";
}

public Notice(String langue) {


this.langue = langue;
}

116
/*methods*/
//toString
@Override
public String toString() {
return "Langue de la notice : " + langue;
}
}

// La classe Game dont les objets seront sérialisés


package iostreams;

import java.io.Serializable;

public class Game implements Serializable {


/*attributes*/
private String nom;
private String style;
private double prix;
private transient Notice notice;

/*constructors*/
public Game(String nom, String style, double prix) {
super();
this.nom = nom;
this.style = style;
this.prix = prix;
this.notice = new Notice();
}

/*methods*/
//Getters & setters
public String getNom() {
return nom;
}

public void setNom(String nom) {


this.nom = nom;
}

public String getStyle() {


return style;
}

public void setStyle(String style) {


this.style = style;
}

117
public double getPrix() {
return prix;
}

public void setPrix(double prix) {


this.prix = prix;
}

public Notice getNotice() {


return notice;
}

public void setNotice(Notice notice) {


this.notice = notice;
}

//toString
@Override
public String toString() {
return "Game [nom=" + nom + ", style=" + style + ", prix=" + prix + "]";
}
}

// La classe Test
package iostreams;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

public class Test {

public static void main(String[] args) {


ObjectInputStream ois = null;
ObjectOutputStream oos = null;

try {
//création d'un fichier et sérialisation d'objets dedans
oos = new ObjectOutputStream(
new BufferedOutputStream(

118
new FileOutputStream("game.txt")));

oos.writeObject(new Game("Assassin's Creed", "Aventure", 45.69));


oos.writeObject(new Game("Tomb Raider", "Plateforme", 23.45));
oos.writeObject(new Game("Tetris", "Stratégie", 2.50));

oos.close();

//désérialisation des objets sérialisés depuis le fichier


ois = new ObjectInputStream(
new BufferedInputStream(
new FileInputStream("game.txt")));

ArrayList<Game> games = new ArrayList<Game>();

games.add((Game)ois.readObject());
games.add((Game)ois.readObject());
games.add((Game)ois.readObject());

for (Game game: games)


System.out.println(game);

} catch(IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (ois != null)
ois.close();

if (oos != null)
oos.close();

} catch (IOException e) {
e.printStackTrace();
}
}
}
}

2.14.2.17 La classe ByteArrayInputStream


1. package : java.io (à importer manuellement)
2. description : idem que la classe BufferedInputStream mais un peu plus
limitée.

119
2.14.2.18 La classe ByteArrayOutputStream
1. package : java.io (à importer manuellement)
2. description : idem que la classe BufferedOutputStream mais un peu
plus limitée.

2.14.2.19 La classe Reader


1. package : java.io (à importer manuellement)
2. description : une classe abstraite, superclasse de toutes les classes
modélisant des flux de lecture de caractères depuis une source de
données.
3. quelques méthodes :
/*
* description : retourne un caractère lu depuis le flux de lecture
*/
public int read() throws IOException;

2.14.2.20 La classe Writer


1. package : java.io (à importer manuellement)
2. description : une classe abstraite, superclasse de toutes les classes
modélisant des flux d’écriture de caractères dans une destination.
3. quelques méthodes :
/*
* description : écriture d'un caractères dans
le flux d'écriture
*/
public void write(int c) throws IOException;

/*
* description : écriture d'un tableau de caractères dans
le flux d'écriture
*/
public void write(char[] cbuf) throws IOException;

/*
* description : écriture d'un string dans
le flux d'écriture
*/
public void write(String str) throws IOException;

2.14.2.21 La classe CharArrayReader


1. package : java.io (à importer manuellement)

120
2. description : une classe désignant un tampon de caractères de taille
adaptative utilisé comme intermédiaire lors de la lecture depuis le
flux de lecture de caractères.
3. remarque : la méthode void close() n’a aucun effet sur un
CharArrayReader.

2.14.2.22 La classe CharArrayWriter


1. package : java.io (à importer manuellement)
2. description : une classe désignant un tampon de caractères de taille
adaptative utilisé comme un intermédiaire lors de l’écriture dans le
flux d’écriture de caractères.
3. quelques méthodes :
/*
* description : retourne une copie du tampon des caractères écrits
*/
public char[] toCharArray();

/*
* description : convertit les caractères du tampon en un String
et le retourne
*/
public String toString();

/*
* description : retourne la taille du tampon
*/
public int size();

/*
* description : vide le contenu du stream
*/
public char[] reset();
4. remarque : la méthode void close() n’a aucun effet sur un
CharArrayWriter.

2.14.2.23 Exemple
package iostreams;

import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.IOException;

121
public class Test {

public static void main(String[] args) {


//création de flux de lecture/écriture de caractères
CharArrayWriter caw = new CharArrayWriter();
CharArrayReader car = null;

try {
//écriture de caractères dans le flux d'écriture de caractères
caw.write("Hey there everyone");
System.out.println(caw);

// caw.close(); n'a aucun effet sur le flux

/*
* création d'un flux de lecture de caractères sur
* le contenu du flux d'écriture
*/
car = new CharArrayReader(caw.toCharArray());

//lecture d'un caractère à la fois


String str = "";
int i;

while((i = car.read()) != -1)


str += (char) i;

//affichage des caractères lus


System.out.println(str);

} catch (IOException e) {
e.printStackTrace();
}
}
}

2.14.2.24 La classe StringReader


1. package : java.io (à importer manuellement)
2. description : une classe désignant flux de lecture de caractères
depuis un String.
3. remarque : mêmes méthodes que CharArrayReader.

2.14.2.25 La classe StringWriter

122
1. package : java.io (à importer manuellement)
2. description : une classe désignant un tampon de taille adap-
tative utilisé comme un intermédiaire lors de l’écriture dans le
flux d’écriture de caractères et qui peut être utilisé ensuite pour
construire un String.
3. remarques :
• mêmes méthodes que CharArrayWriter.
• la méthode void close() n’a aucun effet sur un StringWriter.

2.14.2.26 Exemple
package iostreams;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

public class Test {

public static void main(String[] args) {


//création de flux de lecture/écriture d'un string
StringWriter sw = new StringWriter();
StringReader sr = null;

try {
//écriture de caractères dans le flux d'écriture d'un string
sw.write("Hey there everyone");
System.out.println(sw);

// sw.close(); n'a aucun effet sur le flux

/*
* création d'un flux de lecture de string depuis
* le contenu du flux d'écriture du string
*/
sr = new StringReader(sw.toString());

//lecture d'un caractère à la fois


String str = "";
int i;

while((i = sr.read()) != -1)


str += (char) i;

//affichage des caractères lus

123
System.out.println(str);

} catch (IOException e) {
e.printStackTrace();
}
}
}

2.14.2.27 La classe FileReader


1. package : java.io (à importer manuellement)
2. description : une classe désignant un flux de lecture de caractères
depuis un fichier en utilisant l’encodage et la taille du tampon par
défaut.

2.14.2.28 La classe FileWriter


1. package : java.io (à importer manuellement)
2. description : une classe désignant un flux d’écriture de caractères
dans un fichier en utilisant l’encodage et la taille du tampon par
défaut.

2.14.2.29 La classe BufferedReader


1. package : java.io (à importer manuellement)
2. description : une classe ajoutant un tampon interne à un flux de
lecture de caractères (idem que BufferedInputStream sur les flux de
lecture d’octets).

2.14.2.30 La classe BufferedWriter


1. package : java.io (à importer manuellement)
2. description : une classe ajoutant un tampon interne à un flux
d’écriture de caractères (idem que BufferedOutputStream sur les
flux de lecture d’octets).

2.14.2.31 La classe PrintWriter


1. package : java.io (à importer manuellement)
2. description : une classe permettant d’imprimer des représentations
formatées d’objets dans un flux d’écriture de caractères.
3. quelques méthodes :
/*
* description : imprimer l'objet de type T dans le flux

124
* remarque : T peut désigner : boolean, int, long, float,
double, char, char[], String, Object
*/
public void print(T object)

/*
* description : imprimer l'objet de type T dans le flux
avec un retour à la ligne
* remarque : T désigne les mêmes types que ceux utilisés
avec la méthode print(T object)
*/
public void println(T object)

/*
* description : impression formatée d'objets (printf() du langage C)
*/
public PrintWriter printf(String format, Object ... args);

2.14.2.31.1 Exemple
package iostreams;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Test {

public static void main(String[] args) {


FileWriter fw = null;
FileReader fr = null;

try {
//création d'un flux d'écriture de caractères dans un fichier
fw = new FileWriter("test_fw.txt");
String content = "Bonjour à tous mes amis !\n"
+ "\tComment allez vous ?\n";

fw.write(content);
fw.close();

//création d'un flux de lecture de caractères depuis un fichier


fr = new FileReader("test_fw.txt");

//lecture un caractère à la fois depuis le fichier

125
int i;
content = "";
while((i = fr.read()) != -1)
content += (char) i;

//affichage des caractères lus


System.out.println(content);
fr.close();

} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null)
fw.close();

if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

2.14.3 Le package java.nio

2.14.3.1 Introduction
1. description :
• le package de Java pour les E/S depuis Java 4 (nio = New I/O),
améliorant les performances sur le traitement des fichiers, du
réseau et des buffers, en traitant les données par blocs au lieu
d’octets/caractères et en permettant d’effectuer des E/S non blo-
quantes.
• il est surtout utilisé pour le traitement des flux sur le réseau,
pour faire de la programmation distribuée et pour permettre
l’évolutivité des serveurs.
2. concepts fondateurs :
• canal ou channel : un flux de lecture/écriture sur une source
de données amené à travailler avec un buffer dont nous définis-
sons la taille.
• tampon ou buffer : un stockage intermédiaire des données.
• sélecteur ou selector : un multiplexeur permettant de choisir
un canal parmis plusieurs pour permettre la concurrence et

126
l’aspect non bloquant des E/S.

2.14.3.2 La classe FileChannel


1. package : java.io (à importer manuellement)
2. description : une classe permettant de récupérer un canal sur un
fichier en écriture, en lecture, etc.
3. quelques méthodes :
/*
* description : lire une séquence d'octets depuis le canal,
le met dans le ByteBuffer
et retourne le nombre d'octets lus, ou -1 si le canal
est en fin du stream associé
*/
public int read(ByteBuffer destination) throws IOException;

/*
* description : écrire la séquence d'octets depuis le ByteBuffer
dans le canal
et retourne le nombre d'octets écrits
*/
public int write(ByteBuffer source) throws IOException;

/*
* description : retourne la taille du fichier du canal
*/
public long size() throws IOException;

2.14.3.3 La classe Buffer


1. package : java.io (à importer manuellement)
2. description : une classe abstraite, superclasse de toutes les classes
modélisant des tampons de type primitif (sauf boolean) à utiliser avec
un FileChannel.
3. quelques attributs :
• capacity : le nombre d’éléments du buffer qui (positive et non
modifiable) ;
• limit : l’indexe du premier élément qui ne doit ni être lu ni
écrit dessus (positive et <= capacity) ;
• position : l’indexe du prochain élément qui doit être lu ou écrit
dessus (positive et <= limit).
4. quelques méthodes :
/*
* description : retourne la taille du buffer (i.e. le nombre d'éléments)

127
*/
public final int capacity();

/*
* description : met "position" à 0, "limit" à "capacity"
et prépare le buffer pour l'écriture
(i.e. lire depuis un canal)
*/
public final Buffer clear();

/*
* description : met "limit" à "position", remet "position" à 0
et prépare le buffer pour la lecture
(i.e. écrire dans un canal)
*/
public final Buffer flip();

/*
* description : remet "position" à 0 et
prépare le buffer pour la lecture
(i.e. écrire dans un canal)
*/
public final Buffer rewind();

/*
* description : retourne le contenu du buffer sous forme d'un array
*
* remarque : est spécialisé pour retourner le buffer
selon le type primitif de la classe l'implémentant
par exemple Object -> byte[] pour un ByteBuffer
*/
public abstract Object array();

2.14.3.4 La classe CharBuffer


1. package : java.io (à importer manuellement)
2. description : une classe désignant un tampon de char à utiliser avec
un FileChannel.
3. quelques méthodes :
/*
* description : alloue "capacity" octets pour un buffer et le retourne
*
* remarque : cette méthode est utilisé par tout Buffer
avec comme type retour la classe l'utilisant
par exemple public static ByteBuffer allocate(int capacity)

128
Figure 13: “Hiérarchie des Buffers de java.nio en Java”

129
pour un buffer de type "byte"
*/
public static CharBuffer allocate(int capacity)

2.14.3.5 La classe ByteBuffer


1. package : java.io (à importer manuellement)
2. description : une classe désignant un tampon de byte à utiliser avec
un FileChannel.

2.14.3.6 La classe ShortBuffer


1. package : java.io (à importer manuellement)
2. description : une classe désignant un tampon de short à utiliser avec
un FileChannel.

2.14.3.7 La classe IntBuffer


1. package : java.io (à importer manuellement)
2. description : une classe désignant un tampon de int à utiliser avec un
FileChannel.

2.14.3.8 La classe LongBuffer


1. package : java.io (à importer manuellement)
2. description : une classe désignant un tampon de long à utiliser avec
un FileChannel.

2.14.3.9 La classe FloatBuffer


1. package : java.io (à importer manuellement)
2. description : une classe désignant un tampon de float à utiliser avec
un FileChannel.

2.14.3.10 La classe DoubleBuffer


1. package : java.io (à importer manuellement)
2. description : une classe désignant un tampon de double à utiliser avec
un FileChannel.

2.14.3.10.1 Exemple

130
package iostreams;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class Test {

public static void main(String[] args) {


try(FileInputStream fis = new FileInputStream("dictionnaire.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
FileChannel fc = fis.getChannel()){

//mesurer le temps d'exécution d'un BufferedInputStream


long startTime = System.currentTimeMillis();

while(bis.read() != -1);

System.out.println("Temps de lecture d'un fichier " +


" avec BufferedInputStream : " +
(System.currentTimeMillis() - startTime) + " millisecondes");

//création d'un tampon ayant la taille du fichier


ByteBuffer bBuf = ByteBuffer.allocate((int)fc.size());
System.out.println("taille du buffer d'octets : " + bBuf.capacity());

//mesurer le temps d'exécution d'un FileChannel


startTime = System.currentTimeMillis();
bBuf.clear(); // prépare le buffer pour lire le canal
fc.read(bBuf);

System.out.println("Temps de lecture d'un fichier " +


" avec FileChannel et ByteBuffer : " +
(System.currentTimeMillis() - startTime) + " millisecondes");

//récupérer le contenu du buffer


bBuf.flip(); // prépare le buffer pour lire son contenu
byte[] buffer = bBuf.array();

System.out.println("taille du tableau d'octets obtenu du buffer : "


+ buffer.length);

} catch (IOException e) {
e.printStackTrace();

131
}
}
}

2.14.4 Le package java.nio.charset

2.14.4.1 Introduction
1. description : un package définissant des charsets, encodeurs et dé-
codeurs pour faire des traductions entre les octets et les caractères
Unicode.

2.14.4.2 La classe Charset


1. package : java.nio.charset (à importer manuellement)
2. description : une classe :
• permettant de créer des mappings entre des séquences d’unités
de code Unicode en 16 bits et des séquences d’octets.
• offrant des méthodes pour la création d’encodeurs et de dé-
codeurs et pour manipuler les charsets.

2.14.4.3 La classe StandardCharsets


1. package : java.nio.charset (à importer manuellement)
2. description : une classe finale offrant les charsets standards sous
forme de constantes.
3. quelques constantes :
/*
* description : le charset ASCII
*/
public static final Charset ASCII_US;

/*
* description : le charset ISO Latin 1
*/
public static final Charset ISO_8859_1;

/*
* description : le charset UTF-8
*/
public static final Charset UTF_8;

132
2.14.5 Le package java.nio.file

2.14.5.1 Introduction
1. description : une amélioration du package java.nio pour les E/S
depuis Java 7, notamment pour les systèmes de fichiers.
2. nouveautés :
• meilleure gestion des exceptions de la classe java.io.File ;
• accès complet au système de fichiers avec le support des liens
durs et symboliques ;
• introduction de méthodes utilitaires sur les chemins des
fichiers/dossiers via la classe java.nio.file.Files : déplace-
ment et copie de fichier, lecture/écriture binaire du texte, etc.
;
• remplacement de java.io.File par l’interface java.nio.file.Path.
3. remarque : la classe java.io.File est toujours utilisable pour la
rétrocompatibilité, mais l’interface java.nio.file.Path est à priv-
ilégier.

2.14.5.2 La classe Files


1. package : java.nio.file (à importer manuellement)
2. description : une classe finale fournissant des méthodes utilitaires
statiques sur différents types de fichiers (i.e. fichiers, dossiers, liens,
etc.).
3. quelques méthodes :
/*
* description : crée un fichier ayant le chemin "path"
selon les propriétés du fichier spécifiées par "attrs"
*/
public static Path createFile(Path path, FileAttribute<?>... attrs) throws IOException;

/*
* description : crée un dossier ayant le chemin "dir"
selon les propriétés du fichier spécifiées par "attrs"
et retourner son chemin
*/
public static Path createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException;

/*
* description : retourne true si le fichier
désigné par le chemin "path" existe, false sinon
et retourner son chemin
*/
public static boolean exists(Path path, LinkOption... options);

133
/*
* description : supprimer un fichier/dossier ayant le chemin "path"
*/
public static void delete(Path path) throws IOException;

/*
* description : supprimer un fichier/dossier ayant le chemin "path"
s'il existe et retourner true si le fichier est supprimé
false sinon (i.e. s'il n'existe pas)
*/
public static boolean deleteIfExists(Path path) throws IOException;

/*
* description : retourne true si le fichier
désigné par le chemin "path" est un dossier, false sinon
*/
public static boolean isDirectory(Path path);

/*
* description : copier le contenu du fichier désigné par "source"
dans le fichier désigné par "destination" selon les options de
copie spécifiées par "options"
et retourner le chemin de la destination
*/
public static Path copy(Path source, Path destination, CopyOption... options)
throws IOException;

/*
* description : déplacer/renommer le fichier désigné par "source"
(vers l'endroit désigné) par "destination" selon les options de
copie spécifiées par "options"
et retourner le chemin de la destination
*/
public static Path move(Path source, Path destination, CopyOption... options)
throws IOException;

/*
* description : écrire les octets dans le tableau d'octets "bytes"
dans le fichier désigné par "path" ouvert selon
les options spécifiées par "options"
et retourne le chemin du fichier
*/
public static Path write(Path path, byte[] bytes, OpenOption... options)
throws IOException;

134
/*
* description : ouvre un dossier et retourne
un "DirectoryStream" pour itérer sur les entrées
du dossier ouvert
*/
public static DirectoryStream<Path> newDirectoryStream(Path dir)
throws IOException;

/*
* description : ouvre un dossier et retourne
un "DirectoryStream" pour itérer sur les entrées
du dossier ouvert et filtrer ceux ne matchant pas
le motif spécifié par "pattern"
*/
public static DirectoryStream<Path> newDirectoryStream(Path dir, String pattern)
throws IOException;

/*
* description : ouvre un fichier avec un "InputStream" selon les options
spécifiées par "options"
et retourne un flux de lecture dessus
*/
public static InputStream newInputStream(Path path, OpenOption... options)
throws IOException;

/*
* description : ouvre un fichier avec un "OutputStream" selon les options
spécifiées par "options"
et retourne un flux d'écriture dessus
*/
public static OutputStream newOutputStream(Path path, OpenOption... options)
throws IOException;

/*
* description : ouvre un fichier avec un "BufferedReader"
*/
public static BufferedReader newBufferedReader(Path path)
throws IOException;

/*
* description : ouvre un fichier avec un "BufferedReader"
et l'encodage "cs"
*/
public static BufferedReader newBufferedReader(Path path, Charset cs)
throws IOException;

135
/*
* description : ouvre un fichier avec un "BufferedWriter" selon les options
d'ouverture spécifiées par "options"
et retourne un flux d'écriture dessus
*/
public static BufferedReader newBufferedWriter(Path path, OpenOption... options)
throws IOException;

/*
* description : ouvre un fichier avec un "BufferedWriter"
et l'encodage "cs" selon les options
d'ouverture spécifiées par "options"
et retourne un flux d'écriture dessus
*/
public static BufferedReader newBufferedWriter(Path path, Charset cs, OpenOption... options)
throws IOException;

/*
* description : retourne un stream contenant les lignes du fichier
désigné par le chemin "path"
*/
public static Stream<String> lines(Path path) throws IOException;

/*
* description : retourne un stream contenant les lignes du fichier
désigné par le chemin "path" selon le charset "cs"
*/
public static Stream<String> lines(Path path, Charset cs) throws IOException;

2.14.5.3 L’interface Path


1. package : java.nio.file (à importer manuellement)
2. description : une interface modélisant un chemin thread-safe d’un
fichier/dossier dépendant du système de fichiers. Elle est destinée
à remplacer la classe java.io.File.
3. quelques méthodes :
/*
* description : retourne le chemin absolu du "Path" courant
*/
Path toAbsolutePath();

/*
* description : retourne le nom du fichier désigné par
le "Path" courant sous forme d'un "Path"
*/

136
Path getFileName();

/*
* description : retourne le nom du dossier parent du
fichier désigné par le "Path" courant sous forme d'un "Path"
*/
Path getParent();

2.14.5.4 La classe Paths


1. package : java.nio.file (à importer manuellement)
2. description : une classe finale fournissant des méthodes utilitaires
statiques permettant de convertir un String ou une URI en un Path.
3. méthodes :
/*
* description : retourne un "Path" à partir d'un String
ou de plusieurs String concaténés pour former un chemin
*/
public static Path get(String first, String... more);

/*
* description : retourne un "Path" à partir d'une URI
*/
public static Path get(URI uri);

2.14.5.5 La classe FileSystem


1. package : java.nio.file (à importer manuellement)
2. description : une classe abstraite fournissant une interface aux sys-
tèmes de fichiers et des méthodes factory pour accéder aux fichiers
et d’autres ressources du système.
3. quelques méthodes :
/*
* description : retourne un itérateur sur les dossiers racines
du système de fichier courant
*/
public abstract Iterable<Path> getRootDirectories();

2.14.5.6 La classe FileSystems


1. package : java.nio.file (à importer manuellement)
2. description : une classe finale fournissant des méthodes factory pour
les systèmes de fichiers.
3. quelques méthodes :

137
/*
* description : retourne le système de fichiers par défaut
*/
public static FileSystem getDefault();

2.14.5.7 La classe DirectoryStream<T>


1. package : java.nio.file (à importer manuellement)
2. description : une interface désignant un flux pour itérer sur les en-
trées d’un dossier.

2.14.5.8 L’interface OpenOption


1. package : java.nio.file (à importer manuellement)
2. description : une interface marqueur définissant la manière
d’ouvrir/créer un fichier.

2.14.5.9 L’énumération StandardOpenOption


1. package : java.nio.file (à importer manuellement)
2. description : une énumération définissant les options standards pour
ouvrir/créer un fichier.
3. quelques valeurs :
• CREATE : créer un nouveau fichier s’il n’existe pas ;
• CREATE_NEW : créer un nouveau fichier et échouer si le fichier
existe déjà ;
• READ : accès en lecture ;
• WRITE : accès en écriture :
• APPEND : si le fichier est ouvert en écriture, alors écrire à partir
de sa fin ;
• TRUNCATE_EXISTING : si le fichier existe déjà et est ouvert en
écriture, alors sa taille est réduite à 0.

2.14.5.10 L’énumération LinkOption


1. package : java.nio.file (à importer manuellement)
2. description : une énumération fournissant des options définissant la
manière de traiter les liens symboliques.
3. valeurs :
• NOFOLLOW_LINKS : ne pas suivre les liens symboliques (i.e. ne
pas tenir des comptes des fichiers pointés par les liens)

2.14.5.11 L’interface CopyOption


1. package : java.nio.file (à importer manuellement)

138
2. description : une interface marqueur définissant la manière de dé-
placer/copier un fichier.

2.14.5.12 L’énumération StandardCopyOption


1. package : java.nio.file (à importer manuellement)
2. description : une énumération définissant les options standards pour
déplacer/copier un fichier.
3. quelques valeurs :
• ATOMIC_MOVE : déplacer un fichier d’une manière atomique ;
• COPY_ATTRIBUTES : copier les attributs pour le nouveau fichier ;
• REPLACE_EXISTING : remplacer le fichier copié s’il existe déjà;

2.14.5.13 L’interface FileAttributes<T>


1. package : java.nio.file (à importer manuellement)
2. description : une interface définissant les méthodes d’accès aux pro-
priétés des fichiers.
3. méthodes :
/*
* description : le nom de la propriété
*/
String name();

/*
* description : la valeur de la propriété
*/
T value();

2.14.5.13.1 Exemples
// Exemple d'utilisation des paths
package iostreams;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Hashtable;

public class Test {

public static void properties(Path path) {


System.out.println("File exists ? " + Files.exists(path));

139
System.out.println("File is directory ? " + Files.isDirectory(path));
System.out.println("File name : " + path.getFileName());
System.out.println("File parent : " + path.getParent());
System.out.println("File absolute path : " + path.toAbsolutePath() + "\n");
}

public static void main(String[] args) {


Hashtable<String, Path> paths = new Hashtable<>();
paths.put("src", Paths.get("src"));
paths.put("created", Paths.get("created")); //createDirectory()
paths.put("test/created.txt", Paths.get("test/created.txt")); //createFile()
paths.put("test/test.txt", Paths.get("test/test.txt")); //already existant
paths.put("test/copy.txt", Paths.get("test/copy.txt")); //copy()
paths.put("test/renamed.txt", Paths.get("test/renamed.txt")); //rename()
paths.put("moved.txt", Paths.get("moved.txt")); //move()
paths.put("test/to_be_deleted.txt", Paths.get("test/to_be_deleted.txt")); //delete()

//vérifier l'état du dossier src et le fichier test/test.txt


properties(paths.get("src"));
properties(paths.get("test/test.txt"));

try {
//créer un dossier
System.out.println("Before creating directory: ");
properties(paths.get("created"));

Files.createDirectories(paths.get("created"));

System.out.println("After creating directory: ");


properties(paths.get("created"));

//créer un fichier
System.out.println("Before creating file: ");
properties(paths.get("test/created.txt"));

Files.createFile(paths.get("test/created.txt"));

System.out.println("After creating file: ");


properties(paths.get("test/created.txt"));

//copier un fichier
System.out.println("Before copying file: ");
properties(paths.get("test/copy.txt"));

Files.copy(paths.get("test/test.txt"), paths.get("test/copy.txt"),
StandardCopyOption.REPLACE_EXISTING);

140
System.out.println("After copying file: ");
properties(paths.get("test/copy.txt"));

//renommer un fichier
System.out.println("Before renaming file: ");
properties(paths.get("test/renamed.txt"));

Files.move(paths.get("test/copy.txt"), paths.get("test/renamed.txt"),
StandardCopyOption.REPLACE_EXISTING);

System.out.println("After renaming file: ");


properties(paths.get("test/renamed.txt"));

//déplacer un fichier
System.out.println("Before moving file: ");
properties(paths.get("moved.txt"));

Files.move(paths.get("test/renamed.txt"), paths.get("moved.txt"),
StandardCopyOption.REPLACE_EXISTING);

System.out.println("After moving of file: ");


properties(paths.get("moved.txt"));

//supprimer un fichier
System.out.println("Before deleting file: ");
properties(paths.get("test/to_be_deleted.txt"));

Files.delete(paths.get("test/to_be_deleted.txt"));

System.out.println("After deleting file: ");


properties(paths.get("test/to_be_deleted.txt"));

} catch (IOException e) {
e.printStackTrace();
}
System.out.println("After creation of directory: ");
}
}

/*
* Exemple d'itération sur le contenu des racines du FS
en utilisant DirectoryStream
*/
package iostreams;

141
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;

public class Test {

public static void checkForNewLine(int counter) {


if (counter%4 == 0)
System.out.println("\n");
}

public static void main(String[] args) {


//récupérer les racines du système de fichiers actuel
Iterable<Path> roots = FileSystems.getDefault().getRootDirectories();

for (Path root: roots) {


System.out.println(root);

try(DirectoryStream<Path> oldFiles = Files.newDirectoryStream(root, "*.old");


DirectoryStream<Path> listing = Files.newDirectoryStream(root);) {

int i = 0;
//afficher uniquement les fichiers anciens de la racine
System.out.println("Fichiers anciens de la racine");
System.out.println("===========================");
for (Path oldFile: oldFiles) {
System.out.println("\t\t" + oldFile);
i++;
checkForNewLine(i);
}

System.out.println();

i = 0;
//afficher tout le contenu de la racine
System.out.println("contenu de la racine");
System.out.println("===========================");
for(Path file: listing) {
System.out.println("\t\t" + file + (Files.isDirectory(file)? "/" : ""));
i++;
checkForNewLine(i);
}

142
} catch(IOException e) {
e.printStackTrace();
}
}
}
}

2.15 Les classes imbriquées (nested classes)

2.15.1 Introduction

1. définition : une classe imbriquée est une classe définie au sein d’une
autre classe, appelée la classe englobante.
2. utilités : structuration thématique du code (idem que les packages,
mais moins verbeux)
3. contraintes d’utilisation :
• selon la visibilité choisie pour la classe imbriquée, celle-ci peut
être cachée au monde extérieur et uniquement accessible par
sa classe englobante ;
• sous certaines conditions, la classe imbriquée aura accès à la
partie privée de la classe englobante et inversement.
4. types :
• classes imbriquées statiques (nested static classes)
• classes imbriquées non statiques incluant :
1. les classes internes ;
2. les classes locales ;
3. les classes anonymes.

2.15.2 Les classes imbriquées statiques (nested static classes)

1. définition : une classe statique définie au sein d’une classe en-


globante telle qu’il n’existe aucun lien particulier entre une instance
de la classe imbriquée et une instance de la classe englobante (im-
brication pour des raisons thématiques).
2. contraintes d’utilisation :
• la classe englobante Engl peut accéder aux attributs de la classe
imbriquée quelle que soit sa visibilité.
• la classe imbriquée statique Imbr :
1. aura accès directement aux attributs et méthodes sta-
tiques quelles que soient leurs visibilités ;
2. aura accès aux attributs et méthodes non statiques,
quelles que soient leurs visibilités, à travers une instance de
Engl explicitement créée au sein de l’une de ses méthodes.
• les autres classes Cs:

143
1. si Imbr est public, alors elles auront le même comportement
que la classe Engl.
2. si Imbr est private, alors elles n’auront aucun accès à Imbr.
3. si Imbr est protected ou sans visibilité :
– si Cs sont dans le même package que Imbr ou l’héritent,
alors elles auront le même comportement que la classe
Engl.
– sinon, n’auront aucun accès à Imbr
3. syntaxes :
// instanciation de la classe imbriquée statique
// syntaxe à privilégier
Engl.Imbr inst = new Engl.Imbr();

/*
* syntaxe alternative équivalente, mais à éviter
car ne permet pas de préciser qu'il s'agit d'une classe
imbriquée statique
*/
Imbr inst = new Imbr();
4. exemple :
// La classe Liste
package nested_classes;

public class Liste {


/*attributes*/
private Cellule premier;
private static int nbListes;

/*static nested class*/


public static class Cellule{
/*attributes*/
private int value;
private Cellule suivante;

/*constructors*/
public Cellule() {}
public Cellule(int value) {
this.value = value;
}

/*methods*/
public String essai() {
Liste l = new Liste();
return "" + Liste.nbListes + l.premier;

144
}

//ToString
@Override
public String toString() {
return String.valueOf(this.value);
}

} //end of static nested class Cellule

/*constructors*/
public Liste() {
this.premier = null;
Liste.nbListes++;
}

/*methods*/
public int getPremier() {return this.premier.value;}
public static int getNbListes() {return Liste.nbListes;}

public void ajouteTete(int v) {


Liste.Cellule cell = new Liste.Cellule(v);
cell.suivante = this.premier;
this.premier = cell;
}

//ToString
@Override
public String toString() {
String output = "(";
Liste.Cellule cell = this.premier;

while(cell != null) {
output += cell.toString() + " ";
cell = cell.suivante;
}

if(output.length() > 1) // list is not empty


output = output.substring(0, output.length()-1);

output += ")";

return output;
}
}

145
// La classe Test
package nested_classes;

public class Test {

public static void main(String[] args) {


Liste l = new Liste();
System.out.println(l); // ()

l.ajouteTete(10);
System.out.println(l); // (10)

l.ajouteTete(20);
System.out.println(l); // (20 10)

l.ajouteTete(30);
System.out.println(l); // (30 20 10)
System.out.println("Nombre de listes créées " + Liste.getNbListes()); // 1

l = new Liste();
System.out.println("Nombre de listes créées " + Liste.getNbListes()); // 2

}
}

2.15.3 Les classes internes (internal classes)

1. définition : une classe non statique définie au sein d’une classe


englobante telle qu’il existe un lien d’intimité entre chaque instance
de la classe imbriquée et une instance de la classe englobante (im-
brication pour des raisons de composition).
2. contraintes d’utilisation :
• la classe englobante Engl peut accéder aux attributs et méth-
odes de la classe imbriquée quelle que soit sa visibilité.
• la classe interne Internal :
1. aura accès directement aux attributs et méthodes de
l’instance courante de la classe Engl quelles que soient leurs
visibilités ;
2. ne peut contenir :
– des attributs statiques (sauf s’ils sont aussi final) ;
– des méthodes statiques.
• principe d’exclusivité :
1. une instance de la classe Engl ne peut référencer des in-
stances de la classe interne Internal associées à une autre
instance de la classe Engl.

146
2. une instance de la classe Internal ne peut accéder aux at-
tributs d’une instance Engl si cette dernière ne lui est pas
associée.
3. utilisation :
• création d’une instance de la classe englobante Engl.
• création d’une/plusieurs instances de la classe interne
Internal exclusivement à travers l’instance créée de la classe
englobante Engl.
4. syntaxes :
// création d'une instance de la classe englobante
Engl englobante = new Engl();

// création d'une instance de la classe interne associée à "englobante"


Engl.Interne interne = englobante.new Engl.Interne();

/*
* référencement des propriétés de la classe englobante dans le corps d'une
méthode de la classe interne
*/
Engl.this.attribut; // référencer un attribut de la classe englobante
Engl.this.methode(); // invocation d'une méthode de la classe englobante
5. exemple :
// La classe Personne
package nested_classes;

public class Personne {


/*attributes*/
private String nom;
private Adresse adresse;

/*inner class*/
protected class Adresse{
/*attributes*/
private String ville = "N/A";
private String pays = "N/A";

/*constructor*/
public Adresse(String ville, String pays) {
this.ville = ville;
this.pays = pays;
}

/*methods*/
//ToString

147
@Override
public String toString() {
return this.ville + ", " + this.pays;
}
} //end of Adresse inner class

/*constructors*/
public Personne(String nom, String ville, String pays) {
this.nom = nom;
this.adresse = new Personne.Adresse(ville, pays);
}

/*methods*/
//Getters & Setters
public String getNom() {return this.nom;}
public void setNom(String nom) {this.nom = nom;}

public Personne.Adresse getAdresse() {return this.adresse;}


public void setAdresse(Personne.Adresse adresse) {this.adresse = adresse;}

//ToString
@Override
public String toString() {
return this.nom + " habite à " + this.adresse;
}
}

// La classe Test
package nested_classes;

public class Test {

public static void main(String[] args) {


Personne dupont = new Personne("Dupont", "Paris", "France");
Personne toto = new Personne("Toto", "Boston", "USA");

System.out.println(dupont); // Dupont habite à Paris, France


System.out.println(toto); // Toto habite à Boston, USA

Personne.Adresse adresseDupont = dupont.new Adresse("Dupont Ville", "Dupont Pays");


dupont.setAdresse(adresseDupont);
System.out.println(dupont); // Dupont habite à Dupont Ville, Dupont Pays

Personne.Adresse adresseToto = toto.new Adresse("Toto Ville", "Toto Pays");


toto.setAdresse(adresseToto);
System.out.println(toto); // Toto habite à Toto Ville, Toto Pays

148
}
}

2.15.4 Les classes locales (local classes)

1. définition : une classe interne particulière définie au sein d’un


bloc (le corps d’une méthode, une itération, etc.).
2. portée : le bloc dans lequel elle est définie.
3. visibilité : aucune modifieur de visibilité, considérant qu’elle bénéficie
d’un certain niveau de sécurité étant assez imbriquée.
4. contraintes d’utilisation :
• le bloc englobant de la classe englobante Engl a accès aux at-
tributs et méthodes de la classe imbriquée quelle que soit sa
visibilité.
• la classe locale Local : aura accès directement :
1. aux attributs et méthodes de l’instance courante de la
classe Engl quelles que soient leurs visibilités ;
2. en lecture uniquement aux paramètres et variables du
bloc englobant, à condition qu’ils soient non modifiables
(i.e. final).
5. syntaxes :
// définition de la classe englobante
visibilite class Engl{
...

// définition de la méthode contenant la classe locale


visibilite typeRetour nomMethode([params]){

class Local{
/*corps*/
}

/*instanciation de la classe locale*/


Local local = new Local();
// utilisation de local dans le code de calcul de la méthode
}
}
6. exemple :
// La classe Autorisation
package nested_classes;

import java.util.Scanner;

149
public class Autorisation {
/*attributes*/
private int num;

/*constructors*/
public Autorisation() {}

/*methods*/
public String saisieNumTel(Scanner scanner) {
System.out.println("Saisie numéro de téléphone");
String code = "+";

/*local class*/
class NumTel{
/*attributes*/
private String indicatif;
private String numeroLocal;

/*methods*/
public String saisie(Scanner scanner) {
System.out.println("Saisie indicatif");
this.indicatif = scanner.next();

System.out.println("Saisie numéro local");


this.numeroLocal = scanner.next();

return this.indicatif + this.numeroLocal;


}
} // end of NumTel local class

NumTel num = new NumTel();


return code + num.saisie(scanner);
}
}

// La classe Test
package nested_classes;

import java.util.Scanner;

public class Test {

public static void main(String[] args) {


Autorisation autorisation = new Autorisation();
Scanner scanner = new Scanner(System.in);
System.out.println(autorisation.saisieNumTel(scanner)); //+3312345678

150
}
}

2.15.5 Les classes anonymes (anonymous classes)

1. définition :
• une variante d’une classe locale ne possèdant pas de nom.
• une expression de définition et d’instanciation simultanée
d’une classe à une seule utilisation.
2. utilisation :
• création d’un concept support abstrait (classe abstraite ou inter-
face) ;
• création d’une classe anonyme fournissant des implémentations
des éléments abstraits (i.e. méthodes abstraites) du concept
support créé.
3. propriétés :
• ne peuvent pas être déclarées abstract ou static ;
• ne peuvent pas définir de constructeur ;
• automatiquement déclarées final et donc ne permettent pas
l’héritage.
4. syntaxes :
// définition du concept support sous forme d'une interface
visibilite interface ISupport{
/*autres éléments*/
typeRetour1 nomMethod1([params]);
typeRetour2 nomMethod2([params]);
...
typeRetourK nomMethod3([params]);
}

// définition du concept support sous forme d'une interface


visibilite abstract class AbstractSupport{
/*autres éléments*/
visibilite abstract typeRetour1 nomMethod1([params]);
visibilite abstract typeRetour2 nomMethod2([params]);
...
visibilite abstract typeRetourK nomMethod3([params]);
}

// définition de la classe englobante


visibilite class Engl{
...

// définition de la méthode contenant la classe anonyme

151
visibilite typeRetour nomMethode([params]){

/*définition et instanciation de la classe anonyme à partir de l'interface support*/


ISupport support = new ISupport(){
/*corps contenant les implémentations des méthodes de l'interface*/
};

/*définition et instanciation de la classe anonyme à partir


de la classe abstraite support*/
AbstractSupport supportAbstract = new AbstractSupport(){
/*corps contenant les implémentations des méthodes abstraites
de la classe abstraite support*/
};
// utilisation de support ou supportAbstract dans le code de calcul de la méthode
}
}
5. exemples :
// L'interface support Saisissable
package nested_classes;

import java.util.Scanner;

public interface Saisissable {


String saisie(Scanner scanner);
}

// La classe AutorisationAnonyme
package nested_classes;

import java.util.Scanner;

public class AutorisationAnonyme {


/*attributes*/
private int num;

/*methods*/
public String saisieNumTel(Scanner scanner) {
System.out.println("Saisie numéro de téléphone");
String code = "+";

/*classe anonyme*/
Saisissable saisissable = new Saisissable() {
/*attributes*/
private String indicatif;
private String numeroLocal;

152
/*methods*/
@Override
public String saisie(Scanner scanner) {
System.out.println("Saisie indicatif");
this.indicatif = scanner.next();

System.out.println("Saisie numéro local");


this.numeroLocal = scanner.next();

return this.indicatif + this.numeroLocal;

}
}; //fin de la classe anonyme
return code + saisissable.saisie(scanner);
}
}

// La classe Test
package nested_classes;

import java.util.Scanner;

public class Test {

public static void main(String[] args) {


AutorisationAnonyme autorisation = new AutorisationAnonyme();
Scanner scanner = new Scanner(System.in);
System.out.println(autorisation.saisieNumTel(scanner)); // +33612345678
}
}

2.16 Interfaces fonctionnelles, lambda expressions et


références de méthodes/constructeurs (Depuis Java
8)

2.16.1 Introduction

Depuis Java 8, on a introduit des méchanismes permettant d’utiliser les


méthodes en tant que méthodes du premier ordre pour faire de la pro-
grammation fonctionnelle, tels que :
1. interfaces fonctionnelles.
2. lambda expressions.
3. références de méthodes et de constructeurs.

153
2.16.2 Interfaces fonctionnelles

2.16.2.1 Introduction
1. définition : Une interface fonctionnelle est une interface devant re-
specter les contraintes suivantes :
• a exactement une méthode abstraite qui n’a pas la même
signature qu’une méthode de la classe Object (on parle d’une
interface SAM ou Single Abstract Method interface) ;
• peut être introduite via l’annotation FunctionalInterface
(recommandé mais non obligatoire).
• peut hériter de plusieurs méthodes abstraites qui peuvent se
substituer les unes aux autres à condition que l’une soit plus spé-
cifique que l’autre ;
• peut avoir des méthodes default ou static ;
2. utilité : une instance d’une interface fonctionnelle est une lambda
expression ou une référence de méthode/constructeur (i.e. une fonc-
tion du premier ordre).
3. conception en Java : une hiérarchie d’interfaces fonctionnelles :
• package : java.util.function.
• contenu : signatures de méthodes du premier ordre.
4. exemple d’application en Java : dans le cadre des streams, des
méthodes qui attendent des instances d’interfaces fonctionnelles
en paramètre incluent : filter(), map(), forEach(), etc.

2.16.2.2 L’annotation FunctionalInterface


1. package : java.lang (importée automatiquement)
2. description : une annotation pouvant annoter uniquement des in-
terfaces fonctionnelles.
3. effet : à l’exécution les instances d’une interface fonctionnelle an-
notée par FunctionalInterface sont traitées en tant que méthodes
du premier ordre.
4. signature :
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface{/*...*/ }

2.16.2.3 L’interface fonctionnelle Function<T, R>


1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle générique acceptant en
entrée un paramètre générique de type T et renvoyant un résultat
de type générique R.
3. quelques méthodes :

154
@FunctionalInterface
public interface Function<T, R> {
/*
* description : une fonction invoquée sur un paramètre de type générique "T"
et retournant un résultat de type "R"
*/
R apply(T t);

/*
* description : applique la fonction appelante sur l'entrée
puis applique "after" sur le résultat R1 de l'application de la fonction
courante, et retourne le résultat final
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after);
}

2.16.2.3.1 Exemple
package function;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class Test {

/*
* description : méthode prenant une liste de Personne et utilisant
* l'interface fonctionnelle "func" pour retourner leurs noms
*/
public static List<String> transformToListString(List<Personne> list,
Function<Personne, String> func){
List<String> names = new ArrayList<>();

for (Personne p: list)


names.add(func.apply(p)); // retourne le nom de p

return names;
}

/*
* description : méthode prenant une liste de Personne et utilisant
* l'interface fonctionnelle "func" pour retourner leurs ages
*/

155
public static List<Integer> transformToListInt(List<Personne> list,
Function<Personne, Integer> func){
List<Integer> ages = new ArrayList<>();

for (Personne p: list)


ages.add(func.apply(p)); // retourne l'age de p

return ages;
}

public static void main(String[] args) {


List<Personne> personnes = Arrays.asList(
new Personne(10, "toto"),
new Personne(20, "titi"),
new Personne(30, "tata"),
new Personne(40, "tutu")
);

Function<Personne, String> getNom = p -> p.getNom();


Function<Personne, Integer> getAge = p -> p.getAge();
Function<Integer, Integer> doubleAge = a -> a * 2;

// retourner la liste des noms des personnes


System.out.println(transformToListString(personnes, getNom));

// retourne la liste des ages des personnes, multipliés chacun par 2


System.out.println(transformToListInt(personnes, getAge.andThen(doubleAge)));
}
}

2.16.2.4 L’interface fonctionnelle IntFunction<R>


1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle générique acceptant en
entrée un paramètre de type int et renvoyant un résultat de type
générique R.
3. quelques méthodes :
/**
* description : une fonction invoquée sur un paramètre de type "int"
et retournant un résultat de type générique "R"
*/
@FunctionalInterface
public interface IntFunction<R> {
R apply(int value);
}

156
2.16.2.5 L’interface fonctionnelle DoubleFunction<R>
1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle générique acceptant en
entrée un paramètre de type double et renvoyant un résultat de
type générique R.
3. quelques méthodes :
/**
* description : une fonction invoquée sur un paramètre de type "double"
et retournant un résultat de type générique "R"
*/
@FunctionalInterface
public interface DoubleFunction<R> {
R apply(double value);
}

2.16.2.6 L’interface fonctionnelle ToIntFunction<T>


1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle générique acceptant en
entrée un paramètre générique de type T et renvoyant un résultat
de type int.
3. quelques méthodes :
/**
* description : une fonction invoquée sur un paramètre de type générique "T"
et retournant un résultat de type "int"
*/
@FunctionalInterface
public interface ToIntFunction<T> {
int applyAsInt(T value);
}

2.16.2.7 L’interface fonctionnelle ToDoubleFunction<T>


1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle générique acceptant en
entrée un paramètre générique de type T et renvoyant un résultat
de type double.
3. quelques méthodes :
/**
* description : une fonction invoquée sur un paramètre de type générique "T"
et retournant un résultat de type "double"
*/
@FunctionalInterface

157
public interface ToDoubleFunction<T> {
double applyAsDouble(T value);
}

2.16.2.8 L’interface fonctionnelle IntToDoubleFunction


1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle acceptant en entrée un
paramètre de type int et renvoyant un résultat de type double.
3. quelques méthodes :
/**
* description : une fonction invoquée sur un paramètre de type "int"
et retournant un résultat de type "double"
*/
@FunctionalInterface
public interface IntToDoubleFunction {
double applyAsDouble(int value);
}

2.16.2.9 L’interface fonctionnelle Predicate<T>


1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle générique encapsulant
une fonction désignant un prédicat acceptant en entrée un paramètre
de type générique T.
3. quelques méthodes :
@FunctionalInterface
public interface Predicate<T> {
// un prédicat acceptant un paramètre de type générique "T"
boolean test(T t);
}
4. remarque : cette interface fonctionnelle est utilisée par la méthode
filter() utilisée sur les streams (entre autres).

2.16.2.9.1 Exemple
package function;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Test {

158
public static void main(String[] args) {
List<Personne> personnes = Arrays.asList(
new Personne(10, "toto"),
new Personne(20, "titi"),
new Personne(30, "tata"),
new Personne(40, "tutu")
);

// définition d'un prédicat vérifiant si une personne a plus que 20 ans


Predicate<Personne> predicat = p -> p.getAge() > 20;

for (Personne p: personnes)


if(predicat.test(p))
System.out.println(p.getNom() + " a l'age requis ("
+ p.getAge() + " ans)");
}
}

2.16.2.10 L’interface fonctionnelle Consumer<T>


1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle générique acceptant en
entrée un seul paramètre de type générique T sans retourner un
résultat.
3. quelques méthodes :
@FunctionalInterface
public interface Consumer<T> {
/*
* description : une méthode acceptant un paramètre de type générique "T"
et ne retournant rien
*/
void accept(T t);
}

2.16.2.10.1 Exemple
package function;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class Test {

public static void main(String[] args) {

159
List<Personne> personnes = Arrays.asList(
new Personne(10, "toto"),
new Personne(20, "titi"),
new Personne(30, "tata"),
new Personne(40, "tutu")
);

System.out.println("Avant l'application d'un consumer :");


System.out.println(personnes);
System.out.println();

//définition d'une consumer ajoutant 13 à l'âge de chaque personne


Consumer<Personne> consumer = p -> p.setAge(p.getAge() + 13);

for (Personne p: personnes)


consumer.accept(p);

System.out.println("Après l'application d'un consumer :");


System.out.println(personnes);
System.out.println();
}
}

2.16.2.11 L’interface fonctionnelle IntConsumer


1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle acceptant en entrée un seul
paramètre de type int sans retourner un résultat.
3. quelques méthodes :
@FunctionalInterface
public interface IntConsumer {
/*
* description : une méthode acceptant un paramètre de type "int"
et ne retournant rien
*/
void accept(int value);
}

2.16.2.12 L’interface fonctionnelle Supplier<T>


1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle générique n’acceptant
aucun paramètre en entrée mais retournant un résultat de type
générique T.
3. quelques méthodes :

160
@FunctionalInterface
public interface Supplier<T> {
/*
* description : une méthode n'acceptant aucun paramètre en entrée
mais retournant un résultat de type "T"
*/
T get();
}

2.16.2.12.1 Exemple
package function;

import java.util.function.Supplier;

public class Test {

public static void main(String[] args) {


Supplier<String> stringSupplier = () -> "hello!";
Supplier<Personne> personneSupplier = () -> new Personne(50, "Dédé");

// retourne "hello"
System.out.println(stringSupplier.get());

// retourne une personne nommé "Dédé" et âgée de 50 ans


System.out.println(personneSupplier.get());
}
}

2.16.2.13 L’interface fonctionnelle IntSupplier


1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle n’acceptant aucun
paramètre en entrée mais retournant un résultat de type int.
3. quelques méthodes :
@FunctionalInterface
public interface IntSupplier {
/*
* description : une méthode n'acceptant aucun paramètre en entrée
mais retournant un résultat de type "int"
*/
int getAsInt();
}

161
2.16.2.14 L’interface fonctionnelle UnaryOperator<T>
1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle générique acceptant un
paramètre en entrée du type générique T et retournant un résultat
du même type générique T.

2.16.2.15 L’interface fonctionnelle BinaryOperator<T>


1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle générique acceptant deux
paramètres en entrée du même type générique T et retournant un
résultat du même type générique T.
3. quelques méthodes :
@FunctionalInterface
public interface BinaryOperator<T> {
/*
* description : une méthode acceptant deux opérandes du même type générique "T"
et retournant un résultat du même type générique "T"
*/
T apply(T t1, T t2);
}

2.16.2.16 L’interface fonctionnelle IntBinaryOperator


1. package : java.util.function (à importer manuellement)
2. description : une interface fonctionnelle acceptant deux
paramètres en entrée du type int et retournant un résultat
du type int.
3. quelques méthodes :
@FunctionalInterface
public interface IntBinaryOperator {
/*
* description : une méthode acceptant deux opérandes du même type "int"
et retournant un résultat du même type "int"
*/
int applyAsInt(int t1, int t2);
}

2.16.3 Lambda expressions

1. utilité : définir des méthodes du premier ordre en redéfinissant


une méthode abstraite d’une interface fonctionnelle sans avoir à

162
faire une classe anonyme → simplification de code et amélioration
de sa visibilité.
2. syntaxe : ([params]) -> corps :
• -> : l’opérateur de définition d’une lambda expression.
• si params consiste d’un seul paramètre, on peut omettre les par-
enthèses l’encapsulant (sauf si le paramètre est typé) ;
• si corps consiste d’une seule instruction retournant un résultat
on peut omettre les accolades définissant son bloc et le mot-clé
return ;
• on peut optionnellement typer les paramètres d’une lambda
expression : le type du paramètre sera inféré à partir de
l’instance de l’interface fonctionnelle utilisant la lambda
expression dans la définition de la méthode encapsulée).
3. exemple : définir une méthode de tri que l’on passera à la méthode
sort() de la classe Collections.
4. portée : les lambda expressions peuvent accéder à certains élé-
ments de leur environnement :
• des méthodes ;
• des attributs (modifiables au sein de l’expression) ;
• des variables locales et paramètres de méthodes (en lecture
uniquement et donc non modifiables au sein de l’expression).
5. exemples :
/*
* Lambda expression à :
- sans typage de paramètres
- syntaxe simplifiée du corps.
*/
(a, b) -> a + b;

/*
* Lambda expression à :
- sans typage de paramètres
- syntaxe complète du corps.
*/
d -> {return d.getAnneeCreation() >= 2012;}

/*
* Lambda expression à :
- typage de paramètres
- syntaxe simplifiée du corps.
*/
(DossierEntreprise d) -> d.getAnneeCreation() >= 2012;

/*
* Lambda expression à :

163
- typage de paramètres
- syntaxe complète du corps.
*/
(int a, int b) -> {return a + b;}

/*
* implémentation d'une interface par le biais d'une classe anonyme
et d'une lambda expression
*/
// L'interface fonctionnelle "Dialoguer"
package test;

@FunctionalInterface
public interface Dialoguer {
void parler(String question);
}

// La classe Test
package test;

public class Test2 {

public static void main(String[] args) {


// avec les classes anonymes
Dialoguer d = new Dialoguer() {
@Override
public void parler(String question) {
System.out.println("Tu as dis : " + question);
}
};

System.out.println("Avec une classe anonyme");


d.parler("Bonjour");

// avec les lambda expressions


d = question -> System.out.println("Tu as dis : " + question);

System.out.println("Avec une lambda expression");


d.parler("Bonjour");
}
}

164
2.16.4 Références de méthodes ou de constructeurs

1. utilité : définir la méthode abstraite d’une interface fonctionnelle


en référençant des méthodes ou constructeurs déjà existants ayant
la même signature que la méthode abstraite à définir.
2. éléments pouvant être référencés :
• s’il s’agit d’une interface : méthodes statiques/d’instance ;
• s’il s’agit d’une classe : méthodes statiques/d’instance ou con-
structeurs ;
• s’il s’agit d’une instance d’une classe : méthodes d’instance.
3. opérateur Java : opérateur de résolution de portée “::”.
4. syntaxe :
/*
* description : référencement d'une méthode ou d'un constructeur simple
*/
type::{methode | constructor}
5. exemples :
/*
* référencement de la méthode println() de la classe PrintStream
* de l'objet statique out de la classe System
* désignant la sortie standard
*/
System.out::println;

/*
* référencement de la méthode size() de la classe List<String>
*/
List<String>::size;
/*
* référencement du constructeur par défaut de la classe Arraylist<String>
*/
ArrayList<String>::new;

2.16.4.0.1 Exemple
package function;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.ToDoubleFunction;

public class Test {

public static void main(String[] args) {

165
//Conversion d'un String en un double en utilisant une lambda expression
ToDoubleFunction<String> stringToDoubleLambda = str -> Double.parseDouble(str);

/*
* Conversion d'un String en un double en utilisant
* une référence à une méthode statique d'une classe
*/
ToDoubleFunction<String> stringToDoubleRef = Double::parseDouble;

System.out.println("En utilisant une lambda expression");


System.out.println(stringToDoubleLambda.applyAsDouble("0.1234567"));

System.out.println("En utilisant une référence de méthode");


System.out.println(stringToDoubleRef.applyAsDouble("0.1234567"));
System.out.println();

// Affichage d'un String en utilisant une lambda expression


Consumer<String> stringPrinterLambda = str -> System.out.println(str);

/*
* Affichage d'un String en référençant la méthode d'instance
* de l'attribut statique "out" de la classe "System"
*/
Consumer<String> stringPrinterRef = System.out::println;

System.out.println("En utilisant une lambda expression");


stringPrinterLambda.accept("Bonjour !");

System.out.println("En utilisant une référence de méthode");


stringPrinterRef.accept("Bonjour !");
System.out.println();

// Référencement d'un constructeur pour instancier des entiers


Function<String, Personne> testNew = Personne::new;
Personne personne = testNew.apply("Dude");
System.out.println("Personne créée via le référencement d'un constructeur "
+ "par une interface fonctionnelle : " + personne);
}
}

2.17 Streams et collectors (Depuis Java 8)

2.17.1 Introduction

1. besoin :

166
• les collections/itérateux/tableau/flux stockent leurs éléments
et permettent un accès aux données en lecture/écriture.
• une taille croissante de ceux-ci fait que les traitements peuvent
devenir inefficaces, fastidieux et chronophages.
2. définition : les streams :
• une séquence d’éléments provenant d’une source, supportant
des opérations d’aggrégat séquentielles ou parallèles consistant
de traitements intermédiaires ou finales.
• une description déclarative des éléments telle que :
1. pas de stockage des données de la source : les éléments
sont calculés dynamiquement à la demande d’une manière
paresseuse (Lazy Computation)
2. pas d’accès aux données de la source et donc pas de mod-
ification de ceux-ci.
3. pas de réutilisation d’un stream une fois qu’il est utilisé
complètement : il faut recréer un nouveau stream à partir
de la même source pour ce faire.
4. pas de taille limite sur les streams sans terminaison ex-
plicite des traitements dessus (i.e. le stream reste ouvert en
face des traitements intermédiaires).
3. types de traitements :
• traitements intermédiaires :
1. des méthodes conservant l’état ouvert du stream associé à
une source et permettant de créer des streams intermédiaires
à partir de lui.
2. peuvent être chaînées jusqu’à tomber sur un traitement fi-
nal.
3. exemples :
– filtrage :
1. par un prédicat : filter(predicate);
2. enlevant des doublons : distinct();
3. réduisant la taille : limit(max);
4. sautant les n premiers élément : skip(n);
5. etc.
– projection :
1. en se basant sur une fonction : map(fonction).
2. en se basant sur une fonction retournant des streams spé-
cialisées : mapToInt(fonction), mapToDouble(fonction),
etc.
• tri : sorted().
• traitements terminales :
1. des méthodes consistant à terminer le calcul et à consom-
mer le stream en fournissant un résultat final.
2. exemples :
– application d’une procédure à tous les éléments d’un
stream : forEach(consumer).

167
– matching des éléments par un prédicat :
1. pour vérifier que tous les éléments ou n’importe
quel élément le satisfont : allMatch(predicate),
anyMatch(predicate);
2. en récupérant le premier ou n’importe quel élément
le satisfont : findFirst(); findAny().
• la réduction ou le regroupement via un collector : reduce(init,
accumulator), collect(collector).
4. calcul paresseux (lazy) :
• principe : le calcul des méthodes intermédiaires est chargé par
la JVM et est effectué si possible en une seule passe.
• contraste avec les collections/itérateurs/tableaux/flux : si
le calcul est décomposé en plusieurs passes, il n’y a pas
d’optimisation effectuée.
5. avantages :
• lisibilité du code : syntaxe déclarative pour le traitement des
données.
• amélioration des performances : calcul paresseux et sous cer-
taines conditions avec possibilité de parallélisation.
6. collector : une opération de réduction/regroupement d’éléments
d’une collection.

Figure 14: “Principe des streams en Java”

2.17.2 La classe Optional<T>

1. package : java.util (à importer manuellement)


2. description : une classe finale générique modélisant des objets
qui peuvent contenir une valeur générique non nulle ou pas
(i.e. décrivant la présence optionnelle d’une valeur)
3. quelques méthodes :
/*
* description : si une valeur non nulle est présente,
la retourne, sinon lève l'exception "NoSuchElementException"
*/
public T get();

/*
* description : retourne true si une valeur non nulle est présente,
false sinon

168
*/
public boolean isPresent();

/*
* description : retourne "other" si une valeur non nulle n'est pas présente
*/
public T orElse(T other);

2.17.2.1 La classe OptionalInt


1. package : java.util (à importer manuellement)
2. description : une classe finale modélisant des objets qui peuvent con-
tenir une valeur entière non nulle ou pas.
3. quelques méthodes :
/*
* description : si une valeur est présente, la retourne
*/
public int getAsInt();

2.17.2.2 La classe OptionalDouble


1. package : java.util (à importer manuellement)
2. description : une classe finale modélisant des objets qui peuvent con-
tenir une valeur double non nulle ou pas.
3. quelques méthodes :
/*
* description : si une valeur est présente, la retourne
*/
public double getAsDouble();

2.17.3 L’interface Stream<T>

1. package : java.util.stream (à importer manuellement)


2. description : une interface générique définissant les opérations
d’agrégat à implémenter par toute classe modélisant un stream
d’éléments de type générique T.
3. quelques méthodes :
// méthodes intermédiaires
/*filtration*/
/*
* description : filtrer les éléments du stream
de type générique "T" ne respectant pas le prédicat "predicate"
*/

169
Stream<T> filter(Predicate<? super T> predicate);

/*
* description : supprimer les doublons du stream
*/
Stream<T> distinct();

/*
* description : limiter la taille du stream à "maxSize" éléments
*/
Stream<T> limit(long maxSize);

/*
* description : sauter les n premiers éléments du stream
*/
Stream<T> skip(long n);

/*
* description : tri les éléments du stream selon l'ordre
naturel sur le type "T"
*/
Stream<T> sorted();

/*
* description : tri les éléments du stream selon l'ordre
défini par le comparateur "comparator" sur le type "T"
*/
Stream<T> sorted(Comparator<? super T> comparator);

/*projection*/
/*
* description : appliquer la méthode définie par l'interface fonctionnelle
"mapper" à chaque élément du stream et retourner le stream résultant.
* La méthode prend en entrée le type "T" des éléments du stream ou l'une de ses
superclasses et retourne un résultat de type "R"
*/
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

/*
* description : appliquer une méthode définie par l'interface fonctionnelle
"mapper" à chaque élément du stream et retourner un stream
d'entiers matérialisé par le type "IntStream".
* La méthode prend en entrée le type "T" des éléments du stream ou l'une de ses
superclasses et retourne un "int"
*/
IntStream mapToInt(ToIntFunction<?super T> mapper);

170
/*
* description : appliquer une méthode définie par l'interface fonctionnelle
"mapper" à chaque élément du stream et retourner un stream
de doubles matérialisé par le type "DoublStream".
* La méthode prend en entrée le type "T" des éléments du stream ou l'une de ses
superclasses et retourne un "double"
*/
DoubleStream mapToDouble(ToDoubleFunction<?super T> mapper);

// méthodes finales
/*
* description : applique la procédure définie par l'interface fonctionnelle
désignant un consumer "action"
*/
void forEach(Consumer<? super T> action);

/*
* description : vérifie si tous les éléments du stream respectent
le prédicat défini par l'interface fonctionnelle désignant un Predicate
"predicate"
*/
boolean allMatch(Predicate<? super T> predicate);

/*
* description : vérifie s'il existe au moins un élément du stream respectant
le prédicat défini par l'interface fonctionnelle désignant un Predicate
"predicate"
*/
boolean anyMatch(Predicate<? super T> predicate);

/*
* description : retourne une description éventuellement vide
du premier élément du stream
*/
Optional<T> findFirst();

/*
* description : retourne une description éventuellement vide
de n'importe quel élément du stream
*/
Optional<T> findAny();

/*
* description : accumule la valeur de chaque élément du stream
en utilisant l'opérateur binaire "accumulator" et en commençant

171
depuis la valeur initiale définie par "identity"
et retourne le résultat
*/
T reduce(T identity, BinaryOperator<T> accumulator);

/*
* description : compte le nombre d'éléments dans le stream
*/
long count();

/*
* description : effectue une opération de réduction sur les éléments
du stream courant en utilisant le Collector "collector"
*/
<R,A> R collect(Collection<? super T, A, R> collector);

/*
* description : crée un stream contenant les éléments "values"
de type générique "T"
*/
static <T> Stream<T> of(T... values);

/*
* description : retourne une séquence ordonnée infinie en appliquant
la fonction unaire "f" itérativement sur les éléments générés
en commençant par l'élément initial "seed"
*/
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f);

/*
* description : retourne une séquence infinie non ordonnée où
chaque élément du stream est généré par le supplier "s"
* remarque : utilisé pour générer des streams constants ou aléatoires, etc.
*/
static <T> Stream<T> generate(Supplier<T> s);

2.17.3.1 L’interface IntStream


1. package : java.util.stream (à importer manuellement)
2. description : une interface définissant les opérations d’agrégat à
implémenter par toute classe modélisant un stream de int.
3. quelques méthodes :
/*
* description : opération de réduction retournant
la somme des éléments du stream

172
*/
int sum();

/*
* description : opération de réduction retournant
la possiblement la moyenne des éléments du stream
*/
OptionalDouble average();

2.17.3.2 L’interface DoubleStream


1. package : java.util.stream (à importer manuellement)
2. description : une interface définissant les opérations d’agrégat à
implémenter par toute classe modélisant un stream de double.
3. quelques méthodes :
/*
* description : opération de réduction retournant
la somme des éléments du stream
*/
double sum();

/*
* description : opération de réduction retournant
la possiblement la moyenne des éléments du stream
*/
OptionalDouble average();

2.17.3.3 L’interface Collector<T, A, R>


1. package : java.util.stream (à importer manuellement)
2. description : une interface générique définissant la signature
de divers méthodes de réduction mutables sur des séquences
d’éléments de type générique T, en utilisant un accumulateur de
type générique A et en retournant un résultat de type générique R.

2.17.3.4 La classe Collectors


1. package : java.util.stream (à importer manuellement)
2. description : une classe finale fournissant des implémentations sta-
tiques de plusieurs méthodes de réduction mutables définies par
l’interface générique Collector<T, A, R>.
3. quelques méthodes :
/*
* description : retourne un Collector permettant de calculer la moyenne

173
arithmétique d'une séquence d'éléments de type "T"
et la retourner sous forme d'un "double"
*/
public static <T> Collector<T, ?, Double> averagingDouble(
ToDoubleFunction<? super T> mapper);

/*
* description : retourne un Collector permettant de calculer la moyenne
arithmétique d'une séquence d'éléments de type "T"
et la retourner sous forme d'un "double"
*/
public static <T> Collector<T, ?, Double> averagingInt(
ToIntFunction<? super T> mapper);

/*
* description : retourne un Collector implémentant une opération de
"group by" sur les éléments de la séquence de type "T"
selon la fonction de classification "classifier"
et retournant les résultats dans un Map
*/
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(
Function<? super T, ? extends K> classifier);

/*
* description : retourne un Collector permettant d'accumuler
les éléments de la séquence de type "T" et les retournant
en tant que liste
*/
public static <T> Collector<T, ?, List<T>> toList();

2.17.4 Création de streams

// en extension
Stream<T> myStream = Stream.of(/*valeurs de type T séparées par des ,*/ )

// à partir d'une collection sans parallélisation


Stream<T> myStream = myCollection.stream();

// à partir d'une collection avec parallélisation


Stream<T> myStream = myCollection.parallelStream();

// à partir d'un tableau


Stream<T> myStream = Arrays.stream(T[] array);

// à partir d'un fichier

174
Stream<String> myLines = Files.lines(Paths.get("monFichier"));

// à partir d'une fonction (séquence infinie)


Stream<T> myStream = Stream.iterate(T seed, UnaryOperator<T> f);

2.17.4.1 Exemple 1 de création de streams à partir d’une collection

// La classe DossierEntreprise
package streams;

public class DossierEntreprise {


/*attributes*/
private String identification;
private int anneeCreation;
private String email;
private int nbSalaries;

/*constructors*/
public DossierEntreprise(String identification, int anneeCreation,
String email, int nbSalaries) {
this.identification = identification;
this.anneeCreation = anneeCreation;
this.email = email;
this.nbSalaries = nbSalaries;
}

/*methods*/
//Getters & setters
public String getIdentification() {return this.identification;}
public void setIdentification(String identification) {
this.identification = identification;
}

public int getAnneeCreation() {return this.anneeCreation;}


public void setAnneeCreation(int anneeCreation) {
this.anneeCreation = anneeCreation;
}

public String getEmail() {return this.email;}


public void setEmail(String email) {this.email = email;}

public int getNbSalaries() {return this.nbSalaries;}


public void setNbSalaries(int nbSalaries) {this.nbSalaries = nbSalaries;}
}

175
// La classe AgenceCoordination
package streams;

import java.util.ArrayList;
import java.util.Collections;
import java.util.stream.Collectors;

public class AgenceCoordination {


/*attributes*/
private ArrayList<DossierEntreprise> entreprises = new ArrayList<>();

/*constructors*/
public AgenceCoordination() {}

/*methods*/
public void accueillerEntreprise(DossierEntreprise d) {
if(!entreprises.contains(d))
entreprises.add(d);
}

/*
* calcul du nombre moyen des salariés des entreprises
* à partir d'une annee en utilisant un itérateur
*/
public double nbMoyenSalariesIt(int annee) {
double nb = 0;
int nbEntreprises = 0;

for (DossierEntreprise dossier: entreprises) {


if(dossier.getAnneeCreation() >= annee) {
nbEntreprises++;
nb += dossier.getNbSalaries();
}
}

if(nbEntreprises > 0)
return nb / (double) nbEntreprises;

else return 0;
}

/*
* calcul du nombre moyen des salariés des entreprises
* à partir d'une annee en utilisant un stream
* sans parallélisation

176
*/
public double nbMoyenSalariesStream(int annee) {
return entreprises.stream()
.filter(d -> d.getAnneeCreation()>=annee)
.mapToDouble(DossierEntreprise::getNbSalaries)
.average()
.getAsDouble();
}

/*
* calcul du nombre moyen des salariés des entreprises
* à partir d'une annee en utilisant un stream
* avec parallélisation
*/
public double nbMoyenSalariesParallelStream(int annee) {
return entreprises.parallelStream()
.filter(d -> d.getAnneeCreation()>=annee)
.mapToInt(DossierEntreprise::getNbSalaries)
.average()
.getAsDouble();
}

/*
* calcul du nombre moyen des salariés des entreprises
* à partir d'une annee en utilisant un stream
* sans parallélisation et un Collector
*/
public double nbMoyenSalariesCollector(int annee) {
return entreprises.stream()
.filter(d -> d.getAnneeCreation() >= annee)
.collect(Collectors.averagingInt(DossierEntreprise::getNbSalaries))
.doubleValue();
}

/*
* calcul du nombre moyen des salariés des entreprises
* à partir d'une annee en utilisant un stream
* avec parallélisation et un Collector
*/
public double nbMoyenSalariesParallelCollector(int annee) {
return entreprises.parallelStream()
.filter(d -> d.getAnneeCreation() >= annee)
.collect(Collectors.averagingInt(DossierEntreprise::getNbSalaries))
.doubleValue();
}

177
// affiche les emails des entreprises
public void afficheMails() {
entreprises.stream()
.forEach(d -> System.out.println(d.getEmail()));
}

// affiche les emails des entreprises à partir d'une annee


public void afficheMails(int anneeCreation) {
entreprises.stream()
.filter(d -> d.getAnneeCreation() >= anneeCreation)
.map(d -> d.getEmail())
.forEach(email -> System.out.println(email));
}

// tri les entreprises par email


public void triParEmail() {
Collections.sort(entreprises,
(d1, d2) -> d1.getEmail().compareTo(d2.getEmail()));
}
}

// La classe Test
package streams;

public class Test {

public static void main(String[] args) {


AgenceCoordination ag = new AgenceCoordination();
DossierEntreprise e1 = new DossierEntreprise("00034", 2012,
"cheminvert@op.fr", 10);
DossierEntreprise e2 = new DossierEntreprise("00035", 2008,
"caramelo@op.fr", 10);
DossierEntreprise e3 = new DossierEntreprise("00036", 2014,
"pizzaDelice@op.fr", 20);
DossierEntreprise e4 = new DossierEntreprise("00037", 2016,
"burgerFamille@op.fr", 20);
DossierEntreprise e5 = new DossierEntreprise("00038", 2017,
"saladeRusse@op.fr", 10);
DossierEntreprise e6 = new DossierEntreprise("00039", 2008,
"sushiInternational@op.fr", 25);
DossierEntreprise e7 = new DossierEntreprise("00040", 2009,
"orientalFeast@op.fr", 30);
DossierEntreprise e8 = new DossierEntreprise("00041", 2010,
"russianClassic@op.fr", 15);
DossierEntreprise e9 = new DossierEntreprise("00042", 2013,
"empanadasLocal@op.fr", 10);

178
DossierEntreprise e10 = new DossierEntreprise("00043", 2019,
"africanSpecials@op.fr", 10);

ag.accueillerEntreprise(e1);
ag.accueillerEntreprise(e2);
ag.accueillerEntreprise(e3);
ag.accueillerEntreprise(e4);
ag.accueillerEntreprise(e5);
ag.accueillerEntreprise(e6);
ag.accueillerEntreprise(e7);
ag.accueillerEntreprise(e8);
ag.accueillerEntreprise(e9);
ag.accueillerEntreprise(e10);

System.out.println(
"Moyen du nombre des salariés pour chaque entreprise créée après 2012");
System.out.println(
"====================================================================");

long start = System.currentTimeMillis();


System.out.println(ag.nbMoyenSalariesIt(2012));
System.out.println("Avec un itérateur : " +
(System.currentTimeMillis() - start) +
" millisecondes"); // 3 ms

start = System.currentTimeMillis();
System.out.println(ag.nbMoyenSalariesStream(2012));
System.out.println("Avec un stream sans parallélisation : " +
(System.currentTimeMillis() - start) +
" millisecondes"); // 29 ms

start = System.currentTimeMillis();
System.out.println(ag.nbMoyenSalariesParallelStream(2012));
System.out.println("Avec un stream avec parallélisation : " +
(System.currentTimeMillis() - start) +
" millisecondes"); // 13 ms

start = System.currentTimeMillis();
System.out.println(ag.nbMoyenSalariesCollector(2012));
System.out.println(
"Avec un stream, sans parallélisation et utilisant un collector : " +
(System.currentTimeMillis() - start) +
" millisecondes"); // 4 ms

start = System.currentTimeMillis();
System.out.println(ag.nbMoyenSalariesParallelCollector(2012));

179
System.out.println(
"Avec un stream, avec parallélisation et utilisant un collector : " +
(System.currentTimeMillis() - start) +
" millisecondes"); // 1 ms
System.out.println();

System.out.println("Email pour chaque entreprise");


ag.afficheMails();
System.out.println();

System.out.println("Email pour chaque entreprise créée après 2012");


ag.afficheMails(2012);
System.out.println();

System.out.println("Tri des entreprises par leurs emails");


ag.triParEmail();
ag.afficheMails();
}
}

2.17.4.2 Exemple 2 de création de streams à partir d’une collection

// L'énumération Couleur
package streams;

public enum Couleur {


MARRON("marron"),
BLEU("bleu"),
VERT("vert"),
VERRON("verron"),
ROUGE("rouge"),
INCONNU("non déterminé");

/*attributes*/
private String name = "";

/*constructors*/
private Couleur(String name) {this.name = name;}

/*methods*/
//toString
@Override
public String toString() {
return this.name;

180
}
}

// La classe Personne
package streams;

public class Personne {


/*attributes*/
private String nom, prenom;
private Double taille, poids;
private Couleur yeux;

/*constructors*/
public Personne() {
nom = prenom = "";
taille = poids = 0.0d;
yeux = Couleur.INCONNU;
}

public Personne(String nom, String prenom, double taille, double poids,


Couleur yeux) {
this.nom = nom;
this.prenom = prenom;
this.taille = taille;
this.poids = poids;
this.yeux = yeux;
}

/*methods*/
//Getters & setters
public String getNom() {
return nom;
}

public void setNom(String nom) {


this.nom = nom;
}

public String getPrenom() {


return prenom;
}

public void setPrenom(String prenom) {


this.prenom = prenom;
}

181
public Double getTaille() {
return taille;
}

public void setTaille(Double taille) {


this.taille = taille;
}

public Double getPoids() {


return poids;
}

public void setPoids(Double poids) {


this.poids = poids;
}

public Couleur getYeux() {


return yeux;
}

public void setYeux(Couleur yeux) {


this.yeux = yeux;
}

//toString
@Override
public String toString() {
return prenom + " " + nom + " :\n" +
"Taille : " + taille + " mètre(s), Poids : " + poids + " kilo(s)\n" +
"Couleur des yeux : " + yeux;
}
}

// La classe Test
package streams;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Test {

public static void main(String[] args) {

182
//créer un stream infini
Stream.iterate(1, x -> x + 1)
.forEach(System.out::println);

//limiter un stream infini à 100 éléments


Stream.iterate(1d, x -> x + 1)
.limit(100)
.forEach(System.out::println);

List<Personne> personnes = Arrays.asList(


new Personne("A", "Nicolas", 1.80, 70.0, Couleur.BLEU),
new Personne("B", "Nicole", 1.56, 50.0, Couleur.VERRON),
new Personne("C", "Germain", 1.75, 65.0, Couleur.VERT),
new Personne("D", "Michel", 1.68, 50.0, Couleur.ROUGE),
new Personne("E", "Cyrille", 1.96, 65.0, Couleur.BLEU),
new Personne("F", "Denis", 2.10, 120.0, Couleur.ROUGE),
new Personne("G", "Olivier", 1.90, 90.0, Couleur.VERRON)
);

//créer un stream des personnes de plus de 50 kilos


Stream<Personne> stream = personnes.stream();

System.out.println("Avant tout filtre");


System.out.println("==================");
stream.forEach(System.out::println);
System.out.println();

System.out.println(
"Filtrer uniquement les personnes pesant plus que 50 kilos");
System.out.println(
"===========================================================");

//recréer le stream car il est consommé par le forEach précédent


stream = personnes.stream();
stream
.filter(p -> p.getPoids() > 50)
.forEach(System.out::println);
System.out.println();

System.out.println(
"Projetter uniquement les poids des personnes pesant plus que 50 kilos");
System.out.println(
"=====================================================================");

//recréer le stream car il est consommé par le forEach précédent


stream = personnes.stream();

183
stream
.filter(p -> p.getPoids() > 50)
.map(Personne::getPoids)
.forEach(System.out::println);
System.out.println();

System.out.println("Calculer la somme des poids des personnes filtrées");


System.out.println("===================================================");
//recréer le stream car il est consommé par le forEach précédent
stream = personnes.stream();

double sum = stream


.filter(p -> p.getPoids() > 50)
.map(Personne::getPoids)
.reduce(0.0d, (poids1, poids2) -> poids1 + poids2);
System.out.println(sum + " kilo(s)");
System.out.println();

System.out.println(
"Calculer la somme des poids des personnes pesant plus que 250 kilos");
System.out.println(
"====================================================================");
//recréer le stream car il est consommé par le forEach précédent
stream = personnes.stream();

Optional<Double> optionalSum = stream


.filter(p -> p.getPoids() > 250)
.map(Personne::getPoids)
.reduce((poids1, poids2) -> poids1 + poids2);

if(optionalSum.isPresent())
System.out.println(optionalSum.get() + " kilo(s)");
else
System.out.println("Personne pèse plus que 250 kilos !");
System.out.println();

System.out.println(
"Récupérer le nombre de personnes pesant plus que 50 kilos");
System.out.println(
"=========================================================");
//recréer le stream car il est consommé par le forEach précédent
stream = personnes.stream();

long count = stream


.filter(p -> p.getPoids() > 50)
.map(Personne::getPoids)

184
.count();
System.out.println(count + " personne(s)");
System.out.println();

System.out.println(
"Récupérer la liste des poids des personnes pesant plus que 50 kilos");
System.out.println(
"===================================================================");
//recréer le stream car il est consommé par le forEach précédent
stream = personnes.stream();

List<Double> poids = stream


.filter(p -> p.getPoids() > 50)
.map(Personne::getPoids)
.collect(Collectors.toList());
System.out.println("liste des poids : " + poids);
System.out.println();
}
}

2.17.4.3 Exemple de création de streams à partir d’un fichier


// le contenu du fichier test/data.txt
/*
* 1830 / Le rouge et le noir
* 1837 | Le rose et le vert
* 1830 / Le livre des oracles
*/

// La classe Paire
package streams;

public class Paire<T1, T2> {


/*attributes*/
private T1 first;
private T2 second;

/*constructors*/
public Paire(T1 first, T2 second) {
setFirst(first);
setSecond(second);
}

/*methods*/
//Getters & setters

185
public T1 getFirst() {return this.first;}
public void setFirst(T1 first) {this.first = first;}

public T2 getSecond() {return this.second;}


public void setSecond(T2 second) {this.second = second;}

//toString
@Override
public String toString() {
return "("+first+", "+second+")";
}
}

// La classe Test
package streams;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Collectors;

public class Test {

public static void groupBooksByYear(String fileName) throws IOException {


System.out.println(Files.lines(Paths.get(fileName))
.map(line -> {
System.out.println(line);
String[] elements = line.split("[|/]");
return new Paire<String, String>(elements[0].trim(),
elements[1].trim());
})
.collect(Collectors.groupingBy(Paire<String, String>::getFirst)));
}

public static void main(String[] args) {


try {
groupBooksByYear("test/data.txt");
} catch(IOException e) {
e.printStackTrace();
}
}
}

// output
/*
*{

186
1830=[Paire [first=1830, second=Le rouge et le noir],
Paire [first=1830, second=Le livre des oracles]],
1837=[Paire [first=1837, second=Le rose et le vert]]
}
*/

2.17.4.4 Exemple de création de streams à partir d’une fonction


package streams;

import java.util.stream.Stream;

public class Test {

public static void main(String[] args) {


// création d'un stream infini ne contenant que des entiers pairs
Stream<Integer> pairs = Stream.iterate(0, e -> e + 2);

// limitation du stream pour afficher les 4 premiers éléments


// pairs uniquement
pairs
.limit(4)
.forEach(System.out::println); // affiche 0, 2, 4, 6
}
}

2.18 La réflexivité (l’introspection)

2.18.1 Introduction

1. principe : des classes et méthodes permettant à l’exécution :


• l’accès aux informations sur des classes/objets : attributs, méth-
odes, constructeurs.
• la manipulation des objets des classes : modification d’attributs,
invocation de méthodes/constructeurs.
• limites en Java : une classe ne peut pas être modifiée à
l’exécution (e.g. ajout d’attributs ou de méthodes).
2. utilisation : réalisation de :
• débogueurs ;
• interprètes ;
• inspecteurs d’objets ;
• navigateurs de classes (class browsers) ;
• services particuliers, par exemple :
1. sérialisation (sauvegarde d’objets) ;
2. éditeurs d’objets ;

187
3. intercession (i.e. l’interception d’appels).
3. contextes d’utilisation :
• inspection de méthodes/d’objets ;
• création d’objets selon des types non connu au préalable ;
• invocation de méthodes/constructeurs ;
• accès aux modifiers des entités ;
• inspection des superclasses/super-interfaces, des interfaces,
etc. ;
• création et manipulation de tableaux ;
• création de proxys de classes/d’instances pour intercepter des
appels et ajouter du comportement (e.g. trace automatique)

2.18.2 La classe Class<T>

1. package : java.lang (importée automatiquement)


2. description : classe finale générique désignant le type T à
l’exécution d’une entité (classe interface, énumération, tableau, etc.)
et l’ensemble de ses propriétés (attributs, méthodes, constructeurs, etc.).
3. quelques méthodes :
/*
* description : retourne le nom de l'entité
*/
public String getName();

/*
* description : retourne la superclasse/super-interface de l'entité courante
*/
public Class<? super T> getSuperclass();

/*
* description : retourne le tableau des interfaces implémentées/étendues
par l'entité courante
*/
public Class<?> getInterfaces();

/*
* description : retourne l'attribut publique ayant le nom "name"
ou lève une exception NoSuchFieldException si le nom fournis est inconnu
*/
public Field getField(String name) throws NoSuchFieldException,
SecurityException;

/*
* description : retourne le tableau des attributs publiques de l'entité courante

188
(uniquement ceux directement définis au sein de l'entité courante)
*/
public Field[] getDeclaredFields() throws SecurityException;

/*
* description : retourne le tableau des attributs publiques de l'entité courante
(hérités y inclus)
*/
public Field[] getFields() throws SecurityException;

/*
* description : retourne la méthode publique ayant le nom "name"
éventuellement paramétrée par les paramètres ayant les types "parameterTypes"
ou lève une exception NoSuchMethodException si les spécifications
fournies ne désignent aucune méthode accessible
*/
public Field getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException;

/*
* description : retourne le tableau des méthodes publiques de l'entité courante
(héritées y inclus)
*/
public Method[] getMethods() throws SecurityException;

/*
* description : retourne le constructeur publique
éventuellement paramétré par les paramètres ayant les types "parameterTypes"
ou lève une exception NoSuchMethodException si les spécifications
fournies ne désignent aucun constructeur accessible
*/
public Field getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException;

/*
* description : retourne le tableau des constructeurs publiques de
l'entité courante
(héritées y inclus)
*/
public Method[] getConstructors() throws SecurityException;

/*
* description : retourne le type à l'exécution "Class" de la classe
dont le nom est défini par le String "className"
ou lève une exception ClassNotFoundException si le nom fournis est inconnu
*/

189
public static Class<?> forName(String className) throws ClassNotFoundException;

2.18.3 La classe AccessibleObject

1. package : java.lang.reflect (à importer manuellement)


2. description : classe modélisant toute entité désignant un objet acces-
sible (méthodes, constructeurs, champs).
3. quelques méthodes :
/*
* spécifie l'accessibilité de l'objet courant
*/
public void setAccessibility(boolean flag);

2.18.4 La classe Executable

1. package : java.lang.reflect (à importer manuellement)


2. description : classe abstraite modélisant toute entité exécutable
(méthodes et constructeurs).

2.18.5 La classe Field<T>

1. package : java.lang.reflect (à importer manuellement)


2. description : une classe modélisant un attribut d’une classe/interface,
fournissant des informations dessus (type à l’exécution, type à l’exécution
de sa classe, sa valeur pour un objet, etc.) et permettant sa lecture et
sa modification si sa portée le permet.
3. quelques méthodes :
/*
* description : retourne la valeur de l'attribut courant pour l'objet "objet"
ou lève l'exception IllegalArgumentException si l'objet ne possède pas
l'attribut, ou l'exception IllegalAccessException si l'on n'a pas
le droit à accéder à cet attribut
*/
public Object get(Object obj) throws IllegalArgumentException,
IllegalAccessException;

/*
* description : retourne le nom de l'attribut courant
*/
public String getName();

/*

190
* description : retourne le type à l'exécution de l'attribut courant
*/
public Class<?> getType();

2.18.6 La classe Method

1. package : java.lang.reflect (à importer manuellement)


2. description : une classe modélisant une méthode d’une classe/interface,
fournissant des informations dessus (paramètres, type de retour, excep-
tions) et permettant son invocation si sa portée le permet.
3. quelques méthodes :
/*
* description : retourne le nom de la méthode
*/
public String getName();

/*
* description : retourne le nombre des paramètres de la méthode
*/
public int getParameterCount();

/*
* description : retourne le tableau des types à l'exécution des
paramètres de la méthode par l'ordre de leur déclaration
*/
public Class<?>[] getParameterTypes();

/*
* description : retourne le type à l'exécution du type de retour
de la méthode
*/
public Class<?> getReturnType();

/*
* description : retourne le tableau des types à l'exécution des
exceptions transmises par la méthode par l'ordre de leur déclaration
*/
public Class<?>[] getExceptionTypes();

/*
* description : invoquer la méthode sur l'objet "obj"
éventuellement en fournissant les attributs "args"
*/
public Object invoke(Object obj, Object... args)

191
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException;

2.18.7 La classe Constructor<T>

1. package : java.lang.reflect (à importer manuellement)


2. description : une classe modélisant un constructeur d’une classe,
fournissant des informations dessus (paramètres, exceptions) et permet-
tant son invocation si sa portée le permet.
3. quelques méthodes :
/*
* description : utiliser le constructeur pour instancier la classe
éventuellement en fournissant les attributs "initargs"
*/
public T newInstance(Object... initargs)
throws InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException;

2.18.8 L’interface AnnotatedElement

1. package : java.lang.reflect (à importer manuellement)


2. description : une interface fournissant les méthodes à implémenter
par toute classe modélisant un élément annoté.
3. quelques méthodes :
/*
* retourne l'annotation de type statique "T" et
de type à l'exécution Class<T> de l'élément annoté
sinon retourne null si l'annotation n'existe pas
*/
<T extends Annotation> T getAnnotation(Class<T> annotationClass)

/*
* retourne un tableau des annotations de l'élément annoté
(y inclus celles héritées)
*/
Annotation[] getAnnotations()

/*
* retourne un tableau des annotations de l'élément annoté
(uniquement celles directement annotant l'élément annoté)

192
*/
Annotation[] getDeclaredAnnotations()

/*
* retourne true si l'élément est annoté par une annotation ayant
un type à l'exécution spécifié par "annotationClass"
sinon retourne false
*/
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

2.18.9 Exemples

2.18.9.1 Exemple d’inspection de méthodes par introspection


// La classe Test
package reflection;

import java.lang.reflect.Method;
import java.util.Scanner;

public class Test {

public static void displayPublicMethods(Class<?> c) {


Method[] methods = c.getMethods();

for(Method method: methods) {


String mName = method.getName();
Class<?> mType = method.getReturnType();
Class<?>[] mParamTypes = method.getParameterTypes();
Class<?>[] mExceptions = method.getExceptionTypes();

StringBuffer buffer = new StringBuffer();


buffer.append(mType + " " + mName + "(");

if (mParamTypes.length > 0) {
for (Class<?> paramType: mParamTypes)
buffer.append(paramType.getName() + ", ");
buffer.delete(buffer.toString().length() - 2, buffer.toString()
.length());
}

buffer.append(")");

if (mExceptions.length > 0) {
buffer.append(" throws ");

193
for (Class<?> exception: mExceptions)
buffer.append(exception.getName() + ", ");
buffer.delete(buffer.toString().length() - 2, buffer.toString()
.length());
}

buffer.append(";");

System.out.println(buffer.toString());
}
}

public static void main(String[] args) {


boolean correct = false;
Scanner scanner = new Scanner(System.in);

while(!correct) {
System.out.println("Saisir un nom de classe");
String nomClasse = scanner.nextLine();
try {
Class<?> c = Class.forName(nomClasse);
displayPublicMethods(c);
} catch (ClassNotFoundException e) {
correct = false;
continue;
}
correct = true;
}
}
}

2.18.9.2 Exemple d’inspection d’objets par introspection


// La classe Produit
package reflection;

public abstract class Produit {


/*attributes*/
private String reference;
private String designation;
public double prixHT; // pour tester getField()

/*constructors*/
public Produit() {}
public Produit(String reference, String designation, double prixHT) {

194
this.reference = reference;
this.designation = designation;
this.prixHT = prixHT;
}

/*methods*/
//abstract methods
public abstract double leprixTTC();

//Getters & setters


public String getReference() {
return reference;
}
public void setReference(String reference) {
this.reference = reference;
}
public String getDesignation() {
return designation;
}
public void setDesignation(String designation) {
this.designation = designation;
}
public double getPrixHT() {
return prixHT;
}
public void setPrixHT(double prixHT) {
this.prixHT = prixHT;
}

//toString
@Override
public String toString() {
return "Reference: " + reference +
"\nDesignation: " + designation +
"\nPrix HT: " + prixHT +
"\nPrix TTC: " + this.leprixTTC();
}
}

// La classe ProduitTNormal
package reflection;

public abstract class ProduitTNormal extends Produit {

/*constructors*/
public ProduitTNormal() {}

195
public ProduitTNormal(String reference, String designation, double prixHT) {
super(reference, designation, prixHT);
}

/*methods*/
//implemented abstract methods of Produit
@Override
public double leprixTTC() {
return this.getPrixHT() * 1.196;
}

// La classe Livre
package reflection;

public class Livre extends ProduitTNormal {


/*attributes*/
public String editeur; // pour tester getField()

/*constructors*/
public Livre() {}
public Livre(String reference, String designation, double prixHT,
String editeur) {
super(reference, designation, prixHT);
this.editeur = editeur;
}

/*methods*/
//Getters & setters
public String getEditeur() {
return editeur;
}
public void setEditeur(String editeur) {
this.editeur = editeur;
}

//toString
@Override
public String toString() {
return super.toString() +
"\nEditeur: " + editeur;
}
}

// La classe Test

196
package reflection;

import java.lang.reflect.Field;

public class Test {

public static void main(String[] args) {


Produit p = new Livre("X23", "Paroles de Prévert", 25, "Folio");

Class<?> pClass = p.getClass();


try {
Field pEditeur = pClass.getField("editeur");
Object pEditeurValue = pEditeur.get(p);

Field pPrixHT = pClass.getField("prixHT");


Object pPrixHTValue = pPrixHT.get(p);
System.out.println(pEditeur.getName() + ": " + pEditeurValue);
System.out.println(pPrixHT.getName() + ": " + pPrixHTValue);
} catch (NoSuchFieldException | SecurityException
| IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}

2.18.9.3 Exemple d’inspection générale par introspection


package reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {

public static void main(String[] args) throws Exception {


Class<?> str = String.class;
Class<?> strSuper = str.getSuperclass();
Class<?>[] interfaces = str.getInterfaces();
Field[] attributes = str.getDeclaredFields();
Constructor<?>[] constructors = str.getConstructors();
Method[] methodes = str.getMethods();

System.out.println("Class: " + str);


System.out.println("Extends: " + strSuper);

197
System.out.println("Implements: " + interfaces.length + " interfaces: ");
for (Class<?> inter: interfaces)
System.out.println(inter);
System.out.println();

System.out.println("Attributes: " + attributes.length +


" public attributes (this class only): ");
for(Field field: attributes)
System.out.println(field.getType() + " " + field.getName());
System.out.println();

System.out.println("Constructors: " + constructors.length +


" public constructors: ");
for(Constructor<?> constructor: constructors) {
System.out.println(constructor);
Class<?>[] pTypes = constructor.getParameterTypes();

for(Class<?> p: pTypes)
System.out.println(p.getName());

System.out.println("--------------------------------------\n");
}
System.out.println();

System.out.println("Methods: " + methodes.length +


" public methods (inherited included): ");
for(Method method: methodes) {
System.out.println(method);
Class<?>[] pTypes = method.getParameterTypes();

for(Class<?> p: pTypes)
System.out.println(p.getName());

System.out.println("--------------------------------------\n");
}
System.out.println();
}
}

2.18.9.4 Exemple de création d’objets par introspection


package reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

198
import java.util.Scanner;

public class Test {

public static void main(String[] args) {


Scanner scanner = new Scanner(System.in);

try {

System.out.println("Livre ou Aliment ?");


String nomClasse = scanner.nextLine();
Class<?> classe = Class.forName(nomClasse);

Constructor<?> defaultConstructor = classe.getConstructor();


Object defaultObj = defaultConstructor.newInstance();
System.out.println("Class: " + defaultObj.getClass());
System.out.println(defaultObj);

System.out.println("Livre ou Aliment ?");


nomClasse = scanner.nextLine();
classe = Class.forName(nomClasse);

Constructor<?> paramConstructor = classe.getConstructor(


String.class, String.class, double.class, String.class);
Object paramObj = paramConstructor.newInstance("xx", "Paroles", 12,
"Folio");
System.out.println("Class: " + paramObj.getClass());
System.out.println(paramObj);
} catch (ClassNotFoundException | NoSuchMethodException |
SecurityException | InstantiationException |
IllegalAccessException | IllegalArgumentException |
InvocationTargetException e) {
e.printStackTrace();
}
}
}

2.18.9.5 Exemple d’invocation de méthodes par introspection


package reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner;

199
public class Test {

public static void main(String[] args) {


Produit livre = new Livre("xx", "Paroles", 12, "Folio");
Class<?> livreClasse = livre.getClass();
System.out.println("Classe: " + livreClasse.getName());
System.out.println(livre);
System.out.println("Les méthodes publiques du livre : ");
displayPublicMethods(livreClasse);

Scanner scanner = new Scanner(System.in);


System.out.println("Saisir une méthode sans argument à invoquer : ");
String nomMethode = scanner.nextLine();

try {
Method methode = livreClasse.getMethod(nomMethode);
Object prixTTC = methode.invoke(livre);
System.out.println("Par introspection, Prix TTC: " + prixTTC + "\n");

System.out.println("Les méthodes publiques du livre : ");


displayPublicMethods(livreClasse);
System.out.println("Saisir une méthode avec arguments à invoquer : ");
nomMethode = scanner.nextLine();

methode = livreClasse.getMethod(nomMethode, String.class);


methode.invoke(livre, "Dude");
System.out.println(
"Modification de l'état de l'objet par introspection : ");
System.out.println(livre);
} catch (NoSuchMethodException | SecurityException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
e.printStackTrace();
}
}
}

2.18.9.6 Exemple d’utilisation d’annotations et d’introspection pour


créer un outil de tests unitaires
// L'énumération NiveauRisque
package reflection;

public enum NiveauRisque {


FAIBLE,

200
MOYEN,
ELEVE;
}

// L'annotation Risk
package reflection;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Risk {
NiveauRisque value() default NiveauRisque.MOYEN;
}

// La classe Foo a tester


package reflection;

public class Foo {


@Risk(NiveauRisque.FAIBLE)
public static void m1() {
System.out.println("m1");
}

public static void m2() {


System.out.println("m2");
}

@Risk
public static void m3() {
throw new RuntimeException("Boom");
}

public static void m4() {


System.out.println("m4");
}

@Risk
public static void m5() {
System.out.println("m5");
}

public static void m6() {

201
System.out.println("m6");
}

@Risk(NiveauRisque.ELEVE)
public static void m7() {
throw new RuntimeException("Crash");
}

public static void m8() {


System.out.println("m8");
}
}

// La classe de l'outil de test


package reflection;

import java.lang.reflect.Method;

public class Test {

public static void main(String[] args) throws Exception {


int passed = 0, failed = 0;

for (Method m: Class.forName("reflection.Foo").getMethods()) {


if (m.isAnnotationPresent(Risk.class) &&
m.getAnnotation(Risk.class).value() != NiveauRisque.FAIBLE) {
try {
m.invoke(null);
passed++;
} catch(Throwable e) {
System.out.println("Test " + m + " failed: " + e.getCause());
failed++;
}
}
}

System.out.println("Passed: " + passed + " Failed: " + failed);


}
}

202
2.19 Les dates (Nouvelle API depuis Java 8)

2.19.1 Introduction

1. temps machine ou timestamp ou temps Unix : nombre de secondes


écoulées depuis 01/01/1970.
2. temps humain : le temps tel qu’il est vécu par les humains, avec
plusieurs champs (jour, mois, année, etc.)

Figure 15: “L’hiérarchie de java.io en Java”

2.19.2 L’interface Temporal

1. package : java.time.temporal (à importer manuellement)


2. description : une interface fournissant des méthodes à implémenter
par n’importe quelle classe modélisant des objets temporels.

2.19.3 L’interface TemporalUnit

1. package : java.time.temporal (à importer manuellement)


2. description : une interface fournissant des méthodes à implémenter
par n’importe quelle classe modélisant une unité de temps.
3. quelques méthodes :

203
/*
* description : retourne le temps
selon l'unité désignant l'objet courant
entre les deux objets temporels
"temporal1Inclusive" (inclus) et temporal2Exclusive (exclus)
*/
long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive)

2.19.4 L’interface TemporalAdjuster

1. package : java.time.temporal (à importer manuellement)


2. description : une interface fournissant des méthodes à implémenter
par n’importe ’classe’ modélisant un ajusteur d’objets temporels
définissant sa propre stratégie d’ajustement.

2.19.5 L’énumération ChronoUnit

1. package : java.time.temporal (à importer manuellement)


2. description : une énumération des unités de date utilisées pour ma-
nipuler des dates (implémentant l’interface TemporalUnit).
3. quelques valeurs :
• MILLENNIA : unité = millénaire ;
• CENTURIES : unité = siècle ;
• DECADES : unité = décennie ;
• YEARS : unité = année ;
• MONTHS : unité = mois ;
• WEEKS : unité = semaine ;
• DAYS : unité = jour ;
• HOURS : unité = heure ;
• MINUTES : unité = minute ;
• SECONDS : unité = seconde ;
• MILLIS : unité = milliseconde ;
• MICROS : unité = microseconde ;
• NANOS : unité = nanoseconde ;

2.19.6 L’énumération DayOfWeek

1. package : java.time (à importer manuellement)


2. description : une énumération des jours de la semaine.
3. valeurs :
• MONDAY : lundi ;
• TUESDAY : mardi ;
• WEDNESDAY : mercredi ;

204
• THURSDAY : jeudi ;
• FRIDAY : vendredi ;
• SATURDAY : samedi ;
• SUNDAY : dimanche.

2.19.7 L’énumération Month

1. package : java.time (à importer manuellement)


2. description : une énumération des mois de l’année.
3. valeurs :
• JANUARY : janvier ;
• FEBRUARY : février ;
• MARCH : mars ;
• APRIL : avril ;
• MAY : mai ;
• JUNE : juin ;
• JULY : juillet ;
• AUGUST : août ;
• SEPTEMBER : septembre ;
• OCTOBER : octobre ;
• NOVEMBER : novembre ;
• DECEMBER : décembre.

2.19.8 L’interface ChronoLocalDate

1. package : java.time.chrono (à importer manuellement)


2. description : une interface fournissant les méthodes à implémenter
par n’importe quelle classe modélisant une date sans un fuseau ho-
raire associé.
3. quelques méthodes :
/*
* description : retourne vrai si la date courante est
avant la date "other", false sinon
*/
default boolean isBefore(ChronoLocalDate other)

/*
* description : retourne vrai si la date courante est
après la date "other", false sinon
*/
default boolean isAfter(ChronoLocalDate other)

205
2.19.9 L’interface ChronoLocalDateTime<D extends ChronoLocalDate>

1. package : java.time.chrono (à importer manuellement)


2. description : une interface fournissant les méthodes à implémenter
par n’importe quelle classe modélisant une date de type D sans un
fuseau horaire associé.
3. quelques méthodes :
/*
* description : retourne vrai si le datetime courant est
avant le datetime "other", false sinon
*/
default boolean isBefore(ChronoLocalDateTime<?> other)

/*
* description : retourne vrai si le datetime courant est
après le datetime "other", false sinon
*/
default boolean isAfter(ChronoLocalDateTime<?> other)

2.19.10 L’interface ChronoZonedDateTime<D extends ChronoLocalDate>

1. package : java.time.chrono (à importer manuellement)


2. description : une interface fournissant les méthodes à implémenter
par n’importe quelle classe modélisant une date de type D avec un
fuseau horaire associé.
3. quelques méthodes :
/*
* description : retourne vrai si le datetime courant est
avant le datetime "other", false sinon
*/
default boolean isBefore(ChronoZonedDateTime<?> other)

/*
* description : retourne vrai si le datetime courant est
après le datetime "other", false sinon
*/
default boolean isAfter(ChronoZonedDateTime<?> other)

/*
* description : retourne l'instant correspondant au datetime courant
selon les règles de conversion associées au fuseau horaire
du datetime
*/
default Instant toInstant();

206
2.19.11 La classe Instant

1. package : java.time (à importer manuellement)


2. description : une classe finale modélisant un point instantanné dans
le temps de la machine.
3. quelques attributs :
/*
* description : le point de départ de ce référentiel de temps
(1970-01-01T00:00:00Z)
*/
public static final Instant EPOCH;
4. quelques méthodes :
/*
* description : retourne vrai si l'instant actuel est
avant l'instant "otherInstant", false sinon
*/
public boolean isBefore(Instant otherInstant)

/*
* description : retourne vrai si l'instant actuel est
après l'instant "otherInstant", false sinon
*/
public boolean isAfter(Instant otherInstant)

/*
* description : retourne l'instant courant de l'horloge du système
*/
public static Instant now();

/*
* description : retourne l'instant correspondant au string "text"
qui doit désigner un instant valide
*/
public static Instant parse(CharSequence text);

2.19.12 La classe LocalDate

1. package : java.time (à importer manuellement)


2. description : une classe finale modélisant une date locale non ISO
dans le temps humain.
3. quelques méthodes :
/*
* description : retourne le jour de la semaine de la date courante

207
*/
public DayOfWeek getDayOfWeek();

/*
* description : retourne le jour du mois de la date courante (entre 1 et 31)
*/
public int getDayOfMonth();

/*
* description : retourne le jour de l'année de la date courante (entre 1 et 366)
*/
public int getDayOfYear();

/*
* description : retourne le mois de l'année de la date courante
*/
public Month getMonth();

/*
* description : retourne le mois de l'année de la date courante (entre 1 et 12)
*/
public int getMonthValue();

/*
* description : retourne l'année de la date courante
*/
public int getYear();

/*
* description : retourne true si l'année de la courante est bissextile,
false sinon
*/
public boolean isLeapYear();

/*
* description : retourne une version ajustée de la date courante
par l'ajusteur "adjuster"
*/
public LocalDate with(TemporalAdjuster adjuster);

/*
* description : retourne une copie de la date courante
ayant le jour du mois "dayOfMonth"
*/
public LocalDate withDayOfMonth(int dayOfMonth);

208
/*
* description : retourne une copie de la date courante
ayant le jour de l'année "dayOfYear"
*/
public LocalDate withDayOfYear(int dayOfYear);

/*
* description : retourne une copie de la date courante
ayant le mois "month"
*/
public LocalDate withMonth(int month);

/*
* description : retourne une copie de la date courante
ayant l'année "year"
*/
public LocalDate withYear(int year);

/*
* description : retourne une copie de la date courante
en lui retranchant "amountToSubtract" d'unité temporelle "unit"
*/
public LocalDate minus(long amountToSubtract, TemporalUnit unit);

/*
* description : retourne une copie de la date courante
en lui retranchant "daysToSubtract" jours
*/
public LocalDate minusDays(long daysToSubtract);

/*
* description : retourne une copie de la date courante
en lui retranchant "weeksToSubtract" semaines
*/
public LocalDate minusWeeks(long weeksToSubtract);

/*
* description : retourne une copie de la date courante
en lui retranchant "monthsToSubtract" mois
*/
public LocalDate minusMonths(long monthsToSubtract);

/*
* description : retourne une copie de la date courante
en lui retranchant "yearsToSubtract" années
*/

209
public LocalDate minusYears(long yearsToSubtract);

/*
* description : retourne une copie de la date courante
en lui ajoutant "amountToAdd" d'unité temporelle "unit"
*/
public LocalDate plus(long amountToAdd, TemporalUnit unit);

/*
* description : retourne une copie de la date courante
en lui ajoutant "daysToAdd" jours
*/
public LocalDate plusDays(long daysToAdd);

/*
* description : retourne une copie de la date courante
en lui ajoutant "weeksToAdd" semaines
*/
public LocalDate plusWeeks(long weeksToAdd);

/*
* description : retourne une copie de la date courante
en lui ajoutant "monthsToAdd" mois
*/
public LocalDate plusMonths(long monthsToAdd);

/*
* description : retourne une copie de la date courante
en lui ajoutant "yearsToAdd" années
*/
public LocalDate plusYears(long yearsToAdd);

/*
* description : retourne la date courante de l'horloge du système
dans le fuseau horaire (timezone) par défaut
*/
public static LocalDate now();

/*
* description : retourne la date correspondant au string "text"
qui doit désigner une date valide
*/
public static LocalDate parse(CharSequence text);

/*
* description : retourne une date locale ayant l'année

210
"year", le mois "month" et le jour du mois "dayOfMonth"
avec month entre 1 et 12
*/
public static LocalDate of(int year, int month, int dayOfMonth);

/*
* description : retourne une date locale ayant l'année
"year", le mois "month" et le jour du mois "dayOfMonth"
avec month une valeur de l'énumération "Month"
*/
public static LocalDate of(int year, Month month, int dayOfMonth);

/*
* description : retourne une date locale ayant l'année
"year" et le jour de l'année "dayOfYear"
avec dayOfMonth entre 1 et 366
*/
public static LocalDate ofYearDay(int year, int dayOfYear);

2.19.13 La classe LocalTime

1. package : java.time (à importer manuellement)


2. description : une classe finale modélisant un time local non ISO
dans le temps humain.
3. quelques méthodes :
/*
* description : retourne l'heure du time courant (entre 0 et 23)
*/
public int getHour();

/*
* description : retourne le nombre des minutes du time courant (entre 0 et 59)
*/
public int getMinute();

/*
* description : retourne le nombre des secondes du time courant (entre 0 et 59)
*/
public int getSecond();

/*
* description : retourne le nombre de nanosecondes du time courant
* (entre 0 et 999,999,999)
*/

211
public int getNano();

/*
* description : retourne vrai si le time courant est
avant le time "other", false sinon
*/
public boolean isBefore(LocalTime other)

/*
* description : retourne vrai si le time courant est
après le time "other", false sinon
*/
public boolean isAfter(LocalTime other)

/*
* description : retourne une version ajustée du temps courant
par l'ajusteur "adjuster"
*/
public LocalTime with(TemporalAdjuster adjuster);

/*
* description : retourne une copie du time courant
ayant l'heure "hour"
*/
public LocalTime withHour(int hour);

/*
* description : retourne une copie du time courant
ayant le nombre de minutes "minute"
*/
public LocalTime withMinute(int minute);

/*
* description : retourne une copie du time courant
ayant le nombre de secondes "second"
*/
public LocalTime withSecond(int second);

/*
* description : retourne une copie du time courant
ayant le nombre de nanosecondes "nanoOfSecond"
*/
public LocalTime withNano(int nanoOfSecond);

/*
* description : retourne une copie du time courant

212
en lui retranchant "amountToSubtract" d'unité temporelle "unit"
*/
public LocalTime minus(long amountToSubtract, TemporalUnit unit);

/*
* description : retourne une copie du time courant
en lui retranchant "hoursToSubtract" heures
*/
public LocalTime minusHours(long hoursToSubtract);

/*
* description : retourne une copie du time courant
en lui retranchant "minutesToSubtract" minutes
*/
public LocalTime minusMinutes(long minutesToSubtract);

/*
* description : retourne une copie du time courant
en lui retranchant "secondsToSubtract" secondes
*/
public LocalTime minusSeconds(long secondsToSubtract);

/*
* description : retourne une copie du time courant
en lui retranchant "nanosToSubtract" nanosecondes
*/
public LocalTime minusNanos(long nanosToSubtract);

/*
* description : retourne une copie du time courant
en lui ajoutant "amountToAdd" d'unité temporelle "unit"
*/
public LocalTime plus(long amountToAdd, TemporalUnit unit);

/*
* description : retourne une copie du time courant
en lui ajoutant "hoursToAdd" heures
*/
public LocalTime plusHours(long hoursToAdd);

/*
* description : retourne une copie du time courant
en lui ajoutant "minutesToAdd" minutes
*/
public LocalTime plusMinutes(long minutesToAdd);

213
/*
* description : retourne une copie du time courant
en lui ajoutant "secondsToAdd" secondes
*/
public LocalTime plusSeconds(long secondsToAdd);

/*
* description : retourne une copie du time courant
en lui ajoutant "nanosToAdd" nanosecondes
*/
public LocalTime plusNanos(long nanosToAdd);

/*
* description : retourne le time courant de l'horloge du système
dans le fuseau horaire (timezone) par défaut
*/
public static LocalTime now();

/*
* description : retourne le time correspondant au string "text"
qui doit désigner un time valide
*/
public static LocalTime parse(CharSequence text);

/*
* description : retourne un time local ayant l'heure
"hour" et le nombre de minutes "minute"
*/
public static LocalTime of(int hour, int minute);

/*
* description : retourne un time local ayant l'heure
"hour", le nombre de minutes "minute" et le nombre
de secondes "second"
*/
public static LocalTime of(int hour, int minute, int second);

/*
* description : retourne un time local ayant l'heure
"hour", le nombre de minutes "minute", le nombre
de secondes "second" et le nombre de nanosecondes
"nanoOfSecond"
*/
public static LocalTime of(int hour, int minute, int second, int nanoOfSecond);

214
2.19.14 La classe LocalDateTime

1. package : java.time (à importer manuellement)


2. description : une classe finale modélisant un datetime local non ISO
dans le temps humain.
3. quelques méthodes :
/*
* description : retourne le jour de la semaine du datetime courant
*/
public DayOfWeek getDayOfWeek();

/*
* description : retourne le jour du mois du datetime courant (entre 1 et 31)
*/
public int getDayOfMonth();

/*
* description : retourne le jour de l'année du datetime courant (entre 1 et 366)
*/
public int getDayOfYear();

/*
* description : retourne le mois de l'année du datetime courant
*/
public Month getMonth();

/*
* description : retourne le mois de l'année du datetime courant (entre 1 et 12)
*/
public int getMonthValue();

/*
* description : retourne l'année du datetime courant
*/
public int getYear();

/*
* description : retourne l'heure du datetime courant (entre 0 et 23)
*/
public int getHour();

/*
* description : retourne le nombre des minutes du datetime courant
* (entre 0 et 59)
*/

215
public int getMinute();

/*
* description : retourne le nombre des secondes du datetime courant
* (entre 0 et 59)
*/
public int getSecond();

/*
* description : retourne le nombre de nanosecondes du datetime courant
* (entre 0 et 999,999,999)
*/
public int getNano();

/*
* description : retourne la partie date du datetime courant
*/
public LocalDate toLocalDate();

/*
* description : retourne la partie time du datetime courant
*/
public LocalTime toLocalTime();

/*
* description : le datetime associé au fuseau horaire
ayant l'identifiant "zone"
*/
public ZonedDateTime atZone(ZoneId zone);

/*
* description : retourne une version ajustée du dateime courant
par l'ajusteur "adjuster"
*/
public LocalDateTime with(TemporalAdjuster adjuster);

/*
* description : retourne une copie du datetime courant
ayant le jour du mois "dayOfMonth"
*/
public LocalDateTime withDayOfMonth(int dayOfMonth);

/*
* description : retourne une copie du datetime courant
ayant le jour de l'année "dayOfYear"
*/

216
public LocalDateTime withDayOfYear(int dayOfYear);

/*
* description : retourne une copie du datetime courant
ayant le mois "month"
*/
public LocalDateTime withMonth(int month);

/*
* description : retourne une copie du datetime courant
ayant l'année "year"
*/
public LocalDateTime withYear(int year);

/*
* description : retourne une copie du datetime courant
ayant l'heure "hour"
*/
public LocalDateTime withHour(int hour);

/*
* description : retourne une copie du datetime courant
ayant le nombre de minutes "minute"
*/
public LocalDateTime withMinute(int minute);

/*
* description : retourne une copie du datetime courant
ayant le nombre de secondes "second"
*/
public LocalDateTime withSecond(int second);

/*
* description : retourne une copie du datetime courant
ayant le nombre de nanosecondes "nanoOfSecond"
*/
public LocalDateTime withNano(int nanoOfSecond);

/*
* description : retourne une copie du datetime courant
en lui retranchant "amountToSubtract" d'unité temporelle "unit"
*/
public LocalDateTime minus(long amountToSubtract, TemporalUnit unit);

/*
* description : retourne une copie du datetime courant

217
en lui retranchant "daysToSubtract" jours
*/
public LocalDateTime minusDays(long daysToSubtract);

/*
* description : retourne une copie du datetime courant
en lui retranchant "weeksToSubtract" semaines
*/
public LocalDateTime minusWeeks(long weeksToSubtract);

/*
* description : retourne une copie du datetime courant
en lui retranchant "monthsToSubtract" mois
*/
public LocalDateTime minusMonths(long monthsToSubtract);

/*
* description : retourne une copie du datetime courant
en lui retranchant "yearsToSubtract" années
*/
public LocalDateTime minusYears(long yearsToSubtract);

/*
* description : retourne une copie du datetime courant
en lui retranchant "hoursToSubtract" heures
*/
public LocalDateTime minusHours(long hoursToSubtract);

/*
* description : retourne une copie du datetime courant
en lui retranchant "minutesToSubtract" minutes
*/
public LocalDateTime minusMinutes(long minutesToSubtract);

/*
* description : retourne une copie du datetime courant
en lui retranchant "secondsToSubtract" secondes
*/
public LocalDateTime minusSeconds(long secondsToSubtract);

/*
* description : retourne une copie du datetime courant
en lui retranchant "nanosToSubtract" nanosecondes
*/
public LocalDateTime minusNanos(long nanosToSubtract);

218
/*
* description : retourne une copie du datetime courant
en lui ajoutant "amountToAdd" d'unité temporelle "unit"
*/
public LocalDateTime plus(long amountToAdd, TemporalUnit unit);

/*
* description : retourne une copie du datetime courant
en lui ajoutant "daysToAdd" jours
*/
public LocalDateTime plusDays(long daysToAdd);

/*
* description : retourne une copie du datetime courant
en lui ajoutant "weeksToAdd" semaines
*/
public LocalDateTime plusWeeks(long weeksToAdd);

/*
* description : retourne une copie du datetime courant
en lui ajoutant "monthsToAdd" mois
*/
public LocalDateTime plusMonths(long monthsToAdd);

/*
* description : retourne une copie du datetime courant
en lui ajoutant "yearsToAdd" années
*/
public LocalDateTime plusYears(long yearsToAdd);

/*
* description : retourne une copie du datetime courant
en lui ajoutant "hoursToAdd" heures
*/
public LocalDateTime plusHours(long hoursToAdd);

/*
* description : retourne une copie du datetime courant
en lui ajoutant "minutesToAdd" minutes
*/
public LocalDateTime plusMinutes(long minutesToAdd);

/*
* description : retourne une copie du datetime courant
en lui ajoutant "secondsToAdd" secondes
*/

219
public LocalDateTime plusSeconds(long secondsToAdd);

/*
* description : retourne une copie du datetime courant
en lui ajoutant "nanosToAdd" nanosecondes
*/
public LocalDateTime plusNanos(long nanosToAdd);

/*
* description : retourne le datetime courant de l'horloge du système
dans le fuseau horaire (timezone) par défaut
*/
public static LocalDateTime now();

/*
* description : retourne le datetime correspondant au string "text"
qui doit désigner un datetime valide
*/
public static LocalDateTime parse(CharSequence text);

/*
* description : retourne un datetime local ayant l'année
"year", le mois "month", le jour du mois "dayOfMonth",
l'heure "hour" et le nombre de minutes "minute"
avec month entre 1 et 12
*/
public static LocalDateTime of(int year, int month, int dayOfMonth,
int hour, int minute);

/*
* description : retourne un datetime local ayant l'année
"year", le mois "month", le jour du mois "dayOfMonth",
l'heure "hour", le nombre de minutes "minute" et
le nombre de secondes "second"
avec month entre 1 et 12
*/
public static LocalDateTime of(int year, int month, int dayOfMonth,
int hour, int minute, int second);

/*
* description : retourne un datetime local ayant l'année
"year", le mois "month", le jour du mois "dayOfMonth",
l'heure "hour", le nombre de minutes "minute",
le nombre de secondes "second" et
le nombre de nanosecondes "nanoOfSecond"
avec month entre 1 et 12

220
*/
public static LocalDateTime of(int year, int month, int dayOfMonth,
int hour, int minute, int second, int nanoOfSecond);

/*
* description : retourne un datetime local ayant l'année
"year", le mois "month", le jour du mois "dayOfMonth",
l'heure "hour" et le nombre de minutes "minute"
avec month une valeur de l'énumération "Month"
*/
public static LocalDateTime of(int year, Month month, int dayOfMonth,
int hour, int minute);

/*
* description : retourne un datetime local ayant l'année
"year", le mois "month", le jour du mois "dayOfMonth",
l'heure "hour", le nombre de minutes "minute" et
le nombre de secondes "second"
avec month une valeur de l'énumération "Month"
*/
public static LocalDateTime of(int year, Month month, int dayOfMonth,
int hour, int minute, int second);

/*
* description : retourne un datetime local ayant l'année
"year", le mois "month", le jour du mois "dayOfMonth",
l'heure "hour", le nombre de minutes "minute",
le nombre de secondes "second" et
le nombre de nanosecondes "nanoOfSecond"
avec month une valeur de l'énumération "Month"
*/
public static LocalDateTime of(int year, Month month, int dayOfMonth,
int hour, int minute, int second, int nanoOfSecond);

/*
* description : retourne un datetime local créé à partir
de la date locale "date" et le temps local "time"
*/
public static LocalDateTime of(LocalDate date, LocalTime time);

2.19.15 La classe ZonedDateTime

1. package : java.time (à importer manuellement)


2. description : une classe finale modélisant un datetime associé à un
fuseau horaire.

221
2.19.16 La classe TemporalAdjusters

1. package : java.time.temporal (à importer manuellement)


2. description : une classe finale fournissant des méthodes statiques
définissant et manipulant des ajusteurs communs et utiles.
3. quelques méthodes :
/*
* description : ajuste la date l'utilisant vers la prochaine
occurrence du jour "dayOfWeek"
*/
public static TemporalAdjuster next(DayOfWeek dayOfWeek);

2.19.17 La classe Period

1. package : java.time (à importer manuellement)


2. description : une classe finale modélisant un intervalle de dates
appelé “période” selon le système de calendrier défini par ISO-8601.
3. quelques méthodes :
/*
* description : retourne le nombre de jours de la période courante
*/
public int getDays();

/*
* description : retourne le nombre de mois de la période courante
*/
public int getMonths();

/*
* description : retourne le nombre d'années de la période courante
*/
public int getYears();

/*
* description : une période entre les deux dates locales
"startDateInclusive" (incluse) et
"endDateExclusive" (exclus)
*/
public static Period between(LocalDate startDateInclusive,
LocalDate endDateExclusive);

222
2.19.18 La classe Duration

1. package : java.time (à importer manuellement)


2. description : une classe finale modélisant des durées de temps ma-
chines.
3. quelques méthodes :
/*
* description : retourne le nombre de secondes de la durée courante
*/
public long getSeconds();

/*
* description : une durée entre les deux temps
"startInclusive" (incluse) et
"endExclusive" (exclus)
*/
public static Duration between(Temporal startInclusive, Temporal endExclusive);

2.19.19 La classe ZoneId

1. package : java.time (à importer manuellement)


2. description : une classe abstraite modélisant un identifiant d’un
fuseau horaire (time-zone), par exemple “Europe/Paris”, définissant
les règles de conversion d’un Instant en un LocalDateTime, matérial-
isée par la classe finale ZoneRules, du fuseau horaire.
3. attributs :
/*
* un dictionnaire associant l'identifiant court d'un fuseau horaire
à son identifiant long
*/
public static final Map<String, String> SHORT_IDS;
4. quelques méthodes :
/*
* description : retourne les règles de conversion du fuseau horaire courant
*/
public abstract ZoneRules getRules();

/*
* description : retourne le ZoneId correspondant au string "zoneId"
à condition que "zoneId" soit un identifiant valide d'un fuseau horaire
*/
public static ZoneId of(String zoneId);

223
/*
* description : retourne le fuseau horaire par défaut du système
*/
public static ZoneId systemDefault();

2.19.20 La classe ZoneRules

1. package : java.time.zone (à importer manuellement)


2. description : une classe modélisant des règles définissant comment le
offset d’un fuseau horaire varie. Autrement dit, modélise les règles de
définition d’un Instant en un LocalDateTime pour un fuseau horaire
donné.

2.19.21 Exemples

2.19.21.1 Exemple de base de manipulation de LocalDate, LocalTime


et LocalDateTime
package date;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Test {

public static void main(String[] args) {


LocalDateTime currentDateTime = LocalDateTime.now();
LocalDate currentDate = currentDateTime.toLocalDate();
LocalTime currentTime = currentDateTime.toLocalTime();

System.out.println("Date et heure courants : " + currentDateTime);


System.out.println("Date courante : " + currentDate);
System.out.println("Heure courante : " + currentTime);
System.out.println();

System.out.println("Année : " + currentDate.getYear() + " (" +


(currentDate.isLeapYear()? "Bissextile" : "Non bissextile") + ")");

System.out.println("Mois : " + currentDate.getMonth() + " (" +


currentDate.getMonthValue() + ")");
System.out.println("Jour : " + currentDate.getDayOfWeek() + " (" +
currentDate.getDayOfMonth() + " du mois, " + currentDate.getDayOfYear() +
" de l'année)");
System.out.println();

224
System.out.println("Heure : " + currentTime.getHour());
System.out.println("Minutes : " + currentTime.getMinute());
System.out.println("Secondes : " + currentTime.getSecond());
System.out.println("Nanosecondes : " + currentTime.getNano());
System.out.println();

//création d'une date locale différente ayant le même temps local


LocalDateTime otherDate = currentDateTime
.withDayOfMonth(25)
.withMonth(12)
.withYear(2023);
System.out.println(
"Date locale différente ayant le même temps local que la date courante : " +
otherDate);

//création d'un temps local différent ayant la même date locale


LocalDateTime otherTime = currentDateTime
.withHour(6)
.withMinute(15)
.withSecond(30)
.withNano(256);
System.out.println(
"Temps local différent ayant la même date locale que la date courante : " +
otherTime);
System.out.println();

//création d'une date locale parsée


LocalDate parsedDate = LocalDate.parse("2019-05-08");
System.out.println("Date locale parsée : " + parsedDate);

//création d'un temps local parsé


LocalTime parsedTime = LocalTime.parse("12:15");
System.out.println("Temps local parsé : " + parsedTime);

//création d'un dateTime local parsé


LocalDateTime parsedDateTime = LocalDateTime.parse("2017-04-08T16:30");
System.out.println("DateTime local parsé : " + parsedDateTime);
}
}

2.19.21.2 Exemple de base d’ajout/de soustraction de durées


LocalDate, LocalTime et LocalDateTime
package date;

225
import java.time.LocalDateTime;
import java.time.Month;
import java.time.temporal.ChronoUnit;

public class Test {

public static void main(String[] args) {


LocalDateTime base = LocalDateTime.of(2018, Month.DECEMBER, 25, 13, 37, 0);
System.out.println("DateTime de référence : " + base);
System.out.println();

System.out.println("DateTime après le datetime de référence :");


System.out.println("=========================================");
System.out.println("Après une décennie : " +
base.plus(1, ChronoUnit.DECADES));
System.out.println("Après deux siècles : " +
base.plus(2, ChronoUnit.CENTURIES));
System.out.println("Après un millénaire : " +
base.plus(1, ChronoUnit.MILLENNIA));
System.out.println("Après une semaine : " + base.plusWeeks(1));
System.out.println("Après 2 mois : " + base.plusMonths(2));
System.out.println("Après 3 ans : " + base.plusYears(3));
System.out.println("Après 4 heures : " + base.plusHours(4));
System.out.println("Après 5 minutes : " + base.plusMinutes(5));
System.out.println("Après 6 secondes : " + base.plusSeconds(6));
System.out.println("Après 7 millisecondes : " +
base.plus(7, ChronoUnit.MILLIS));
System.out.println("Après 8 microsecondes : " +
base.plus(8, ChronoUnit.MICROS));
System.out.println("Après 9 nanosecondes : " + base.plusNanos(9));
System.out.println();

System.out.println("DateTime avant le datetime de référence :");


System.out.println("=========================================");
System.out.println("Avant une décennie : " +
base.minus(1, ChronoUnit.DECADES));
System.out.println("Avant deux siècles : " +
base.minus(2, ChronoUnit.CENTURIES));
System.out.println("Avant un millénaire : " +
base.minus(1, ChronoUnit.MILLENNIA));
System.out.println("Avant une semaine : " + base.minusWeeks(1));
System.out.println("Avant 2 mois : " + base.minusMonths(2));
System.out.println("Avant 3 ans : " + base.minusYears(3));
System.out.println("Avant 4 heures : " + base.minusHours(4));
System.out.println("Avant 5 minutes : " + base.minusMinutes(5));
System.out.println("Avant 6 secondes : " + base.minusSeconds(6));

226
System.out.println("Avant 7 millisecondes : " +
base.minus(7, ChronoUnit.MILLIS));
System.out.println("Avant 8 microsecondes : " +
base.minus(8, ChronoUnit.MICROS));
System.out.println("Avant 9 nanosecondes : " +
base.minusNanos(9));
System.out.println();
}
}

2.19.21.3 Exemple d’utilisation d’un TemporalAdjuster


package date;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class Test {

public static void main(String[] args) {


LocalDate date = LocalDate.now();

System.out.println("Date courante : " + date);

//Le prochain samedi


LocalDate nextSaturday = date.with(TemporalAdjusters
.next(DayOfWeek.SATURDAY));
System.out.println("Prochain samedi : " + nextSaturday);

// Le troisième mardi du mois suivant


/*On avance vers le mois suivant*/
LocalDate nextMonth = date.plusMonths(1);

/*On crée une nouvelle date désignant le premier jour du mois*/


LocalDate firstNextMonth = LocalDate.of(nextMonth.getYear(),
nextMonth.getMonth(), 1);

/*On avance de trois mardis*/


LocalDate thirdTuesday = firstNextMonth.with(TemporalAdjusters
.next(DayOfWeek.TUESDAY))
.with(TemporalAdjusters.next(DayOfWeek.TUESDAY))
.with(TemporalAdjusters.next(DayOfWeek.TUESDAY));

System.out.println("Le troisième mardi du mois prochain : " + thirdTuesday);

227
}
}

2.19.21.4 Exemple d’utilisation de Period et Duration


package date;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.Period;
import java.time.temporal.ChronoUnit;

public class Test {

public static void main(String[] args) {


LocalDateTime dt1 = LocalDateTime.of(2018, Month.DECEMBER, 25, 13, 37, 0);
LocalDateTime dt2 = dt1.plus(3, ChronoUnit.YEARS);
LocalDateTime dt3 = dt1.minusMinutes(1337);

Period p = Period.between(dt1.toLocalDate(), dt2.toLocalDate());


Duration d = Duration.between(dt1.toLocalTime(), dt3.toLocalTime());
System.out.println(("Nombre d'années de la période entre " + dt1 + " et "
+ dt2 + " : " + p.getYears() + " années"));

System.out.println(
"(Avec ChronoUnit) Nombre d'années de la période entre " + dt1 + " et " +
dt2 + " : " + ChronoUnit.YEARS.between(dt1, dt2) + " années.");
System.out.println("(Avec ChronoUnit) Nombre de mois de la période entre " +
dt1 + " et " + dt2 + " : " + ChronoUnit.MONTHS.between(dt1, dt2) + " mois.");
System.out.println("(Avec ChronoUnit) Nombre de jours de la période entre " +
dt1 + " et " + dt2 + " : " + ChronoUnit.DAYS.between(dt1, dt2) + " jours.");
System.out.println();
System.out.println(("Nombre de secondes de la durée entre " + dt1 + " et " +
dt3 + " : " + d.getSeconds() + " secondes."));
}
}

2.19.21.5 Exemple d’utilisation de ZoneId et ZonedDateTime


package date;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Arrays;

228
import java.util.List;
import java.util.Map;

public class Test {

public static void main(String[] args) {


System.out.println("Liste des fuseaus horaires et de leurs identifiants :");
System.out.println("=====================================================");
Map<String, String> maps = ZoneId.SHORT_IDS;
maps
.values()
.stream()
.forEach(zoneId -> {
System.out.println(zoneId + " -- " + ZoneId.of(zoneId).getRules());
});

System.out.println();
System.out.println("Fuseau horaire par défaut : " + ZoneId.systemDefault());
System.out.println("Règles de conversion : " + ZoneId.systemDefault().getRules());
System.out.println();

System.out.println(
"Représentation du moment actuel en fuseaux horaires différents :");
System.out.println(
"=================================================================");
LocalDateTime currentTime = LocalDateTime.now();

//liste des fuseaux horaires dans lesquels nous affichons le moment actuel
List<ZoneId> zones = Arrays.asList(
ZoneId.of("Europe/Paris"),
ZoneId.of("Asia/Tokyo"),
ZoneId.of("America/Anchorage")
);

System.out.println("Moment actuel dans le fuseau horaire par défaut : "


+ currentTime);

zones
.stream()
.forEach(zoneId -> {
System.out.println("\tMoment actuel dans le fuseau horaire \"" +
zoneId.toString() + "\" : " + currentTime.atZone(zoneId).toInstant());
});
}
}

229
2.20 JVM et la modularité (Depuis Java 9)

2.20.1 Introduction

1. avant Java 9 : Java était contenu dans quelques fichiers .jar et


bibliothèques assez grosses (e.g. rt.jar contenant environ 53 Mo du
langage).
2. depuis Java 9 : découpage du langage en modules (i.e. des fichiers
compressés .jmod).
3. avantages :
• réduire :
1. la taille du JRE → possibilité de l’embarquer avec des
équipements ayant un espace mémoire/disque réduit ;
2. la surface d’attaque par des personnes malveillantes →
augmenter la sécurité des applications.
• introduire un nouveau niveau de portée sur les objets à travers
les modules : seuls les éléments exportés pourront être accessi-
bles, quels que soient leurs portées propres.
4. module : un ensemble de packages décrit par un fichier appelé un
“descripteur de module”.
5. descripteur de module : un fichier module-info.java contenant :
• les dépendances d’un module et
• ses éléments exportés qui sont accessibles/importables par
d’autres modules.
6. java.base.jmod :
• fichier contenant le module parent de tous les modules en Java.
• n’a pas besoin d’être explicitement spécifié en tant que dépen-
dance par un autre module, vu qu’il l’est par défaut.

Figure 16: “Avant et après Java 9”

2.20.2 Contraintes de manipulation des modules

1. un package/module doit avoir un nom unique avec les mêmes con-


ventions de nommage ;
2. un seul descripteur de module par module, localisé à sa racine ;

230
3. un module peut contenir plusieurs packages, mais un package ne
peut être contenu que dans un seul module ;

2.20.3 Syntaxe de définition d’un module

// fichier module-info.java

module nomModule.java {
/*dépendances, exportations et interfaces*/
}

/*
* description : tous les éléments publiques de "package" sont accessibles
par les autres packages
*/
// exports <package>;

/*
* description : tous les éléments publiques de "package1" sont accessibles
uniquement depuis les packages "package2"[, ...]
*/
// qualified exports <package1> to <package2>[, ...];

/*
* description : le module dépend du package "package"
*/
// requires <package>;

/*
* description : le module, et toutes les applications
qui en dépenderont, dépendent du package "package"
*/
// requires <package> transitive;

/*
* description : le module fournit l'interface "package.Interface" et
une implémentation de celle-ci "package.Implementation"
*/
// provides <package.Interface> with <package.Implementation>;

2.20.4 CLI ajoutée par JDK pour les modules (Depuis Java 9)

java --list-modules # lister les modules de Java avec leurs numéros de version

231
# décrit le module :
# - ses exportations :
# - ses dépendances :
# - ses interfaces et leurs implémentations
java --describe-module <module>

3 Tests en Java

3.1 JUnit et les tests unitaires

3.1.1 Introduction

1. description : un framework de test open-source écrit en Java par E.


Gamma et K. Beck pour les applications Java.
2. origines :
• XP (Xtreme Programming ou test-first development) et méthodes
agiles.
• xUnit : framework de tests de facto pour un langage de pro-
grammation x tels que :
1. JUnit : Java ;
2. NUnit : .NET ;
3. PiUnit : Python ;
4. FlexUnit : Flex ;
5. etc.
3. objectif : création des tests d’applications Java, tests unitaires, et
tests de non régression.
4. versions :
• versions < 4 :
1. paramétrage par spécialisation ;
2. contraintes de nommage.
• versions >= 4 :
1. utilisation d’annotations.
2. la plupart de la documentation en ligne se base sur JUnit 3
et non JUnit 4.
3. pas de runner graphique en version >= 4 → utilisation d’un
IDE.

3.1.1.1 Propriétés :
1. faciliter la définition des tests grâce à des assertions, des méthodes
d’initialisation et de finalisation.
2. enchaîner l’exécution des méthodes de test définies par le testeur.
3. savoir en un seul clic quels tests ont réussi/planté/échoué grâce à
une construction et une exécution rapide de tests.

232
4. mettre à disposition du testeur toute l’infrastructure nécessaire
pour :
• définir des tests : JUnit ne définit pas les tests, ni propose des
principes pour structurer leur définition, c’est la responsabilité
du testeur.
• définir leurs oracles.
• se remettre à JUnit pour leur exécution : le testeur n’invoque
pas explicitement les tests dans le programme, c’est la respons-
abilité du framework d’invoquer les tests définis par le testeur.

3.1.1.2 Oracle d’un test


1. définition : méchanisme permettant d’indiquer si un programme
s’est exécuté correctement pour un test spécifique.
2. composition :
• information de l’oracle : le résultat attendu par le test ;
• procédure de l’oracle : la procédure de comparaison entre le
résultat attendu et le résultat réel obtenu.

3.1.2 Les bases

3.1.2.1 Principe général


1. écrire une/plusieurs classe(s) de test destinée(s) à contenir les tests
et y insérer les méthodes de test.
2. écrire les méthodes de test qui :
• invoquent une/plusieurs méthodes du système à tester.
• supposent avoir une instance d’une classe du système à tester,
qui doit être instanciée dans un endroit dans la classe de tests.
• incluent des instructions permettant un verdict automatique :
les assertions.

3.1.2.2 Classes de test


1. description : une classe de test est une collection de cas de test
(ordre quelconque).
2. composition :
• les méthodes de test ;
• éventuellement des méthodes particulières pour positionner
l’environnement de test.
3. définition en JUnit :
• versions < 4 : la classe de test hérite de JUnit.framework.TestCase.
• version >= 4 : la classe de test est une classe quelconque.

233
3.1.2.3 Méthodes de test/Cas de test
1. cas de test :
• description : un cas de test s’intéresse à une seule unité de
code/un seul comportement et doit rester court.
• propriété : les cas de test sont indépendants les uns des autres.
2. définition en JUnit :
• un cas de test ≡ une méthode de test ;
• versions < 4 : les noms des méthodes de test commencent
par test.
• versions >= 4 : les méthodes de test sont annotées par @Test.
3. méthodes de test
• définition :
1. sont sans paramètres et sans type de retour ;
2. embarquent l’oracle et donc contiennent des assertions, par
exemple :
– x vaut 3.
– le résultat de l’appel de telle méthode est non nul.
– x est inférieur à y.
– etc.
• exécution : invocation par JUnit dans un ordre quelconque.

3.1.2.4 Verdicts
1. définition : un verdict désigne le résultat de l’invocation de la procé-
dure de comparaison (une assertion) d’un oracle dans un cas de test.
2. résultats :
• Pass (vert) : aucune faute détectée.
• Fail (rouge) : échec; on attendait un résultat et on a eu un
autre.
• Error : le test n’a pas pu s’exécuter correctement (i.e. une
exception inattendue).
3. remarque : depuis JUnit version >= 4, il n’y a plus de différence
entre Fail et Error.

3.1.2.5 L’environnement de test


1. besoin : les méthodes de test ont besoin d’invoquer les méthodes du
système à tester sur des instances des classes du système à tester.
2. approche :
• déclarer les instances des classes du système à tester comme
attributs d’instance de la classe de test.
• déléguer l’instanciation des classes du système à tester et
la mise en place de l’environnement de test aux méthodes
d’initialisation et de finialisation appelées aussi préambules et
postambules.

234
3.1.2.6 Préambules et postambules
1. description : des méthodes écrites par le testeur pour mettre en
place l’environnement de test.
2. définition en JUnit 4 :
• méthodes annotées par @Before et @After :
1. publiques et non statiques ;
2. invoquées avant/après chaque méthode de test par le
framework ;
3. possiblement plusieurs à être annotées par ces deux anno-
tations dans une classe de test, avec un ordre d’invocation
quelconque.
• méthodes annotées par @BeforeClass et @AfterClass :
1. publiques et statiques ;
2. invoquées avant/après la première/dernière méthode de
test par le framework ;
3. une seule méthode annotée par chaque annotation dans
une classe de test.
3. définition en JUnit 3 :
• méthodes équivalentes aux méthodes annotées par @Before et
@After : les méthodes setUp() et tearDown().
• pas de méthodes équivalentes aux méthodes annotées par
@BeforeClass et @AfterClasse.

3.1.2.7 Exemple introductif


// La classe Subscription à tester
package junit;

public class Subscription {


/*attributes*/
private int price;
private int length;

/*constructors*/
public Subscription(int price, int length) {
this.price = price;
this.length = length;
}

/**
* Calculate the monthly subscription price in euro,
* rounded up to the nearest cent
*
* @return the monthly subscription price in euro
*/

235
public double pricePerMonth() {
return (double) price / (double) length;
}

/**
* cancel/ nullify this subscription
*/
public void cancel() {this.length = 0;}
}

// La classe de test SubscriptionTest


/*pas d'utilisation de préambules et de postambules*/
package junit;

import static org.junit.Assert.assertTrue;

import org.junit.Test;

public class SubscriptionTest {


@Test
public void testReturnEuro(){
System.out.println("Test if pricePerMonth() returns Euro...");
Subscription s = new Subscription(200,2);
assertTrue(s.pricePerMonth() == 1.0);
}

@Test
public void testRoundUp() {
System.out.println("Test if pricePerMonth() rounds up correctly...");
Subscription s = new Subscription(200, 3);
assertTrue(s.pricePerMonth() == 0.67);
}
}

3.1.3 Approfondissements

3.1.3.1 Détails sur les méthodes de test en JUnit >=4


/*
* description : indiquer le type d'exception attendu
* retour : Pass si cette exception est levée, Fail sinon
*/
@Test(expected=ClasseException.class)
public void testMethod(){/*code*/ }

236
/*
* description : indiquer le maximum timeout permis en ms
* retour : Pass si la méthode termine son exécution avant le timeout
Fail sinon
*/
@Test(timeout=10)
public void testMethod(){/*code*/ }

/*
* description : ignorer la méthode de test avec éventuellement
un message indiquant pourquoi. La méthode de test ne sera pas
ainsi invoquée par JUnit
*/
@Ignore("message")
public void testMethod(){/*code*/ }

3.1.3.2 Les assertions en JUnit 4


1. description : une assertion permet d’embarquer un oracle dans un
cas de test et d’automatiser le verdict.
2. structuration et implémentation : les assertions :
• sont implémentées en tant que méthodes statiques du package
org.junit.Assert ;
• lancent des erreurs de type java.lang.AssertionError en cas
de non respect de l’assertion ;
• existent en plusieurs types et sont fortement surchargées.
3. importation :
// importer toutes les assertions
import static org.junit.Assertion.*;

// importer une assertion spécifique


import static org.junit.Assertion.someAssertionMethod;
4. depuis JUnit 4.4 :
// syntaxe générale de l'assertion "assertThat()"
// Pass s'il y a un match, Fail sinon
assertThat([value], [matcher statement]);

// exemples
// Pass si x = 3
assertThat(x, is(3));

// Pass si x != 3
assertThat(x, is(not(3)));

237
// Pass si "color" ou "colour" sont des sous-strings de "responseString"
assertThat(responseString, either(containsString("color"))
.or(containsString("colour")));

// Pass si la liste "myList" contient l'élément "3"


assertThat(myList, hasItem("3"));

// syntaxe générale de la supposition "assumeThat()"


// Pass s'il y a un match, ignorée sinon
assumetThat([value], [matcher statement]);

// exemples
// Pass si le séparateur du système de fichier est "/"
assumetThat(File.separatorChar, is('/'))

3.1.4 Tests paramétrés

3.1.4.1 Introduction
1. besoin : réutiliser une méthode de test avec des jeux de données
de test différents.
2. approche :
/*importations requises*/
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

/*
* indiquer que la classe de test est paramétrée par
des jeux de données différents
*/
@RunWith(Parameterized.class)
visibilite class ClasseTest{
/*
* attributs désignant les composants d'un jeu
de données de test
*/
visibilite type1 jeuPart1;
visibilite type2 jeuPart1;
...

/*
* constructeur publique initialisant une instance de
la classe de test via des paramètres désignant
les composants d'un jeu de données, qui retournera

238
une instance de la classe de test
pour chaque jeu de données de test
*/
public ClasseTest([jeu]){/*code*/ }

/*méthodes de test exécutées sur chaque jeu de données de test*/


...

/*méthode de génération de jeux de données de test*/


@Parameters
public static Collection<Object[]> testData(){
return Arrays.asList(new Object[][]{
{jeu1},
{jeu2},
...
{jeuN},
});
}
}

3.1.4.2 Exemple
// La classe Sum à tester
package junit;

public class Sum {


/*methods*/
public int sum(int x, int y) {
return x + y;
}
}

// La classe de test paramétrée TestSum


package junit;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

239
@RunWith(Parameterized.class)
public class TestSum {
/*attributes*/
private int x;
private int y;
private int result;

/*constructor*/
public TestSum(int x, int y, int result) {
this.x = x;
this.y = y;
this.result = result;
}

/*methods*/
//test methods
@Test
public void testSum() {
assertEquals(result, new Sum().sum(x, y));
}

//data generating methods


@Parameters
public static Collection<Object[]> testData(){
return Arrays.asList(new Object[][] {
{0, 0, 0},
{1, 1, 2},
{1, -1, 0},
{-1, -1, -2}
});
}
}

3.1.5 Suites de tests

3.1.5.1 Introduction
1. besoin : rassembler des cas de test pour enchaîner leur exécution
en groupant l’exécution de classes de test.
2. principe :
• création de plusieurs classes de test.
• création d’une classe vide de suite de tests
3. syntaxe :
/*importations requises*/
import org.junit.runner.RunWith;

240
import org.junit.runners.Suite;

/*
* indiquer que la classe de test désigne une suite
de classes de test et indiquer quelles classes de test
constituent la suite
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({ClasseTest1.class, ClasseTest2.class, ...})
visibilite class ClasseSuiteTest{}

3.1.5.2 Exemple
// La classe à tester Calculator
package junit;

public class Calculator {


/*attributes*/
private static int result;

/*methods*/
public void add(int n) {result += n;}
public void subtract(int n) {
result -= 1; // Bug: should be result -= n
}

public void multiply(int n) {} //not implemented yet


public void divide(int n) {result /= n;}
public void square(int n) {result = n*n;}
public void squareRoot(int n) {
// Bug: infinite loop
for (; ;);
}

public void clear() {result = 0;}


public void switchOn() {
/*
* Switch on the screen
* Display "Hello",
* Beep,
* Do other things that calculators nowadays do
*/
result = 0;
}

241
public void switchOff() {
/*
* Display "bye bye",
* Beep,
* Switch off the screen
*/
}

public int getResult() {return result;}


}

// La classe de tests de base CalculatorBasicTest


package junit;

import static org.junit.Assert.assertEquals;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

public class CalculatorBasicTest {


/*attributes*/
private static Calculator cal = new Calculator();

/*methods*/
//environment methods
@Before
public void clearCalculator() {
cal.clear();
}

//test methods
@Test
public void testAdd() {
cal.add(1);
cal.add(2);
assertEquals(cal.getResult(), 3);
}

@Test
public void testSubtract() {
cal.add(10);
cal.subtract(2);
assertEquals(cal.getResult(), 8);
}

242
@Ignore("not ready yet")
@Test
public void testMultiply() {
cal.add(10);
cal.multiply(10);
assertEquals(cal.getResult(), 100);
}

@Test
public void testDivide() {
cal.add(8);
cal.divide(4);
assertEquals(cal.getResult(), 2);
}

@Test(expected=ArithmeticException.class)
public void testDivideByZero() {
cal.add(10);
cal.divide(0);
}
}

/*
* La classe de test abstraite CalculatorAbstractTest
préparant l'environnement de test
*/
package junit;

import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;

public abstract class CalculatorAbstractTest {

@BeforeClass
public static void startTestSystem() {
System.out.println("Start test system");
}

@AfterClass
public static void stopTestSystem() {
System.out.println("Stop test system");
}

@Before
public void initTestSystem() {

243
System.out.println("Initialize test system");
}
}

// La classe de tests avancée CalculatorAdvancedTest


package junit;

import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class CalculatorAdvancedTest extends CalculatorAbstractTest {


/*attributes*/
private static Calculator cal;

/*methods*/
//environment methods
@BeforeClass
public static void switchOnCalculator() {
System.out.println("Switch on calculator");
cal = new Calculator();
cal.switchOn();
}

@AfterClass
public static void switchOffCalculator() {
System.out.println("Switch off calculator");
cal.switchOff();
cal = null;
}

@Before
public void clearCalculator() {
System.out.println("Clear calculator");
cal.clear();
}

//test methods
@Test(timeout=10)
public void testSquareRoot() {
cal.squareRoot(2);
}
}

/*

244
* La classe de tests paramétrée CalculatorSquareParameterizedTest
pour tester l'opération square(int)
*/
package junit;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class CalculatorSquareParameterizedTest {

/*attributes*/
private static Calculator cal = new Calculator();
private int param;
private int result;

/*constructors*/
public CalculatorSquareParameterizedTest(int param, int result) {
this.param = param;
this.result = result;
}

/*methods*/
//test methods
@Test
public void testSquare() {
cal.square(param);
assertEquals(result, cal.getResult());
}

//data generation method


@Parameters
public static Collection<Object[]> testData(){
return Arrays.asList(new Object[][] {
{0, 0},
{-1, 1},
{1, 1},
{2, 4},
{4, 16},

245
{5, 25}
});
}
}

/*
* La classe désignant la suite des classes de tests
de la classe à tester Calculator
*/
package junit;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Suite.class)
@Suite.SuiteClasses({
CalculatorBasicTest.class,
CalculatorAdvancedTest.class,
CalculatorSquareParameterizedTest.class
})
public class CalculatorSuiteTest {}

4 Programmation multi-threadée en Java

4.1 Rappels sur le parallélisme et la programmation multi-


processus

4.1.1 Terminologie de Base

1. programme : entité statique désignant de(s) fichier(s) avec un point


d’entrée pour l’exécution via la méthode main().
2. processus :
• entité dynamimque, désignant un programme en cours d’exécution.
• une seule unité d’exécution qui s’exécute séquentiellement sur un seul
processeur, même si :
1. l’ordinateur comporte plusieurs processeurs
2. il existe plusieurs parties du code, indépendantes les unes des
autres, et pouvant s’exécuter en parallèle.
3. concurrence ou parallélisme : exécution simultanée parallélisée de
plusieurs codes sur une même machine, désignant :
• soit l’exécution parallèle de plusieurs programmes/processus, pou-
vant désigner une même application, ou des applications différentes
et éventuellement indépendantes.

246
• soit l’exécution d’un programme engendrant plusieurs processus
lourds (via le méchanisme de fork) ou légers (appelés aussi des
threads).
4. programmation multi-processus : faire du parallélisme en créant
plusieurs processus.
5. thread :
• un fil d’exécution, une tâche, une activité ou un processus léger.
• une partie, ou un chemin d’exécution, à l’intérieur d’un processus.
6. programmation multi-threadée : faire du parallélisme en créant
plusieurs threads d’un même processus.

4.1.2 Avantage et inconvénient du parallélisme

1. avantage : le parallélisme est surtout utilisé pour accélérer les calculs.


2. inconvénient : le OS ne peut pas détecter les éventuelles dépendances
entre les fonctions d’un programme ; c’est au programmeur de le faire.

4.1.3 Problème de la programmation multi-processus et le besoin de


la programmation multithreadée

La programmation multi-procesus comporte plusieurs inconvénients :


1. le changement de contexte entre les processus peut être coûteux.
2. l’espace d’adressage d’un processus n’est pas partageable et il faut
donc passer par des mécanismes de comminucation entre les processus
(e.g. tubes, files de messages, mémoires partagées, . . .)
3. les outils de synchronisation entre les processus sont difficiles à prendre en
main.

4.2 Introduction à la programmation multi-threadée

4.2.1 Processus légers (Threads)

1. définition : une partie, ou un chemin d’exécution, à l’intérieur d’un pro-


cessus.
2. synonymes: un fil d’exécution, une tâche, une activité ou un processus
léger.
3. programmation multi-threadée :
• découper un processus en plusieurs threads et dérouler plusieurs
suites d’instruction en parallèle.
• un mécanisme permettant à un processus de réaliser plusieurs unités
d’exécution d’une manière asynchrone.
4. utilisation : exécuter une fonction telle que :

247
• chaque thread possède sa propre pile (et donc ses propres variables
locales).
• tous les threads d’un processus parent partagent ses segments de code,
de données et de tas.
• les threads peuvent communiquer entre eux et partager des données
à travers des variables en mémoire.
• le thread exécutant la fonction main() dans un processus est le
thread principal et tout autre éventuel thread du même procesus
est secondaire.
• sur une machine comportant plusieurs processeurs, chaque thread
peut s’exécuter sur un processeur, indépendamment d’un autre.
• la commutation de contexte entre les threads est gérée par le OS.

4.2.2 Synchronisation de threads

4.2.2.1 Introduction
Il existe plusieurs outils de synchronisation de threads :
1. l’attente de la fin d’un thread.
2. la création de verrous binaires (i.e. à deux états : vérouillé ou pas) utilis-
ables par des threads d’un même processus ou de processus différents.
3. la définition de variables conditionnelles permettant d’attendre
l’occurrence d’un événement.

4.2.2.2 Attente de la fin de l’exécution d’un thread


1. problème : si le thread principal termine son exécution avant que les
autres finissient la leur, le processus terminera son exécution et leur exé-
cution sera arrêtée.
2. solution : utliser un mécanisme d’attente de la fin d’un thread, tel que
un thread peut attendre la fin d’un autre thread qu’il connaît.

4.2.3 Verrous (Mutex)

4.2.3.1 Introduction
1. problème : l’accès à une donnée partagée, en lecture par un thread et en
écriture par un autre, pourra engendrer des incohérences au niveau de sa
valeur lors de son lecture/écriture.
2. solution :
• utiliser un mécanisme d’accès exclusif à une donnée partagée par un
seul thread à la fois.
• on parle de “mutual exclusion” ou exclusion mutuelle.
3. définition : un verrou est un sémaphore ayant deux états possibles :
occupé (i.e. vérouillé) ou libre (i.e. déverrouillé).

248
4. opérations : le verrouillage et le déverrouillage sont des opérations atom-
iques (i.e. non interruptibles).
5. utilisation :
• lorsqu’un verrou est libre, un thread peut demander de l’occuper.
• un seul thread peut occuper un verrou à la fois.
• seul le thread occupant le verrou peut le libérer.
• lorsqu’un thread occupe un verrou, les autres threads souhaitant oc-
cuper le même verrou :
1. seront bloqués dans un contexte bloquant et échoueront dans un
contexte non bloquant.
2. ce comportement dépend de l’OS.
6. section critique : une zone d’un programme comportant des instructions
d’accès à une donnée partagée.

4.2.3.2 Avantages et limitations


1. avantages :
• protéger l’accès à la donnée partagée et assurer l’intégrité et la co-
hérence de sa valeur.
• offrir une gestion facile de la partage des données communes accessi-
bles en lecture/écriture par plusieurs threads.
2. limitation : parfois on a besoin de savoir l’état d’une donnée commune,
afin de réaliser (ou pas) des opérations en fonction de cet état, et ceci est
non réalisable avec les verrous.

4.2.4 Variables conditionnelles

4.2.4.1 Cas d’usage


Deux threads T1 et T2 accèdent à un entier x commun. T1 effectue des opérations
uniquement si x > seuil, et seul T2 peut engendrer une telle situation.

4.2.4.2 Solution simpliste : Utiliser des verrous et des synchronisa-


tion de threads par attente

4.2.4.2.1 Schéma algorithmique


// Partie commune
int x, seuil;
mutex verrou;
...

//Thread T1
...

249
occuper(verrou);
tant que (x <= seuil){
libérer(verrou);
occuper(verrou);
}
...
// travail sur x;
libérer(verrou);
...

//Thread T2
...
occuper(verrou);
// travail sur x;
libérer(verrou);
...

4.2.4.2.2 Avantages et inconvénients


1. avantage : solution éventuellement correcte.
2. inconvénient : solution inefficace, pouvant engendrer plusieurs opéra-
tions de vérouillage et de déverrouillage inutiles dans la boucle tant que
du thread T1 , étant donné que le mécanisme d’attente de la satisfaction
de la condition du seuil n’est pas atomique.

4.2.4.3 Schéma d’une solution plus optimale


1. objectif : définir un mécanisme permettant d’attendre la satisfaction d’un
prédicat d’une manière atomique pour éviter les opérations de vérouillage
et de déverouillage inutiles dans le thread T1 .
2. implémentation :
• l’attente est implémentée par le biais d’un écouteur du signal blo-
quant jusqu’à l’occurrence d’un événement désignant la satisfaction
du prédicat.
• lors de l’émission de l’événement, tous les threads écoutant sont aver-
tis, et un seul occupera le verrou à la fois, alors que les autres ren-
treront dans un état d’écoute atomique de l’ocurrence de l’événément
une prochaine fois.
• on parle de “conditional variable” ou variable conditionnelle.
3. remarque : l’objectif de la solution optimale, traité sur deux threads,
peut être généralisé sur n threads qui attendront tous la satisfaction du
même prédicat, et par suite un seul parmi eux occupera le verrou.
// Thread 1
...
bloquer l'accès;

250
tant que (Predicat non satisfait) faire
relâcher l'accès;
attendre event(Predicat satisfait);
bloquer l'accès;

// Section critique
relâcher l'accès;

//Thread T1=2
...
bloquer l'accès;
// Section critique
signaler event(Predicat satisfait);
relâcher l'accès;

4.2.4.4 Introduction
1. définition : une variable conditionnelle est :
• une donnée commune à plusieurs threads;
• symbolisant un écouteur d’événements ;
• et fonctionnant comme un prédicat (signalant l’occurrence d’un
événement ou pas).
2. implémentation : traiter la valeur de la variable conditionnelle, com-
mune à plusieurs threads, comme section critique devrant être protégée
par une exclusion mutuelle.
3. utilisation :
• un thread occupant un verrou, pourra passer à un état d’attente
atomique, écoutant l’occurrence d’un événement.
• il demandera d’être réveillé une fois l’événement survenu.
• le réveil est réalisé par un autre thread, qui réveillera aussi tous
les autres threads utilisant la variable conditionnelle pour écouter
l’occurrence du même événement.
• parmi les threads réveillés, un seul occupera le verrou, alors que les
autres rentreront dans une attente bloqué atomiquement, écoutant
l’occurrence de l’événement ultérieurement afin d’être réveillés à nou-
veau.
4. remarque : étant donné qu’un seul thread pourra occuper le verrou à la
fois, il est préférable de ne réveiller qu’un seul thread à la fois, parmi ceux
attendant d’être réveillé lors de l’occurrence d’un événement, pour plus
d’efficacité.

4.2.4.5 Schéma algorithmique


donnee_commune;
verrou;

251
cond_var;
...
// initialisation
...
// Thread 1
...
occuper(verrou);
tant que (donnee_commune hors bornes) faire
libérer(verrou);
attendre(cond_var); // attente atomique (i.e. bloquante)
// réveil
occuper(verrou);
// travail
libérer(verrou);

// Thread 2
...
occuper(verrou);
// accés et modification de verrou
si (donnee_commune dans bornes) alors
réveillerTaches(cond);
libérer(verrou);

4.3 L’interface Runnable

1. package : java.lang
2. description :
• à implémenter par toute classe dont les instances seront exécutées au
sein d’un thread quand celui-ci est activé.
• annotée par @FunctionalInterface et donc désigne une interface
fonctionnelle pouvant être le cible d’une lambda expression ou d’une
référence de méthode.
3. quelques méthodes :
/**
* méthode invoquée par la JVM quand le thread est activé
*/
void run();

4.4 La classe Thread

1. package : java.lang.
2. description :
• une classe, superclasse de tous les threads en Java.

252
• implémente Runnable.

4.4.1 Propriétés

1. le choix du thread à exécuter est géré par un ordonnanceur (scheduler),


tel que :
• tout thread possède une priorité d’exécution.
• les threads ayant une priorité supérieure sont exécutés en préférence
avant les threads ayant une priorité inférieure.
• l’alternance entre les threads à exécuter se fait d’une manière aléa-
toire, où :
1. tout thread s’exécute pendant un certain temps, avant d’être
interrompu et mis en sommeil, alors qu’un autre sera mis en
éveil et commence/continue son exécution, jusqu’à ce que tous
les threads auront terminé leur exécution.
2. un thread est exécuté à la fois, sauf si le processeur de la machine
comporte plusieurs coeurs permettant une exécution simultanée
de plusieurs threads à la fois, chacun sur un coeur.
2. tout thread peut être un daemon (i.e. s’exécutant en arrière-plan) ou
pas.
3. tout thread T créé par un thread Ts possède la même priorité initiale que
Ts et est un daemon uniquement si Ts l’est aussi.
4. tout thread est identifié par un identifiant et possède un nom tel que :
• l’identifiant est généré automatiquement et ne peut être modifié ;
• si un thread est créé sans un nom explicite, un nom lui sera généré au-
tomatiquement, et peut éventuellement être modifié ultérieurement.
• plusieurs threads peuvent avoir le même nom ;
5. quand la JVM est lancée, le thread principal (non daemon) est celui qui
fait appel à une classe de l’application contenant une méthode main().
6. une JVM continue à exécuter des threads jusqu’à ce que :
• tous les threads non daemon aient terminé leur exécution ;
• une exception est lancée au sein d’une méthode run() d’un thread
et se propage au delà de sa portée (i.e. n’est pas capturée et traitée
au sein de la méthode).
• la méthode exit() de la classe Runtime est invoquée et le gestion-
naire de sécurité de l’application l’a permis de s’exécuter.

4.4.2 Quelques méthodes

/**
* crée un nouveau thread ayant un nom automatiquement généré de la forme
* "Thread-n" où n désigne un entier
*/
public Thread();

253
/**
* crée un nouveau thread ayant le nom "name"
*/
public Thread(String name);

/**
* crée un thread exécutant la méthode run() de l'objet Runnable "target"
* et ayant un nom automatiquement généré de la forme
* "Thread-n" où n désigne un entier
*/
public Thread(Runnable target);

/**
* crée un thread exécutant la méthode run() de l'objet Runnable "target"
* et ayant le nom "name"
*/
public Thread(Runnable target, String name);

/**
* retourne une référence sur le thread en cours d'exécution
*/
public static Thread currentThread();

/**
* retourne l'identifiant du thread courant
*/
public long getId();

/**
* retourne le nom du thread courant
*/
public final String getName();

/**
* change le nom du thread courant en "name"
*/
public final void setName(String name);

/**
* retourne la priorité d'exécution du thread courant
*/
public final int getPriority();

/**
* retourne l'état du thread courant

254
*/
public Thread.State getState();

/**
* invoqué par un autre thread T' pour activer le thread courant T et commencer
* son exécution, causant la JVM à invoquer sa méthode run()
* et lancer son exécution en concurrence avec T'
*
* Remarque : on peut invoquer la méthode start() sur un thread au plus une fois
* et on ne peut le re-activer une fois qu'il aura terminé son exécution
*/
public void start();

/**
* interrompt le thread courant (si possible)
* lire la documentation pour voir les cas d'interruption possible d'un thread
*/
public void interrupt();

/**
* suspend l'exécution du thread courant pendant "millis" millisecondes
*/
public static void sleep(long millis);

/**
* suspend l'exécution du thread courant pendant "millis" millisecondes
* et "nanos" nanosecondes
*/
public static void sleep(long millis, int nanos);

/**
* retourne true si le thread courant est "vivant"
* (i.e. sa méthode run() est en cours d'exécution
* et n'a pas encore terminé)
* false sinon
*/
public final boolean isAlive();

/**
* retourne true si le thread courant est un thread "daemon"
*/
public final boolean isDaemon();

/**
* retourne true si le thread courant est interrompu
*/

255
public boolean isInterrupted();

4.4.3 L’énumération Thread.State

1. package : java.lang.
2. description : une énumération des valeurs de l’état d’un thread.
3. les valeurs :
• NEW : l’état d’un thread quand il vient d’être créé.
• RUNNABLE : l’état d’un thread lors de son activation par l’invocation
de sa méthode start().
• BLOCKED : l’état d’un thread lorsqu’il est mis en sommeil par
l’ordonnanceur pour en utiliser un autre.
• WAITING : l’état d’un thread lorsqu’il attend indéfiniment la fin
d’exécution d’un autre thread.
• TIMED_WAITING : l’état d’un thread lorsqu’il attend pendant un péri-
ode d’attente spécifique la fin d’exécution d’un autre thread.
• TERMINATED : l’état d’un thread lorsqu’il termine l’exécution de sa
méthode run().

4.4.4 Exemple

/* une classe définissant un thread*/


package threads;

public class TestThread extends Thread {


/* ATTRIBUTES */
private Thread t;

/* CONSTRUCTORS */
public TestThread(String name) {
super(name);
displayState();

this.start();
displayState();
}

public TestThread(String name, Thread t) {


super(name);
setThread(t);
displayState();

this.start();
displayState();

256
}

/* METHODS */
@Override
public void run() {
for (int i = 0; i < 10; i++) {
displayState();

if (t != null)
displayTState();
}
}

public void displayState() {


System.out.println("Statut du thread " + this.getName() + " : " + this.getState());
}

public void displayTState() {


System.out.println("Statut du thread " + t.getName() +
" pendant l'exécution du thread " + this.getName() + " : " + t.getState());
}

public void setThread(Thread t) {


this.t = t;
}
}

/* classe de test */
package threads;

public class Test {

public static void main(String[] args) {


TestThread t1 = new TestThread("A");
TestThread t2 = new TestThread(" B", t1);

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

t1.displayState();
t2.displayState();
}
}

257
Output
Statut du thread A : NEW
Statut du thread A : RUNNABLE
Statut du thread A : RUNNABLE
Statut du thread B : NEW
Statut du thread A : RUNNABLE
Statut du thread A : RUNNABLE
Statut du thread B : RUNNABLE
Statut du thread A : RUNNABLE
Statut du thread B : RUNNABLE
Statut du thread A : RUNNABLE
Statut du thread A pendant l'exécution du thread B : RUNNABLE
Statut du thread A : RUNNABLE
Statut du thread B : RUNNABLE
Statut du thread A : RUNNABLE
Statut du thread A pendant l'exécution du thread B : RUNNABLE
Statut du thread A : RUNNABLE
Statut du thread B : RUNNABLE
Statut du thread A : RUNNABLE
Statut du thread A pendant l'exécution du thread B : RUNNABLE
Statut du thread A : RUNNABLE
Statut du thread B : RUNNABLE
Statut du thread A pendant l'exécution du thread B : TERMINATED
Statut du thread B : RUNNABLE
Statut du thread A pendant l'exécution du thread B : TERMINATED
Statut du thread B : RUNNABLE
Statut du thread A pendant l'exécution du thread B : TERMINATED
Statut du thread B : RUNNABLE
Statut du thread A pendant l'exécution du thread B : TERMINATED
Statut du thread B : RUNNABLE
Statut du thread A pendant l'exécution du thread B : TERMINATED
Statut du thread B : RUNNABLE
Statut du thread A pendant l'exécution du thread B : TERMINATED
Statut du thread B : RUNNABLE
Statut du thread A pendant l'exécution du thread B : TERMINATED
Statut du thread A : TERMINATED
Statut du thread B : TERMINATED

4.5 Le mot-clé synchronized

1. description :
• un mot-clé décrivant une méthode qui ne peut être accédée par un
autre thread si elle l’est déjà par un thread, même si ce dernier est
mis en sommeil par l’ordonnanceur.

258
• les threads cherchant à utiliser une méthode déjà prise en charge par
un autre thread seront mis dans une liste d’attente.
2. utilité : synchroniser l’accès à des données partagées en lecture et
écriture par deux threads, de manière à ne pas permettre l’émergence
d’incohérences.

4.6 La classe Runtime

1. package : java.lang.
2. description : une classe singleton modélisant une interface entre une
application et son environnement d’exécution.
3. quelques méthodes :
/**
* retourne la seule instance de la classe pour l'application courante
*/
public static Runtime getRuntime();

/**
* retourne le nombre de processeurs disponible à la JVM exécutant l'application
*/
public int availableProcessor();

4.7 Le pattern Fork/Join

4.7.1 Pré-requis

1. disposer d’une machine ayant un processeur multi-coeur.


2. disposer d’une tâche à exécuter pouvant être découpée en plusieurs sous-
tâches.
3. vérifier que le résultat de l’exécution simultanée des sous-tâches d’une
tâche découpable est plus efficace que l’exécution de la tâche entière non
découpée.

4.7.2 Principe

259
Figure 17: Principe du pattern Fork/Join

4.7.3 Processus

1. utiliser un objet permettant de découper une tâche en sous-tâches pouvant


être exécutées en parallèle.
2. définir le traitement à effectuer en parallèle au sein d’une méthode
compute().
3. créer un nouveau thread dans un pool de threads via une méthode fork().
4. le pool de threads aura pour rôle de superviser l’exécution des tâches et
sous-tâches afin de pouvoir fusionner les threads en fin de traitement.
5. lancer l’exécution d’un thread à partir du pool de threads le contenant en
exécutant sa méthode compute().
6. récupérer le résultat de l’exécution du thread depuis le pool de threads
et fusionner le thread au thread principal lançant son exécution via une
méthode join().
Remarque
Vu que l’approche Fork/Join peut être gourmande en termes de coeurs à utiliser,
on définit un seuil au delà duquel on passera en mode Fork/Join, sinon on exé-
cute notre tâche en mode monothread (i.e. le thread principal de l’application).

260
4.7.4 Implémentation en Java (Depuis Java 7)

4.7.4.1 La classe ForkJoinTask<V>


1. package : java.util.concurrent.
2. description : une classe abstraite générique, superclasse de toutes les
tâches pouvant être découpées en sous-tâches pouvant être exécutées en
parallèle et retournant un résultat de type V.
3. quelques méthodes :
/**
* une méthode créant un thread pour l'exécution asynchrone
* de la tâche courante au sein du pool de threads fournis par le service
* d'exécution associé à cette tâche.
* Elle retourne la tâche courante
*/
public final ForkJoinTask<V> fork();

/**
* une méthode retournant le résultat de l'exécution de la tâche,
* une fois que l'exécution est terminé,
* et fusionnant le thread de son exécution au sein du pool de threads fournis
* par le service d'exécution associé à cette tâche.
*/
public final V join();

4.7.4.2 La classe RecursiveAction


1. package : java.util.concurrent.
2. description :
• une classe abstraite, superclasse de toutes les tâches pouvant être
découpées en sous-tâches, pouvant être exécutées récursivement en
parallèle, et ne retournant aucun résultat.
• hérite de ForkJoinTask<Void>.
3. quelques méthodes :
/**
* méthode encapsulant le traitement à effectuer par une sous-tâche.
*/
protected abstract void compute();

4.7.4.3 La classe RecursiveTask<V>


1. package : java.util.concurrent.
2. description :

261
• une classe abstraite générique, superclasse de toutes les tâches pou-
vant être découpées en sous-tâches, pouvant être exécutées récursive-
ment en parallèle, et retournant un résultat de type V.
• hérite de ForkJoinTask<Void>.
3. quelques méthodes :
/**
* méthode encapsulant le traitement à effectuer par une sous-tâche,
* et retournant un résultat de type V
*/
protected abstract V compute();

4.7.4.4 La classe ForkJoinPool


1. package : java.util.concurrent.
2. description :
• une classe modélisant un service d’exécution pour les tâches
ForkJoinTask.
• hérite de ForkJoinTask<Void>.
3. propriétés :
• gestion d’un pool de threads pour l’exécution des tâches.
• supervision des tâches et de leurs sous-tâches.
• fusion d’un thread dans le pool une fois qu’il aura terminé son exé-
cution.
4. quelques méthodes :
/**
*crée un pool de threads pouvant utiliser "parallelism" processeurs
* pour la création et l'exécution des threads encapsulant les sous-tâches
* d'une tâche pouvant être parallélisés.
*/
public ForkJoinPool(int parallelism);

/**
* invoke le pool de threads courant sur la tâche parallélisable "task"
* afin de l'exécuter et retourne son résultat de type "T"
* lors de la complétion de l'exécution de ses sous-tâches.
*/
public <T> T invoke(ForkJoinTask<T> task);

4.7.4.5 Exemple en mode Fork/Join


/*
* une classe contenant une méthode exécutant une tâche
* pouvant être parallélisée en utilisant Fork/Join
*/

262
package threads.fork_join;

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.RecursiveTask;

public class FolderScanner extends RecursiveTask<Long> {

/* ATTRIBUTES */
private Path path;
private String filter = "*";
private long result;

/* CONSTRUCTORS */
public FolderScanner() {}
public FolderScanner(Path path, String filter) {
this.path = path;
this.filter = filter;
}

/* METHODS */
public long getResult() {return this.result;}

/**
* Scans folders recursively
* @return the number of files in this scanner's path that math this scanner's filter
* @throws ScanException
*/
public long sequentialScan() throws ScanException {
if (path == null || path.toString().equals(""))
throw new ScanException(
"Invalid path error: the provided path is either empty or null"
);

System.out.println(
"Scanning \"" + path + "\" for files having the extension \""
+ filter + "\""
);

try(DirectoryStream<Path> listing = Files.newDirectoryStream(path)){

for (Path p: listing) {

263
if (Files.isDirectory(p.toAbsolutePath())) {
FolderScanner f = new FolderScanner(p.toAbsolutePath(), filter);
result += f.sequentialScan();
}
}

} catch (IOException e) {
e.printStackTrace();
}

try(DirectoryStream<Path> listing = Files.newDirectoryStream(path, filter)){

for (Path p: listing)


result++;

} catch (IOException e) {
e.printStackTrace();
}

return result;
}

public long parallelScan() throws ScanException {


List<FolderScanner> taskList = new ArrayList<>();

if (path == null || path.toString().equals(""))


throw new ScanException(
"Invalid path error: the provided path is either empty or null"
);

System.out.println(
"Scanning \"" + path + "\" for files having the extension \""
+ filter + "\"");

try(DirectoryStream<Path> listing = Files.newDirectoryStream(path)){

for (Path p: listing) {

if(Files.isDirectory(p.toAbsolutePath())) {
FolderScanner task = new FolderScanner(p.toAbsolutePath(), filter);
taskList.add(task);
task.fork();
}
}

} catch (IOException e) {

264
e.printStackTrace();
}

try(DirectoryStream<Path> listing = Files.newDirectoryStream(path, filter)) {

for (Path p: listing)


result++;

} catch (IOException e) {
e.printStackTrace();
}

for (FolderScanner task: taskList)


result += task.join();

return result;
}

@Override
protected Long compute() {
long result = 0;

try {
result = this.parallelScan();
} catch(ScanException e) {
e.printStackTrace();
}

return result;
}
}

/* classe d'exception */
package threads.fork_join;

public class ScanException extends Exception {


public ScanException() {super();}
public ScanException(String message) {super(message);}
}

/* classe de test */
package threads.fork_join;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ForkJoinPool;

265
public class Test {

public static void main(String[] args) {


Path path = Paths.get("/home/anonbnr/Desktop/MEGA/Studies/");
String filter = "*.pdf";
FolderScanner fs = new FolderScanner(path, filter);

int processors = Runtime.getRuntime().availableProcessors();


ForkJoinPool pool = new ForkJoinPool(processors);
Long start = System.currentTimeMillis();

pool.invoke(fs);

Long end = System.currentTimeMillis();


System.out.println(
"There are " + fs.getResult() + " file(s) having the extension " + filter
);
System.out.println("Execution time: " + (end - start) + " ms");
}
}
Output
Scanning "/home/anonbnr/Desktop/MEGA/Studies/Shared_Studies/
M2_Informatique_AIGLE/S9/HMIN302 - E-applications/TD-TP/TP2/solution/part2/
gro_blog/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Resources"
for files having the extension "*.pdf"
Scanning "/home/anonbnr/Desktop/MEGA/Studies/Shared_Studies/
M2_Informatique_AIGLE/S9/HMIN302 - E-applications/TD-TP/TP2/solution/part2/
gro_blog/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Tests/Logout"
for files having the extension "*.pdf"
...
There are 1254 file(s) having the extension *.pdf
Execution time: 451 ms

4.7.4.6 Exemple définissant un seuil pour l’utilisation de Fork/Join

/*
* une classe contenant une méthode exécutant une tâche
* pouvant être parallélisée en utilisant Fork/Join
*/
package threads.fork_join;

import java.util.concurrent.RecursiveTask;

266
public class SuiteComputation extends RecursiveTask<Long> {

/* ATTRIBUTES */
private long debut, fin, result;
private static int SEUIL = 1_000;

/* CONSTRUCTOR */
public SuiteComputation() {
debut = fin = result = 0;
}

public SuiteComputation(long debut, long fin) {


this.debut = debut;
this.fin = fin;
}

/* METHODS */
public long getResult() {
return result;
}

public long getNumberOfIterations() {


return fin - debut;
}

public long computeResult() {


for (long i = 0; i <= fin; i++)
result += i;

return result;
}

@Override
protected Long compute() {
long nbIterations = getNumberOfIterations();

if (nbIterations < SEUIL) {


System.out.println("Monothread execution of task/sub-task");
result = computeResult();
}

else {
System.out.println("Fork/Join mode of execution of task");

// Dichotomic computation

267
long middle = nbIterations / 2;

SuiteComputation suite1 = new SuiteComputation(debut, debut + middle - 1);


suite1.fork();

SuiteComputation suite2 = new SuiteComputation(debut + middle, fin);


result = suite2.compute() + suite1.join();
}

return result;
}
}

/* classe de test */
package threads.fork_join;

import java.util.concurrent.ForkJoinPool;

public class Test {

public static void main(String[] args) {


ForkJoinPool pool = new ForkJoinPool();
SuiteComputation suiteComputation = new SuiteComputation(0, 10_000);

pool.invoke(suiteComputation);

System.out.println("Result: " + suiteComputation.getResult());


}
}
Output
Fork/Join mode of execution of task
Fork/Join mode of execution of task
Fork/Join mode of execution of task
Fork/Join mode of execution of task
Monothread execution of task/sub-task
Fork/Join mode of execution of task
Fork/Join mode of execution of task
Fork/Join mode of execution of task
Fork/Join mode of execution of task
Monothread execution of task/sub-task
Fork/Join mode of execution of task
Monothread execution of task/sub-task
Fork/Join mode of execution of task
Monothread execution of task/sub-task
Monothread execution of task/sub-task

268
Fork/Join mode of execution of task
Monothread execution of task/sub-task
Monothread execution of task/sub-task
Monothread execution of task/sub-task
Monothread execution of task/sub-task
Monothread execution of task/sub-task
Fork/Join mode of execution of task
Fork/Join mode of execution of task
Monothread execution of task/sub-task
Monothread execution of task/sub-task
Fork/Join mode of execution of task
Monothread execution of task/sub-task
Fork/Join mode of execution of task
Monothread execution of task/sub-task
Monothread execution of task/sub-task
Monothread execution of task/sub-task
Result: 292105640

5 Programmation Événementielle en Java

5.1 Introduction

5.1.1 Programmation procédurale vs. programmation événemen-


tielle

1. programmation procédurale :
• exécuter une application : exécuter la méthode main() d’une des
classes de l’application.
• terminer une application : fin de l’exécution de la méthode
main().
2. programmation événementielle :
• application = assemblage de composants graphiques.
• l’état de l’application change au fur et à mesure des interactions
des utilisateurs avec l’application.
• événement (event) : toute action utilisateur interagissant avec
l’interface graphique de l’application.
• écouteur d’événements (event listener) : un composant per-
mettant à une vue/fenêtre d’écouter l’occurrence d’un type
d’événements (e.g., clic d’un bouton)
• traitement d’un événement (event handler) : méthode de
traitement invoquée lors de l’occurrence d’un événement.
• exécuter une application : exécuter la méthode main().
• terminer une application :
1. indépendante de la fin de la méthode main().

269
2. arrêter la boucle d’attente d’événements déclenchés par
l’utilisateur.

5.1.2 Programmation événementielle en Java

Java offre deux bibliothèques permettant la programmation événementielle :


1. Java AWT (Abstract Window Toolkit) du package java.awt :
• la première bibliothèque introduite par Java ;
• contenu :
1. des composants graphiques lourds, car fortement liés à l’OS.
2. les événements de base dans le package java.awt.event.
2. Java Swing du package javax.swing :
• un remplacement partiel de Java AWT offrant des composants
graphiques légers :
1. de plus haut niveau (i.e., dans un container) ;
2. plus indépendants du OS.
• un complément d’événements aux événements de Java AWT.
3. changements entre AWT et Swing :
• la classe Frame est remplacée par JFrame ;
• la classe Button est remplacée par JButton ;
• la classe Dialog est remplacée par JDialog ;
• toutes les classes de la bibliothèque commencent par J : e.g. JFrame,
JButton, JTextField, . . .
4. remarque : il ne faut jamais mélanger les composants graphiques de
Swing avec ceux de AWT dans une même application.

Figure 18: “Interfaces graphiques en Java avec AWT et Swing”

270
5.2 Les composants graphiques

5.2.1 Les couleurs, polices, points, et dimensions

5.2.1.1 La classe Color


1. package : java.awt.Color.
2. description : une classe modélisant des couleurs dans un espace de
couleurs (par défaut l’espace standard RGB).
3. quelques constantes : toutes les constantes désignant des couleurs spé-
cifiques sont des instances statiques de Color.

Instance Alias Description


Color.BLACK Color.black la couleur noire
Color.BLUE Color.blue la couleur bleue
Color.CYAN Color.cyan la couleur cyan
Color.DARK_GRAY Color.darkGray la couleur gris foncé
Color.GRAY Color.gray la couleur gris
Color.GREEN Color.green la couleur vert
Color.LIGHT_GRAY Color.lightGray la couleur gris clair
Color.MAGENTA Color.mangenta la couleur mangenta
Color.ORANGE Color.orange la couleur orange
Color.PINK Color.pink la couleur rose
Color.RED Color.red la couleur rouge
Color.WHITE Color.white la couleur blanc
Color.YELLOW Color.yellow la couleur jaune

5.2.1.2 La classe Font


1. package : java.awt.
2. description : un texte modélisant des polices, utilisés pour le rendu des
graphiques textuels.
3. quelques méthodes :
/**
* crée une police de nom "name", ayant une taille de "size" pt et un style "style"
*
* style : une des constantes définies pour les polices (e.g. Font.BOLD)
*/
public Font(String name, int style, int size);
1. quelques constantes :
/** la constante pour le style bold */
public static final int BOLD;

271
/** la constante pour le style italic */
public static final int ITALIC;

5.2.1.3 La classe Point


1. package : java.awt.
2. description :
• une classe modélisant des points (x, y) dans un espace de coordonées
cartésien, dont les coordonées sont des entiers.
• l’espace de coordonnées cartésien définit son origine (i.e., le point
(0, 0)) au coin supérieur gauche de l’écran.
3. quelques méthodes :
/**
* retourne la coordonnée x du point
*/
public int getX();

/**
* retourne la coordonnée y du point
*/
public int getY();

/**
* retourne le point courant
*/
public Point getLocation();

/**
* déplace le point courant vers le point ("x", "y")
*/
public void setLocation(int x, int y);

/**
* déplace le point courant vers les coordonées du point "point"
*/
public void setLocation(Point point);

5.2.1.4 La classe Dimension


1. package : java.awt.
2. description : une classe modélisant des dimensions entières (largeur, hauteur).
3. quelques méthodes :
/**
* retourne la largeur de la dimension courante en tant qu'un double

272
*/
public double getWidth();

/**
* retourne la hauteur de la dimension courante en tant qu'un double
*/
public double getHeight();

/**
* retourne la dimension courante
*/
public Dimension getSize();

/**
* change la dimension courante pour avoir une largeur "width"
* et une hauteur "height"
*/
public void setSize(int width, int height);

/**
* change la dimension courante pour avoir une largeur entière
* obtenue à partir de "width" et une hauteur entière
* obtenue à partir de "height"
*/
public void setSize(double width, double height);

/**
* change la dimension courante pour avoir la largeur et hauteur
* de la dimension "d"
*/
public void setSize(Dimension d);

5.2.2 Les classes de base

5.2.2.1 La classe Component


1. package : java.awt.
2. description :
• une classe abstraite modélisant des objets ayant une interface
graphique et capables de réagir à des actions utilisateur.
• la superclasse de tous les composants graphiques (AWT et Swing)
en Java.
• joue le rôle de Component dans le design pattern Composite, sur
lequel AWT et Swing se sont basées dans leur conception.
3. quelques méthodes :

273
/**
* change la couleur de l'arrière-plan du composant courant en "c"
*/
public void setBackground(Color c);

/**
* retourne la couleur de l'arrière-plan du composant courant
*/
public Color getBackground();

/**
* change la couleur du premier plan du composant courant en "c"
*/
public void setForeground(Color c);

/**
* retourne la couleur du premier plan du composant courant
*/
public Color getForeground();

/**
* change la police du composant courant en "font"
*/
public void setFont(Font f);

/**
* retourne la police du composant courant
*/
public Font getFont();

/**
* redimensionne le composant courant pour avoir
* une largeur "width" et une hauteur "height"
* (par défaut, un composant n'a pas de taille)
*/
public void setSize(int width, int height);

/**
* redimensionne le composant courant pour avoir la largeur
* et hauteur de la dimension "d"
*/
public void setSize(Dimension d);

/**
* retourne la dimension (largeur, hauteur) désignant
* la dimension du composant courant.

274
*/
public Dimension getSize();

/**
* retourne la largeur du composant courant
*/
public int getWidth();

/**
* retourne la hauteur du composant courant
*/
public int getHeight();

/**
* redimensionne le composant courant pour avoir une dimension préférée
* définie par "d", qui sera prise en compte par le layout manager
* responsable de la disposition du composant courant dans son parent.
*
* Remarque : le redimensionnement selon une dimension préférée ne fonctionne
* que si le parent du composant utilise un layout manager
*/
public void setPreferredSize(Dimension d);

/**
* retourne la dimension préférée du composant courant
*/
public Dimension getPreferredSize();

/**
* déplace le composant courant vers le point ("x", "y")
* (par défaut, un composant est localisé au point (0, 0)
*/
public void setLocation(int x, int y);

/**
* retourne le point (x, y) désignant la localisation du composant courant.
*/
public Point getLocation();

/**
* retourne la coordonnée x du composant par rapport au point (0, 0)
*/
public int getX();

/**
* retourne la coordonnée y du composant par rapport au point (0, 0)

275
*/
public int getY();

/**
* déplace le composant courant vers le point ("x", "y")
* (le coin gauche supérieur de l'écran est au point (0, 0))
* et le redimensionne pour avoir une largeur de "width"
* et une hauteur de "height"
*/
public void setBounds(int x, int y, int width, int height);

/**
* change la visibilité du composant courant en "b"
* (par défaut le composant est invisible)
*/
public void setVisible(boolean b);

/**
* crée et retourne un contexte graphique pour le composant courant
* afin de pouvoir dessiner dessus.
*
* Remarque : retourne null si le composant courant est invisible
*/
public Graphics getGraphics();

/**
* redessine le composant courant
*
* Remarque : appelée automatiquement lorsqu'un événement écouté par le composant
* est émis
*/
public void repaint();

/**
* rattache l'écouteur d'événéments souris "listener" au composant courant
*/
public void addMouseListener(MouseListener listener);

/**
* définie si ce composant courant est activé/ désactivé selon la valeur de "b"
* (les composants sont activés par défaut)
*/
public void setEnabled(boolean b);

/**
* retourne true si le composant courant est activé, false sinon

276
*/
public boolean isEnabled();

5.2.2.2 La classe Container


1. package : java.awt.
2. description :
• une classe modélisant des composants conteneurs d’autres com-
posants.
• hérite de Component.
• joue le rôle de Composite dans le design pattern Composite.
3. propriétés :
• possède un LayoutManager (i.e., gestionnaire de disposition) qui
s’occupe de la disposition et de la taille de ses composants.
• la disposition et la taille des composants sont dynamiques et
s’adaptent aux modifications de dimensions du conteneur.
• chaque type de LayoutManager possède ses propres règles, définis-
sant la manière dont la gestion de la disposition et de la taille des
composants dans le conteneur est effectuée.
4. quelques méthodes :
/**
* ajoute le composant "comp" à la fin de ce conteneur et le retourne
*/
public Component add(Component comp);

/**
* ajoute le composant "comp" à la fin de ce conteneur et
* notifie le layout manager d'ajouter le composant
* au sein du layout associé au conteneur selon les contraintes définies
* par "constraints"
*/
public void add(Component comp, Object constraints);

/**
* utilise "manager" comme layout manager du conteneur courant
*/
public void setLayout(LayoutManager manager);

5.2.2.3 La classe Insets


1. package : java.awt.
2. description :
• une classe modélisant les bordures d’un Container.
• les bordures peuvent être visibles, invisibles, voire désigner un titre.

277
3. quelques méthodes :

5.2.2.4 La classe Window


1. package : java.awt.
2. description :
• une classe modélisant toutes les fenêtres.
• hérite de Container.
3. quelques méthodes :
/**
* retourne true is la fenêtre courante est toujours au premier plan
*/
public final boolean isAlwaysOnTop();

/**
* spécifie si la fenêtre courante est au premier plan.
* S'il existe plusieurs fenêtres au premier plan, leur ordre est non spécifié
* et dépend de la plateforme.
*/
public final void setAlwaysOnTop(boolean c) throws SecurityException;

/**
* ajuste la taille de la fenêtre courante de manière à englober les dimensions
* préférées de ses composants contenus.
*
* Remarque : ne doit être invoquée qu'une fois tous les composants de la
* fenêtre y soient ajoutés, sinon certains composants pourront être invisibles.
*/
public void pack();

/**
* change la position de la fenêtre courante relativement au composant "c",
* selon les scénarios suivants :
* 1. si "c" est "null", alors la fenêtre est placée au centre de l'écran.
* 2. si "c" est non "null", mais invisible, alors la fenêtre est placée au
* centre de la fenêtre contenant "c".
* 3. si "c" est non "null" est visible, alors la fenêtre est placée de
* manière à avoir son centre et celui de "c" coincident.
*/
public void setLocationRelativeTo(Component c);

5.2.2.5 La classe Frame


1. package : java.awt.
2. description :

278
• une classe modélisant tous les frames AWT.
• hérite de Window.
3. quelques méthodes :
/**
* affecte "title" comme titre de la fenêtre courante
*/
public void setTitle(String title);

/**
* retourne le titre de la fenêtre courante
*/
public String getTitle();

/**
* retourne true si la fenêtre courante est redimensionnable.
* (initialement toutes les fenêtres le sont)
*/
public boolean isResizable();

/**
* spécifie si la fenêtre courante est redimensionnable selon la valeur de "c"
*/
public void setResizable(boolean c);

/**
* retourne true si la fenêtre courante est non décorée
* (i.e., n'a ni des contours ni des boutons de contrôle)
* (initialement toutes les fenêtres ne le sont pas)
*/
public boolean isUndecorated();

/**
* spécifie si la fenêtre courante est décorée selon la valeur de "c"
*
* remarque : la fenêtre ne doit pas être visible lors de l'appel de cette méthode
*/
public void setUndecorated(boolean c);

5.2.2.6 La classe JComponent


1. package : javax.swing.
2. description :
• une classe abstraite modélisant tous les composants Swing, sauf les
fenêtres gérée toujours par Window d’AWT.
• hérite de Container.

279
3. quelques méthodes :
/**
* appelle la méthode paint() du délégué de l'UI, si celui-ci est non null
* en lui passant une copie du contexte graphique "g" pour dessiner dessus
*
* Remarque : cette méthode est invoquée automatiquement :
- quand le composant courant est instancié
- quand le conteneur du composant ou le composant lui-même alternent en
termes de visibilité
- quand le conteneur du composant ou le composant lui-même sont redimensionnés
*/
protected void paintComponent(Graphics g);

5.2.3 Les fenêtres Swing

5.2.3.1 Terminologie
1. frame :
• une fenêtre graphique indépendante de toute autre fenêtre, appelée
aussi “fenêtre cadre”.
• contient un titre, et une bordure.
2. panneau (panel) : un composant graphique représentant une surface rect-
angulaire sans bordure, utilisé comme conteneur de composants.

5.2.3.2 La classe JFrame

5.2.3.2.1 Structure
Les composants formant la structure d’un JFrame :
1. la fenêtre ;
2. le root pane (en vert), le conteneur principal de la fenêtre.
3. le layered pane (en violet), un panneau composé du conteneur global et
de la barre de menu.
4. la barre de menu (en orange), quand il y en a une.
5. le content pane (en rose), un panneau consitutant un conteneur global
dans lequel on met tous nos composants.
6. le glass pane (en transparence), une couche utilisée pour intercepter les
actions de l’utilisateur avant qu’elles ne parviennent aux composants.

5.2.3.2.2 Métadonnées
1. package : javax.swing.
2. description :

280
Figure 19: Structure d’une JFrame

281
• une classe modélisant tous les frames Swing.
• hérite de Frame.
• par défaut, utilise un BorderLayout comme gestionnaire de disposi-
tion.
3. quelques méthodes :
/**
* spécifier l'action par défaut à effectuer lors de la fermeture de la frame
* courante, où "operation" désigne l'une des constantes de l'interface
* `WindowConstants`.
* Par défaut, l'action à effectuer est associée à "WindowConstants.HIDE_ON_CLOSE".
*/
public void setDefaultCloseOperation(int operation);

/**
* retourne le "content pane" de la fenêtre courante.
*/
public Container getContentPane();

/**
* changer le "content" de la fenêtre courante en "contentPane"
*/
public void setContentPane(Container contentPane);

5.2.3.3 L’interface WindowConstants


1. package : javax.swing.
2. description : une interface fournissant des constantes pour contrôler
l’action liée à la fermeture d’une fenêtre Swing.
3. constantes :
• DISPOSE_ON_CLOSE : constante associée à l’opération “cacher et sup-
primer de la mémoire” d’une fenêtre Swing lors de sa fermeture.
• DO_NOTHING_ON_CLOSE : constante associée à l’opération de “rien
faire” pour une fenêtre Swing lors de sa fermeture.
• EXIT_ON_CLOSE : constante associée à l’opération “quitter
l’application” pour une fenêtre Swing lors de sa fermeture.
• HIDE_ON_CLOSE : constante associée à l’opération “cacher” pour une
fenêtre Swing lors de sa fermeture.

5.2.3.4 La classe JPanel


1. package : javax.swing.
2. description :
• une classe modélisant les panneaux Swing.
• hérite de JComponent.
• par défaut, utilise un FlowLayout comme gestionnaire de disposition.

282
3. quelques méthodes :

5.2.4 Les contextes graphiques

5.2.4.1 La classe Graphics


1. package : javax.swing.
2. description : une classe abstraite, superclasse de tous les contextes
graphiques, ceux-ci permettant à une application de dessiner sur des com-
posants et des images “off-screen”.
3. quelques méthodes :
/**
* dessine le contour d'un ovale délimité par le rectangle
* ayant une largeur "width" et une hauteur "height"
* à partir du point ("x", "y")
*/
public abstract void drawOval(int x, int y, int width, int height);

/**
* dessine un ovale rempli et délimité par le rectangle
* ayant une largeur "width" et une hauteur "height"
* à partir du point ("x", "y")
*/
public abstract void fillOval(int x, int y, int width, int height);

/**
* dessine le contour d'un rectangle
* ayant une largeur "width" et une hauteur "height"
* à partir du point ("x", "y")
*/
public void drawRect(int x, int y, int width, int height);

/**
* dessine un rectangle rempli
* ayant une largeur "width" et une hauteur "height"
* à partir du point ("x", "y")
*/
public abstract void fillRect(int x, int y, int width, int height);

/**
* dessine le contour d'un rectangle rond
* ayant une largeur "width", une hauteur "height",
* une largeur d'arc du coin "arcWidth" et une longueur d'arc du coin "arcHeight"
* à partir du point ("x", "y")
*/

283
public abstract void drawRoundRect(int x, int y, int width, int height,
int arcWidth, int arcHeight);

/**
* dessine un rectangle rond rempli
* ayant une largeur "width", une hauteur "height",
* une largeur d'arc du coin "arcWidth" et une longueur d'arc du coin "arcHeight"
* à partir du point ("x", "y")
*/
public abstract void fillRoundRect(int x, int y, int width, int height,
int arcWidth, int arcHeight);

/**
* dessine une ligne passant par les points ("x1", "y1") et ("x2", "y2")
*/
public abstract void drawLine(int x1, int y1, int x2, int y2);

/**
* dessine un polygone de "nPoints" points rempli
* tels que chaque point pi = ("xPoints[ i]", "yPoints[ i]").
*
* Remarque : le dernier point est lié au premier point automatiquement
*/
public abstract void fillPolygon(int[] xPoints, int[] yPoints, int nPoints);

/**
* dessine le contour d'un polygone de "nPoints" points,
* tels que chaque point "pi" = ("xPoints[ i]", "yPoints[ i]")
*
* Remarque : le dernier point est lié au premier point automatiquement
*/
public abstract void drawPolygon(int[] xPoints, int[] yPoints, int nPoints);

/**
* dessine le texte "str" tel que la ligne de fond du premier caractère de "str"
* passe par le point ("x", "y")
*/
public abstract void drawString(String str, int x, int y);

/**
* change la couleur de l'objet graphique courant en "c"
*/
public abstract void setColor(Color c);

/**
* retourne la couleur de l'objet graphique courant

284
*/
public abstract Color getColor();

/**
* change la police de l'objet graphique courant en "font"
* (uniquement pour les objets textuels)
*/
public abstract void setFont(Font font);

/**
* retourne la police de l'objet graphique courant
*/
public abstract Font getFont();

/**
* dessine une image "img" en commençant du point ("x", "y") et en lui affectant
* un observateur d'images "observer".
*/
public abstract boolean drawImage(Image img, int x, int y,
ImageObserver observer);

/**
* dessine une image "img" en commençant du point ("x", "y") et en lui affectant
* les dimensions ("width", "height") et un observateur d'images "observer".
*
* Remarque : L'image est mise à l'échelle des dimensions fournies si nécessaire.
*/
public abstract boolean drawImage(Image img, int x, int y, int width, int height,
ImageObserver observer);

5.2.4.2 La classe Graphics2D


1. package : javax.swing.
2. description :
• une amélioration des contextes graphiques de Graphics, surtout pour
les contextes 2D, images, et contextes textuels en termes :
1. du contrôle de la géometrie des composants graphiques ;
2. des transformations des coordonées ;
3. de la gestion de la couleur ;
4. de la disposition des objets textuels.
• hérite de Graphics
3. quelques méthodes :
/**
* affecte "paint" comme le motif de couleurs utilisé par le contexte graphique
* courant pour ses opération draw() et fill()

285
*/
public abstract void setPaint(Paint paint);

5.2.5 Les images

5.2.5.1 La classe Image


1. package : java.awt
2. description : une classe abstraite, superclasse de tous les objets
graphiques désignant des images.
3. quelques méthodes :

5.2.5.2 La classe BufferedImage


1. package : java.awt
2. description :
• une classe modélisant des images tamponnées (i.e., dont les données
sont contenues dans un tampon).
• hérite d’Image.
3. quelques méthodes :

5.2.5.3 L’interface ImageObserver


1. package : java.awt
2. description : une interface à implémenter par tous les objets graphiques
observateurs d’images qui seront notifiés lors de la construction d’une im-
age pour se mettre à jour (i.e., le patron de conception Observer).
3. quelques méthodes :

5.2.5.4 La classe ImageIO


1. package : javax.imageio
2. description : une classe utilitaire contenant des méthodes statiques
pour la localisation facile de lecteurs/écrivains d’images (i.e., les classes
javax.imageio.ImageReader et javax.imageio.ImageWriter respec-
tivement) et la réalisation d’opérations d’encodage/décodage d’images.
3. quelques méthodes :
/**
* une méthode retournant une image tamponnée du fichier "input" pointant
* vers une image ou null si aucune ImageReader n'est capable de lire l'image.
*/
public static BufferedImage read(File input) throws IOException;

286
5.2.6 Les formes et les paintures

5.2.6.1 L’interface Shape


1. package : java.awt.
2. description : une interface offrant l’interface commune à tous les com-
posants graphiques ayant des formes géométriques.
3. quelques méthodes :

5.2.6.2 L’interface Paint


1. package : java.awt.
2. description : une interface pour la génération de motifs de couleur utilisés
par les opération de Graphics2D, qui seront utilisés par les opérations
draw() et fill().
3. quelques méthodes :

5.2.6.3 La classe GradientPaint


1. package : java.awt.
2. description :
• une classe modélisant des motifs de gradients de couleurs linéaires
permettant de définir les opérations draw() et fill() d’un contexte
graphique Graphics2D.
• implémente Paint.
3. quelques méthodes :
/**
* crée un gradient des couleurs "color1" et "color2" passant par les points
* ("x1", "y1") et ("x2", "y2"), sans répétition
*/
public GradientPaint(float x1, float y1, Color color1, float x2, float y2,
Color color2);

/**
* crée un gradient des couleurs "color1" et "color2" passant par les points
* ("x1", "y1") et ("x2", "y2"), avec répétition si "cyclic" vaut true, et sans
* répétition sinon
*/
public GradientPaint(float x1, float y1, Color color1, float x2, float y2,
Color color2, boolean cyclic);

5.2.7 Les gestionnaires de disposition (Layout Managers)

5.2.7.1 L’interface LayoutManager

287
1. package : java.awt.
2. description : une interface offrant l’interface commune à tous les layout
managers.
3. quelques méthodes :

5.2.7.2 L’interface LayoutManager2


1. package : java.awt.
2. description :
• une interface ajoutant la notion de contraintes pour l’implémentation
des layout managers.
• hérite de l’interface LayoutManager.
3. quelques méthodes :

5.2.7.3 La classe BorderLayout

Figure 20: Exemple d’un applet géré par un BorderLayout

1. package : java.awt.
2. description :

288
• une classe modélisant un layout manager divisant l’espace disponible
dans un conteneur pour l’ajout de composants en 5 zones: nord, sud,
est, ouest, et centre.
• implémente LayoutManager1 et LayoutManager2.
3. propriétés :
• chaque zone est identifée par une constante correspondante :
1. BorderLayout.NORTH : zone du nord occupant ;
2. BorderLayout.SOUTH : zone du sud ;
3. BorderLayout.EAST : zone de l’est ;
4. BorderLayout.WEST : zone de l’ouest ;
5. BorderLayout.CENTER : zone centrale ;
• chaque zone peut contenir au plus un composant.
• si on essaye d’ajouter un composant à une zone déjà occupée, le
composant ajouté se superpose avec le composant déjà ajouté.
• l’ajout d’un élément à un frame utilisant un BorderLayout se fait
par défaut dans la zone centrale.
• un composant ajouté en BorderLayout.CENTER occupe la totalité de
la largeur et de la hauteur de son conteneur.
• un composant ajouté en BorderLayout.NORTH ou BorderLayout.SOUTH
occupe la totalité de la largeur de son conteneur mais une hauteur
proportionnelle à celle de son contenu.
• un composant ajouté en BorderLayout.EAST ou BorderLayout.WEST
occupe la totalité de l’hauteur de son conteneur mais une largeur
proportionnelle à celle de son contenu.
4. quelques méthodes :

5.2.7.4 La classe FlowLayout


1. package : java.awt.
2. description :
• une classe modélisant un layout manager ajoutant les composants
dans un conteneur les uns à la suite des autres, selon leur ordre
d’ajout.
• implémente LayoutManager.
3. propriétés :
• la direction du flôt d’ajout des composants est déterminée
par l’orientation du conteneur utilisant le gestionnaire (i.e.,
ComponentOrientation.LEFT_TO_RIGHT ou ComponentOrientation.RIGHT_TO_LEFT).
• par défaut, le flôt d’ajout des composants est de gauche à droite et
de haut en bas (i.e., ComponentOrientation.LEFT_TO_RIGHT).
• chaque composant ajouté occupe la taille minimale nécessaire pour
son affichage correct.
4. quelques méthodes :

5.2.7.5 La classe GridLayout

289
Figure 21: Exemple d’un applet géré par un FlowLayout

Figure 22: Exemples de layouts avec GridLayout

290
1. package : java.awt.
2. description :
• une classe modélisant un layout manager ajoutant les composants
dans une grille rectangulaire dont chaque cellule possède la même
taille, selon leur ordre d’ajout.
• implémente LayoutManager.
3. propriétés :
• la direction du flôt d’ajout des composants est déterminée par
l’orientation du conteneur utilisant le gestionnaire.
• par défaut, le flôt d’ajout des composants est de gauche à droite et
de haut en bas.
4. quelques méthodes :
/**
* crée un GridLayout Swing contenant une grille
* de "rows" lignes et "cols" colonnes
*/
public GridLayout(int rows, int cols);

/**
* définie "cols" comme nombre de colonnes de la grille
* du layout manager courant
*/
public void setColumns(int cols);

/**
* définie "rows" comme nombre de lignes de la grille
* du layout manager courant
*/
public void setRows(int rows);

/**
* définie l'espace horizontal entre les composants dans les cellules de la grille
* du layout manager courant par "hgap" pixels
*/
public void setHgap(int hgap);

/**
* définie l'espace vertical entre les composants dans les cellules de la grille
* du layout manager courant "vgap" pixels
*/
public void setVgap(int vgap);

5.2.7.6 La classe BoxLayout


1. package : java.awt.

291
Figure 23: Exemple de BoxLayout

2. description :
• une classe modélisant un layout manager ajoutant des composants
à la suite sur un axe de positionnement qui est soit horizontal (une
ligne), soit vertical (sur une colonne).
• implémente LayoutManager1 et LayoutManager2.
3. quelques constantes :
• BoxLayout.X_AXIS : les composants sont positionnés horizontale-
ment de gauche à droite.
• BoxLayout.Y_AXIS : les composants sont positionnés verticalement
de haut en bas.
• BoxLayout.LINE_AXIS :
1. les composants sont positionnés comme s’ils étaient des mots sur
une ligne
2. leur orientation dépend de la ComponentOrientation de leur con-
teneur :
– si elle est verticale, alors ils sont positionnés verticalement
de haut en bas.
– si elle est horizontale, alors si elle est, de plus, de gauche
à droite, ils sont positionnés horizontalement de gauche à
droite, sinon ils sont positionnés horizontalement de droite à
gauche.
• BoxLayout.PAGE_AXIS :
1. les composants sont positionnés comme s’ils étaient des lignes
sur une page
2. leur orientation dépend de la ComponentOrientation de leur con-
teneur (idem que BoxLayout.LINE_AXIS).
4. quelques méthodes :
/**
* crée un BoxLayout pour le conteneur cible "target", selon l'axe de positionnement
* défini par "axis"
*/
public BoxLayout(Container target, int axis);

292
5.2.7.7 La classe Box
1. package : javax.swing.
2. description :
• une classe modélisant des boîtes désignant des containers utilisant un
BoxLayout par défaut.
• hérite de JComponent
3. quelques méthodes :
/**
* crée une Box qui positionne ses éléments horizontalement de gauche à droite
*/
public static Box createHorizontalBox();

/**
* crée une Box qui positionne ses éléments verticalement de haut en bas
*/
public static Box createVerticalBox();

5.2.7.8 La classe CardLayout


1. package : java.awt.
2. description :
• une classe modélisant un gestionnaire de disposition qui traite son
conteneur parent et ses composants en tant qu’une pile de cartes, où
chaque carte (composant) est visible à la fois.
• implémente LayoutManager1 et LayoutManager2.
3. quelques méthodes :
/**
* affiche la première carte du conteneur "parent"
*/
public void first(Container parent);

/**
* affiche la prochaine carte du conteneur "parent".
*
* Remarque : si la carte visible du conteneur avant le changement
est la dernière carte, alors la première carte est envoyée
*/
public void next(Container parent);

/**
* affiche la carte précédente du conteneur "parent".
*
* Remarque : si la carte visible du conteneur avant le changement
est la première carte, alors la dernière carte est envoyée

293
*/
public void previous(Container parent);

/**
* affiche la dernière carte du conteneur "parent"
*/
public void last(Container parent);

/**
* affiche la carte du conteneur "parent" associée au nom "name"
*/
public void show(Container parent, String name);

5.2.7.9 La classe GridBagLayout


1. package : java.awt.
2. description :
• une classe modélisant un gestionnaire de disposition positionnant les
composants d’un conteneur parent dans une grille rectangulaire dy-
namique, tels que chaque composant occupe une/plusieurs cellules
de la grille appelée sa zone d’affichage (display area).
• implémente LayoutManager et LayoutManager2.
3. quelques méthodes :

5.2.7.10 La classe GridBagConstraints


1. package : java.awt.
2. description : une classe modélisant des contraintes pour les conteneurs
dont les composants sont positionnés par un GridBagLayout.
3. quelques constantes et attributs :
/**
* spécifie la position en x du composant dans la grille
* valeur spéciale : RELATIVE, quand le composant doit être ajouté immédiatement
* après le dernier composant ajouté sur l'axe des X.
*/
public int gridx;

/**
* spécifie la position en y du composant dans la grille
* valeur spéciale : RELATIVE, quand le composant doit être ajouté immédiatement
* après le dernier composant ajouté sur l'axe des Y.
*/
public int gridy;

/**

294
* spécifie le nombre de colonnes occupées par le composant
* (par défaut vaut 1 et doit être non nul)
*
* valeurs spéciales :
* 1. REMAINDER : le composant est le dernier composant dans la ligne
* 2. RELATIVE : le composant est l'avant dernier composant dans la ligne
*/
public int gridwidth;

/**
* spécifie le nombre de lignes occupées par le composant
* (par défaut vaut 1 et doit être non nul)
*
* valeurs spéciales :
* 1. REMAINDER : le composant est le dernier composant dans la colonne
* 2. RELATIVE : le composant est l'avant dernier composant dans la colonne
*/
public int gridheight;

/**
* 1. quand utilisée avec gridx, gridy: le composant doit être ajouté
* immédiatement après le dernier composant ajouté sur l'axe des X, Y.
* 2. quand utilisé avec gridwidth, gridheight: le composant est l'avant
* dernier composant dans la ligne/ colonne
*/
public static final int RELATIVE;

/**
* quand utilisé avec gridwidth, gridheight : le composant est le dernier dans
* la ligne, colonne.
*/
public static final int REMAINDER;

/**
* à utiliser lorsque la taille de la zone d'affichage du composant est plus
* large que sa taille préférée. il détermine si le composant à besoin d'être
* redimensionné, et si oui alors comment le faire, selon les constantes
* NONE, HORIZONTAL, VERTICAL, BOTH.
*/
public int fill;

/**
* ne pas redimensionner le composant
*/
public static final int NONE;

295
/**
* redimensionner le composant horizontalement seulement
*/
public static final int HORIZONTAL;

/**
* redimensionner le composant verticalement seulement
*/
public static final int VERTICAL;

/**
* redimensionner le composant horizontalement et verticalement
*/
public static final int BOTH;

/**
* ancrage du composant dans la cellule (i.e., son alignement), pouvant avoir les
* valeurs suivantes :
* 1. FIRST_LINE_START : en haut à gauche.
* 1. PAGE_START : en haut au centre.
* 1. FIRST_LINE_END : en haut à droite.
* 1. LINE_START : au milieu à gauche.
* 1. CENTER : au milieu et centré.
* 1. LINE_END : au milieu à droite.
* 1. LAST_LINE_START : en bas à gauche.
* 1. PAGE_END : en bas au centre.
* 1. LAST_LINE_END : en bas à droite.
* 1. ...
*/
public int anchor;

/**
* en haut à gauche de sa zone d'affichage
* (ou en haut à droite selon l'orientation du composant)
*/
public static final int FIRST_LINE_START;

/**
* en haut au centre de sa zone d'affichage
*/
public static final int PAGE_START;

/**
* en haut à droite de sa zone d'affichage
* (ou en haut gauche selon l'orientation du composant)
*/

296
public static final int FIRST_LINE_END;

/**
* au milieu à gauche de sa zone d'affichage
* (ou à droite selon l'orientation du composant)
*/
public static final int LINE_START;

/**
* positionne le composant au milieu et centré dans sa zone d'affichage
*/
public static final int CENTER;

/**
* au milieu à droite de sa zone d'affichage
* (ou à gauche selon l'orientation du composant)
*/
public static final int LINE_END;

/**
* en bas à gauche de sa zone d'affichage
* (ou à droite selon l'orientation du composant)
*/
public static final int LAST_LINE_START;

/**
* en bas au centre de sa zone d'affichages
*/
public static final int PAGE_END;

/**
* en bas à droite de sa zone d'affichage
* (ou à gauche inversement selon l'orientation du composant)
*/
public static final int LAST_LINE_END;

/**
* si la grille est plus large que la zone d'affichage du composant, alors
* l'espace est redistribué proportionnellement aux valeurs de weightx des
* différentes colonnes
*/
public double weightx;

/**
* si la grille est plus haute que la zone d'affichage du composant, alors
* l'espace est redistribué proportionnellement aux valeurs de weighty des

297
* différentes lignes
*/
public double weighty;

/**
* l'espacement à droite et à gauche du composant
*/
public int ipadx;

/**
* l'espacement à au dessus et au dessous du composant
*/
public int ipady;

/**
* l'espacement autour du composant, s'ajoutant à ses propriétés ipadx et ipady
*/
public Insets insets();

5.2.8 Les boutons

5.2.8.1 La classe AbstractButton


1. package : javax.swing.
2. description :
• une classe abstraite encapsulant les comportements communs à tous
les boutons et items de menu Swing.
• hérite de JComponent.
3. quelques méthodes :
/**
* affecte "text" comme valeur de l'étiquette textuelle du bouton courant
*/
public void setText(String text);

/**
* retourne le contenu de l'étiquette textuelle du bouton courant
*/
public String getText();

/**
* rattache l'écouteur d'événéments d'actions "listener" au bouton courant
*/
public void addActionListener(ActionListener listener);

298
5.2.8.2 La classe JButton
1. package : javax.swing.
2. description :
• une classe modélisant tous les boutons cliquables Swing.
• hérite de AbstractButton.
3. quelques méthodes :
/**
* crée un boutton Swing contenant le texte "text"
*/
public JButton(String text);

5.2.9 Les événements et leurs écouteurs

5.2.9.1 La classe EventObject


1. package : java.util
2. description: la superclasse de tous les événements émis sur un composant
et fournissant des informations dessus.
3. quelques méthodes :
/**
* retourne le composant source sur lequel l'événement courant est émis.
*/
public Object getSource();

5.2.9.2 La classe MouseEvent


1. package : java.awt.event
2. description: une classe modélisant tous les événements émis par la souris
sur un composant et fournissant des informations dessus.
3. événéments concernés :
• un bouton de la souris est foncé (pressed).
• un bouton de la souris est lâché (released).
• un bouton de la souris est cliqué (pressed and released).
• le curseur de la souris entre et survole une zone non obscure d’un
composant.
• le curseur de la souris sort et arrête de survoler une zone non obscure
d’un composant.
• la souris est bougée (moved).
• la souris est traînée (dragged).
4. quelques méthodes :
/**
* retourne la position x horizontale de l'événement relativement au composant
* cible de l'événement (i.e., sur lequel l'événement est émis)

299
*/
public int getX();

/**
* retourne la position y verticale de l'événement relativement au composant
* cible de l'événement
*/
public int getY();

5.2.9.3 L’interface MouseListener


1. package : java.awt.event
2. description: une interface commune à tous les écouteurs des événements
émis par la souris (i.e., MouseEvent).
3. quelques méthodes :
/**
* méthode invoquée quand le bouton de la souris est foncé (pressed)
* sur un composant ayant un MouseListener y rattaché
*/
void mousePressed(MouseEvent e);

/**
* méthode invoquée quand le bouton de la souris est lâché (released)
* sur un composant ayant un MouseListener y rattaché
*/
void mouseReleased(MouseEvent e);

/**
* méthode invoquée quand le bouton de la souris est cliquée (pressed and released)
* sur un composant ayant un MouseListener y rattaché
*/
void mouseClicked(MouseEvent e);

/**
* méthode invoquée quand la souris entre et survole une zone non obscure
* d'un composant ayant un MouseListener y rattaché
*/
void mouseEntered(MouseEvent e);

/**
* méthode invoquée quand la souris sort et arrête de survoler une zone
* non obscure d'un composant ayant un MouseListener y rattaché
*/
void mouseExited(MouseEvent e);

300
5.2.9.4 La classe ActionEvent
1. package : java.awt.event
2. description: une classe modélisant tous les événements d’action émis par
un composant et fournissant des informations dessus.
3. quelques méthodes :

5.2.9.5 L’interface ActionListener


1. package : java.awt.event
2. description: une interface commune à tous les écouteurs des événements
d’action émis (i.e., ActionEvent).
3. quelques méthodes :
/**
* méthode invoquée quand l'événement d'une action "e" est émis.
*/
void actionPerformed(ActionEvent e);

5.2.10 Les formulaires

5.2.10.1 La classe JLabel


1. package : javax.swing.
2. description :
• une classe modélisant des étiquettes désignant les composants Swing
affichables et non modifiables par des actions utilisateurs (e.g. un
texte, une image, . . .).
• hérite de JComponent.
3. quelques méthodes :
/**
* crée une étiquette Swing contenant le texte "text"
*/
public JLabel(String text);

/**
* affecte "text" comme valeur de l'étiquette courante
*/
public void setText(String text);

/**
* retourne le contenu de l'étiquette courante
*/
public String getText();

/**

301
* définie l'alignement horizontal de l'étiquette courant selon la constante
* d'alignement "alignment"
*/
public void setHorizontalAlignment(int alignment);

/**
* retourne l'alignement horizontal de l'étiquette courante
*/
public int getHorizontalAlignment();

/**
* définie l'alignement vertical de l'étiquette courant selon la constante
* d'alignement "alignment"
*/
public void setVerticalAlignment(int alignment);

/**
* retourne l'alignement vertical de l'étiquette courante
*/
public int getVerticalAlignment();

5.2.10.2 L’interface SwingConstants


1. package: javax.swing
2. description: une classe défnissant des constantes de positionnement et
d’alignement de composants sur l’écran.
3. quelques constantes:
/**
* position centrale dans une zone d'affichage
*/
static final int CENTER;

5.2.10.3 La classe JTextComponent


1. package : javax.swing.
2. description :
• une classe abstraite, superclasse de tous les composants Swing
textuels modifiables par des actions utilisateurs.
• hérite de JComponent.
3. quelques méthodes :

5.2.10.4 La classe JTextField


1. package : java.awt.
2. description :

302
• une classe modélisant un champ de texte monoligne modifiable par
des actions utilisateurs.
• hérite de JTextComponent.
3. quelques méthodes :
/**
* crée un JTextField de taille "columns"
* et contenant le texte "text"
*/
public JTextField(String text, int columns);

5.2.11 Les menus et boîtes de dialogue

5.2.11.1 La classe Dialog

5.2.11.2 La classe JDialog

303

Vous aimerez peut-être aussi