Vous êtes sur la page 1sur 121

Module IP1

Programmation Orientée Objets


Le Langage JAVA

Amaury Habrard
Université de Saint-Etienne
amaury.habrard@univ-st-etienne.fr
Mis à jour par François-Xavier Dupé
Université d’Aix-Marseille
francois-xavier.dupe@lif.univ-mrs.fr
2
Table des matières

1 Avant propos 7

2 Introduction : le langage JAVA 9


2.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2 Historique du langage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2.1 Quelques mots clés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3 Java et la programmation orientée objet . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3.1 Le concept d’objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3.2 Indépendance vis à vis de la plateforme . . . . . . . . . . . . . . . . . . . . 11
2.4 Structure d’un programme JAVA . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4.1 Que contient un fichier source ? . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4.2 Execution d’un programme Java . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4.3 La machine virtuelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4.4 La méthode main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4.5 Utilisation d’arguments dans la fonction main . . . . . . . . . . . . . . . . . 13
2.5 Installation et utilisation de Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.6 Types, expressions et structures de contrôle fondamentales . . . . . . . . . . . . . . 15
2.6.1 les types primitifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
La notion de type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
le type entier (int) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Les flottants/réels (float ou double) . . . . . . . . . . . . . . . . . . . . . . 16
Le type caractère (char) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Le type booléen (boolean) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Initialisations et constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.6.2 Opérateurs et expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Opérateurs arithmétiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Opérateurs de comparaison . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Opérateurs logiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Opérateurs d’affectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Conversion de types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Opérateur conditionnel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Priorité des opérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.6.3 Les structures de contrôle . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
do-while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Les instructions break et continue . . . . . . . . . . . . . . . . . . . . . . . 24
2.6.4 Les éléments spécifiques au langage JAVA . . . . . . . . . . . . . . . . . . . 25
2.7 Une première introduction au type String . . . . . . . . . . . . . . . . . . . . . . . 25
2.8 Règles d’écriture d’un programme JAVA . . . . . . . . . . . . . . . . . . . . . . . 26

3
4 TABLE DES MATIÈRES

2.8.1 Les identificateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26


2.8.2 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.8.3 Mots clés réservés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.8.4 Remarques globales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.8.5 Commentaires sur le rendu d’un programme Java . . . . . . . . . . . . . . . 27
2.8.6 Liens sur la programmation JAVA . . . . . . . . . . . . . . . . . . . . . . . 28
2.8.7 Bibliographie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.9 Exercices de cours . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.9.1 Compilateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.9.2 Mots croisés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

3 La notion d’objets : définition et utilisation 31


3.1 Introduction au développement objets . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.2 La notion de classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.2.1 Exemple de classe : . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.2.2 Création d’un objet à l’aide d’une classe . . . . . . . . . . . . . . . . . . . . 34
3.2.3 Exemple d’utilisation dans un programme complet . . . . . . . . . . . . . . 35
3.3 La notion de constructeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.4 Affectation et comparaison d’objets . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.4.1 La référence null . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.5 Le ramasse-miettes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.6 L’encapsulation de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.7 Champs et méthodes statiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.7.1 Variables statiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.7.2 Les méthodes statiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.8 Surdéfinition/surcharge de méthodes . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.8.1 Surdéfinition de constructeurs . . . . . . . . . . . . . . . . . . . . . . . . . . 45
3.9 La copie d’objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.10 Autoréférencement : le mot clé this . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.11 Remarques sur la définition de méthodes . . . . . . . . . . . . . . . . . . . . . . . . 49
3.11.1 Méthodes de type procédures et fonctions . . . . . . . . . . . . . . . . . . . 49
3.11.2 Les arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.11.3 Les variables locales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.11.4 La récursivité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.12 Les paquetages (packages) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.12.1 Attribution d’une classe à un paquetage . . . . . . . . . . . . . . . . . . . . 51
3.12.2 Utilisation d’une classe située dans une paquetage . . . . . . . . . . . . . . 52
3.12.3 Droits d’accès . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.13 Les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.13.1 Déclaration et création . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.13.2 Tableau en argument ou en retour . . . . . . . . . . . . . . . . . . . . . . . 56
3.13.3 Les tableaux à plusieurs indices . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.14 Exercices de cours . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
3.14.1 Compilateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
3.14.2 Qui suis-je ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
3.14.3 Compilateur 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.14.4 Qui suis-je ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.14.5 Compilateur 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.14.6 Mots croisés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
3.14.7 Mots croisés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
TABLE DES MATIÈRES 5

4 Les concepts d’Héritage et de Polymorphisme 69


4.1 Présentation et définition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.2 Construction et initialisation des objets dérivés . . . . . . . . . . . . . . . . . . . . 72
4.3 Redéfinition, surdéfinition et héritage . . . . . . . . . . . . . . . . . . . . . . . . . . 74
4.3.1 Redéfinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
4.3.2 La surchage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.3.3 En résumé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.4 Le polymorphisme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.4.1 Tableaux hétérogènes d’objets . . . . . . . . . . . . . . . . . . . . . . . . . . 77
4.4.2 Règles du polymorphisme en Java . . . . . . . . . . . . . . . . . . . . . . . 78
4.5 La super-classe Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.6 Accès par membres protégés : protected . . . . . . . . . . . . . . . . . . . . . . . 80
4.7 Méthodes et classes finales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
4.8 Les classes abstraites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
4.9 Les interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
4.9.1 Définition et mise en œuvre . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
4.9.2 Intérêt des interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
4.10 Connaı̂tre la classe et les types d’un objet . . . . . . . . . . . . . . . . . . . . . . . 83
4.10.1 L’opérateur instanceof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
4.10.2 La class Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
4.11 Exercices de Cours . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
4.11.1 Classes abstraites - classes concrètes . . . . . . . . . . . . . . . . . . . . . . 85
4.11.2 Compilateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
4.11.3 Qui suis-je ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
4.11.4 Vrai ou Faux ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

5 La gestion d’exceptions 89
5.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
5.2 Gestion de plusieurs exceptions et transmission d’informations . . . . . . . . . . . 90
5.3 Dérivation et redéclenchement d’exceptions . . . . . . . . . . . . . . . . . . . . . . 93
5.4 Le bloc finally . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
5.5 Les exceptions standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.6 Exercices de Cours . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
5.6.1 Vrai ou Faux ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
5.6.2 Mots croisés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

6 Les chaı̂nes de caractères en Java 99


6.1 Le type String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
6.1.1 Les chaı̂nes de caractères sont des objets . . . . . . . . . . . . . . . . . . . . 99
6.1.2 Affichage d’une chaı̂ne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
6.1.3 Longueur d’une chaı̂ne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
6.1.4 accès aux caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
6.1.5 Concaténation : l’opérateur + . . . . . . . . . . . . . . . . . . . . . . . . . . 100
6.1.6 Recherche dans une chaı̂ne : la méthode indexOf . . . . . . . . . . . . . . . 100
6.1.7 La comparaison de chaı̂nes . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
Tester l’égalité de deux chaı̂nes . . . . . . . . . . . . . . . . . . . . . . . . . 100
Comparer deux chaı̂nes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6.1.8 Modification d’un caractère . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6.1.9 Extraction de sous-chaı̂nes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6.1.10 Passage en majuscules/minuscules . . . . . . . . . . . . . . . . . . . . . . . 101
6.1.11 Conversion d’une chaı̂ne vers un type primitif . . . . . . . . . . . . . . . . . 101
6.1.12 Conversion d’un type primitif vers une chaı̂ne . . . . . . . . . . . . . . . . . 101
6.1.13 Chaı̂nes et tableaux de caractères . . . . . . . . . . . . . . . . . . . . . . . . 102
6.1.14 Tableau de chaı̂nes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
6 TABLE DES MATIÈRES

6.2 La classe StringBuffer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102


6.3 La classe StringTokenizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

7 Les entrées/sorties 105


7.1 Les fichiers, la classe File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
7.1.1 Les champs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
7.1.2 Les constructeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
7.1.3 Les méthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
7.2 Les flux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
7.3 Lecture/écriture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

8 Quelques éléments de l’API 109


8.1 La classe Math . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
8.2 Les classes enveloppes pour les valeurs primitives . . . . . . . . . . . . . . . . . . . 109
8.3 Les collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
8.3.1 Définition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
8.3.2 ArrayList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
L’ancienne classe Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
8.3.3 Les ensembles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
8.3.4 Les algorithmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
Recherche de maximum ou minimum . . . . . . . . . . . . . . . . . . . . . . 114
Algorithme de tris et mélanges . . . . . . . . . . . . . . . . . . . . . . . . . 115
8.3.5 Les tables associatives (HashMap) . . . . . . . . . . . . . . . . . . . . . . . . 116
8.4 Divers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
8.4.1 Eléments disponibles à partir de Java 1.5 . . . . . . . . . . . . . . . . . . . 119
Boucle for améliorée . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
8.4.2 Généricité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
8.4.3 Réflection et Manipulation des types . . . . . . . . . . . . . . . . . . . . . . 119
8.5 Les autres éléments de l’API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
Chapitre 1

Avant propos

Ce polycopié de cours a pour objectif d’introduire les concepts fondamentaux de la program-


mation objet à l’aide du langage java. Il est composé de 4 chapitres de cours et de 3 chapitres
présentant des éléments de la bibliothèque du langage. A l’issue de chaque chapitre de cours, un
ensemble d’exercices originaux sont proposés. Ces exercices sont complémentaires au exercices de
travaux dirigés et au travaux pratiques. Ils ont pour objectif de faire travailler les notions vues en
cours sous une forme différente et d’insister sur le vocabulaire spécifique à java ou à la program-
mation objets, il est donc fortement conseillé de chercher sérieusement ces exercices. Voici un bref
descriptif des différents chapitres :
— Chapitre 2 : chapitre d’introduction présentant le langage dans sa globalité, son historique
et les principes de bases pour écrire un premier programme. On y détaille tous les types
primitifs, les structures de contrôles et quelques structures fondamentales du langage. On
présente également les conventions à suivre lorsque l’on programme en Java.
— Chapitre 3 : ce chapitre présente la philosophie du langage : l’objet. On détaille les différentes
structures permettant de déclarer et de créer les objets ainsi que les implications de chacune
d’elles.
— Chapitre 4 : on y introduit des concepts fondamentaux de la programmation objet l’héritage
et le polymorphisme. Ils font la force de ce type de programmation en permettant notam-
ment une meilleure lisibilité des programmes et une plus grande facilité de réutilisation de
l’existant.
— Chapitre 5 : dans ce chapitre la gestion d’erreurs via la notion d’exception est présentée.
Ce type de gestion est propre au langage java.
— Chapitre 6 : détail de la plupart des classes de la bibliothèque permettant de gérer des
chaı̂nes de caractères, type fondamental et traiter de manière spécifique en Java.
— Chapitre 7 : présentation des éléments permettant de lire des flux et notamment de lire et
d’écrire dans des fichiers.
— Chapitre 8 : chapitre présentant une partie de la bibliothèque. On parle de l’accès à des
fonctions mathématiques, de l’utilisation de listes ou de tables associatives, de quelques
algorithmes utilisables, ansi que la généricité.

Ce document en est à sa troisième version, il existe de manière certaine des coquilles, des fautes
de frappes, des oublis de mot, des erreurs de code involontaire, des copier/coller non pertinents, . . .
Dans le but d’améliorer ce document, merci de signaler toutes ces erreurs. Si vous avez quelques
suggestions pour améliorer certaines parties, corriger certaines affirmations, elles sont les bienve-
nues.

Bonne lecture et bon travail !

7
8 CHAPITRE 1. AVANT PROPOS
Chapitre 2

Introduction : le langage JAVA

2.1 Présentation
Java est une technologie composée d’un langage de programmation orienté objet et d’un en-
vironnement d’exécution. Préalablement nommé Oak, il a été créé par James Gosling et Patrick
Naughton chez Sun Microsystems avec le soutien de Bill Joy.
Le langage Java fut officiellement présenté le 23 mai 1995 au SunWorld.
Java est à la fois un langage de programmation et une plateforme d’exécution. Le langage Java
a la particularité principale d’être portable sur plusieurs systèmes d’exploitation tels que Windows,
MacOS ou Linux. C’est la plateforme qui garantit la portabilité des applications développées en
Java.
Le langage reprend en grande partie la syntaxe du langage C++, très utilisé par les infor-
maticiens. Néanmoins, Java a été épuré des concepts les plus subtils du C++ et à la fois les
plus déroutants, tels que les pointeurs. Les concepteurs ont privilégié l’approche orientée objet de
sorte qu’en Java, tout est objet à l’exception des primitives (nombres entiers, nombres à virgule
flottante, etc.).
Java permet de développer des applications autonomes mais aussi, et surtout, des applications
client-serveur. Côté client, les applets sont à l’origine de la notoriété du langage. C’est surtout côté
serveur que Java s’est imposé dans le milieu de l’entreprise grâce aux servlets, le pendant serveur
des applets, et plus récemment les JSP (Java Server Pages) qui peuvent se substituer à PHP et
ASP.
Les applications Java peuvent être exécutées sur tous les systèmes d’exploitation pour lesquels a
été développée une plateforme Java, dont le nom technique est JRE (Java Runtime Environment -
Environnement d’exécution Java). Cette dernière est constituée d’une JVM (Java Virtual Machine
- Machine Virtuelle Java), le programme qui interprète le code Java et le convertit en code natif.
Mais le JRE est surtout constitué d’une bibliothèque standard à partir de laquelle doivent être
développés tous les programmes en Java. C’est la garantie de portabilité qui a fait la réussite de
Java dans les architectures client-serveur en facilitant la migration entre serveurs, très difficile
pour les gros systèmes.
Dans le cadre de ce cours notre objectif sera d’étudier les concepts fondamentaux de la pro-
grammation objet à l’aide du langage java. Le but est d’acquérir les bases permettant ensuite de
développer des applications plus consistantes.

2.2 Historique du langage


— Naissance ≡ 1991 : conception d’un langage applicable à de petits appareils électriques (on
parle de code embarqué) par la société Sun Microsystem. La syntaxe est proche du C++,
utilisant le concept de machine virtuelle. Le code source est traduit dans un langage uni-
versel disposant de fonctionnalités communes à toutes les machines. Ce code intermédiaire

9
10 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA

est dit formé de byte code et est compact et portable sur n’importe quelle machine : il suffit
qu’elle dispose d’un programme permettant d’interpréter le langage, on parle de machine
virtuelle. Ce projet s’appelait Oak.
— Eté 1992 : première présentation interne des possibilités de Oak. Un appareil appelé ”Star
Seven” permet de visualiser une animation montrant Duke, l’actuelle mascotte de Java.
— 1994 : développement de HotJava, un navigateur internet entièrement écrit en Java capable
d’exécuter des applets écrites en byte code.
— 1995 : lancement officiel de Java 1.0
— 1996 : lancement du JDK 1.0 et des versions 1.01 et 1.02 du langage JAVA (250 classes
dans la bibliothèque).
— 1998 : version 1.1 du JDK (500 classes).
— 1999 : version 1.2 JDK que l’on appelle Java2 (1500 classes).
— 2000 : version de J2SE (Java 2 Standard Edition) 1.3 (1800 classes).
— 2002 : version de J2SE (Java 2 Standard Edition) 1.4 (2700 classes), applications web et
entreprises.
— 2003 : version de J2EE (Java 2 Entreprise Edition) 1.4.
— 2004 : version du J2SE 1.5 également appelé J2SE 5.0 ou Java 5 (≈ 3500 classes).
— 2006 : version du J2SE 1.6 également appelé J2SE 6.0 ou Java 6 (≈ 3800 classes).
— 2011 : version du J2SE 1.7 également appelé J2SE 7.0 ou Java 7 (≈ 3900 classes).
— 2014 : version du J2SE 1.8 également appelé J2SE 8.0 ou Java 8 (≈ 4000 classes).

2.2.1 Quelques mots clés


— JRE : Java Runtime Environnement logiciel permettant d’exécuter des applications java.
— JDK : Java Development Kit logiciel permettant de concevoir et d’exécuter des applications
java.
— J2SDK : Java 2 Software Development Kit même chose que précédemment.
— API : Application Programming Interface (interface de programmation) qui définit la manière
dont un composant informatique peut communiquer avec un autre. Dans le cas de Java,
ce terme désigne une bibliothèque de classes et de fonctionnalités mises à disposition du
programmeur.
— J2EE : Java 2 Platform, Enterprise Edition Java 2 Platform, Enterprise Edition est une
spécification pour le langage de programmation Java de Sun plus particulièrement destinée
aux applications d’entreprise. Dans ce but, toute implémentation de cette spécification
contient un ensemble d’extension au cadre d’applications Java standard (J2SE, Java 2
standard edition) afin de faciliter la création d’applications réparties. Voici quelques API
présentes dans cette extension : Servlets, JSP, JDBC, JAXB, RMI, . . .
— javac programme contenu dans le JDK pour compiler des programmes java.
— java nom du langage programme contenu dans le JDK ou JRE pour lancer des programmes
java.
— javadoc programme contenu dans le JDK pour créer automatiquement une documentation
HTML à partir de sources java.
— jar programme contenu dans le JDK pour compresser un (ou des programmes java) dans
un seul fichier.

2.3 Java et la programmation orientée objet


La programmation orientée objets se caractérise par 3 points importants :
— une programmation structurés,
— fiabilité des logiciels accrue,
— facilite la réutilisation du code existant.
2.4. STRUCTURE D’UN PROGRAMME JAVA 11

2.3.1 Le concept d’objets


En programmation impérative (par exemple le langage C), un programme est formé de différentes
procédures et structures de données généralement indépendantes des procédures.
En programmation orientée objets, on met en œuvre différents objets. Chaque objet associe
des données et des méthodes agissant exclusivement sur les données de l’objet.
— On parle de méthodes plutôt que de procédures.
— On utilisant indifféremment le mot champ, donnée ou encore attribut.

L’encapsulation de données : il n’est pas possible d’agir directement sur les données d’un objet,
il est nécessaire de passer par des méthodes associées à l’objet. Ces méthodes jouent le rôle d’in-
terface obligatoire. L’appel d’une méthode peut être vu comme l’envoi d’un message à un objet.
Vu de l’extérieur, un objet se caractérise uniquement par ses spécification (données attributs) et
ses méthodes.

La notion de classe :
— elle généralise la notion de type de donnée
— elle permet de décrire un ensemble d’objets ayant une structure de données commune et
disposant de mêmes méthodes.
— Les objets apparaissent comme des variables d’un type de classe donnée, on parle d’instances
de classe.

La notion d’héritage. Elle permet de définir une nouvelle classe à partir d’une autre. On réutilise
cette dernière en bloc et on lui ajoute de nouvelles fonctionnalités. La conception d’une nouvelle
classe, qui hérite (récupère) toutes les propriétés et aptitudes de l’ancienne. Il est ainsi possible
de s’appuyer sur des réalisations antérieures parfaitement au point et les spécifier à volonté. Ceci
facilite donc la réutilisation de code ou de logiciel déjà existant.

La programmation objet facilite énormément la réalisation d’interfaces graphiques. Chaque


élément de l’interface est vu comme un objet, par exemple un objet fenêtre, plusieurs objets bou-
tons, un objet ascenseur, etc.

2.3.2 Indépendance vis à vis de la plateforme


La portabilité du code. Un programme est portable si un même code source peut être exploité
dans des environnements différents moyennant une nouvelle compilation. En Java, il existe la
notion de machine virtuelle java - la JVM (pour Java Virtual Machine) - qui peut exécuter le
même code sur n’importe quelle plateforme en utilisant un code source sous forme de bytecode : un
langage machine spécifique à la plateforme Java. La création d’un programme Java s’effectue en
2 temps : une phase de compilation qui transforme le code source en bytecode. Le code est ensuite
interprété sur une machine virtuelle Java

2.4 Structure d’un programme JAVA


2.4.1 Que contient un fichier source ?
Un fichier source porte l’extension .java et contient la définition de classe. La définition d’une
classe est définie par 2 accolades.
public class Chien{

//code de la classe JAVA

}
12 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA

Une classe contient une ou plusieurs méthodes. Par exemple dans la classe Chien, la méthode
aboyer() va contenir des instructions spécifiant la façon dont un chien aboie. Les méthodes sont
obligatoirement déclarées dans une classe.
public class Chien{

void aboyer()
{
// code de la methode aboyer où l’on indique comment
// la méthode doit ^
etre exécutée
}

2.4.2 Execution d’un programme Java

Source Compilation Execution


On compile le(s) fichier(s)
source(s) avec le programme L’exécution est lancée en uti-
javac, par exemple javac lisant le programme java
On crée un document source
Toto.java. Si la compilation avec le nom du fichier princi-
avec une extension .java, par
a réussi, un fichier .class pal du programme sans l’ex-
exemple Toto.java
Toto.class est créé, ce fi- tension .class. Par exemple
chier est constitué de byte java Toto.
code

2.4.3 La machine virtuelle


Un programme s’exécute en lançant la JVM sur le fichier principal (Toto.class). Ce fichier
contient du byte code et la JVM traduit ce byte code en code machine compréhensible par la
plateforme sous-jacente et exécute le programme.
Lorsque la JVM démarre elle cherche la classe spécifiée à l’exécution, puis elle recherche une
méthode spéciale – la méthode main – qui ressemble exactement à ceci :
public static void main(String [] args)
{
//le code de la methode main
}
La JVM exécute ensuite tout ce qui se trouve entre les accolades{ } de la méthode main.

2.4.4 La méthode main


Toute application java a au moins une méthode main. Attention, pas une par classe, mais au
moins une par application. La méthode main correspond à l’endroit où le programme commence
à s’exécuter.
Voici un exemple de programme complet :
public class PremiereAppli{
public static void main(String [] args)
{
System.out.println("Bonjour à tous");
System.out.println("Je m’appelle toto");
}
}
2.4. STRUCTURE D’UN PROGRAMME JAVA 13

1. Enregistrement dans le fichier PremiereAppli.java


2. Compilation javac PremiereAppli.java
3. Lancement du programme java PremiereAppli
4. Le résultat du programme consiste à afficher les 2 lignes suivantes :
Bonjour à tous
Je m’appelle toto

2.4.5 Utilisation d’arguments dans la fonction main


Les arguments de la méthode main sont placés dans le tableau args, qui est un tableau constitué
de chaı̂nes.
Lors du lancement d’un programme, les arguments se placent à la suite de l’identificateur de
classe du programme lancé et sont séparés par un espace. Exemple :
java AfficheArguments Le langage Java
Voici le programme correspondant :
public class AfficheArguments{
public static void main(String [] args)
{
for(int i=0;i<args.length;i++)
{
System.out.println("contenu de args[" + i +"] : "+ args[i]);
}
}
}
A l’exécution, ce programme affiche le résultat suivant :
contenu de args[0] : Le
contenu de args[1] : langage
contenu de args[2] : Java
N.B. Les arguments sont passés sous la forme de chaı̂nes de caractères, si nous voulons des
nombres nous devons convertir les arguments.

Exemple.
public class Addition2Entiers{
public static void main(String [] args)
{
int entier1, entier2, resultat;

entier1=Integer.parseInt(args[0]);
entier2=Integer.parseInt(args[1]);

resultat = entier1 + entier2;

System.out.println("La somme de "+entier1+" et "+entier2+" est : "+resultat);


}
}
Pour lancer le programme : java Addition2Entiers 1 3.
Autre exemple.
14 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA

public class ConversionFrancsEuros{


public static void main(String [] args)
{
final float taux = 6.55957f;

float somme = 0f, resultat;

somme=Float.parseFloat(args[0]);
resultat = somme / taux;

System.out.println("Le resultat de la convertion de "+somme+"


francs est "+resultat+" euros.");
}
}

2.5 Installation et utilisation de Java


Pour installer un outil de développement java, il suffit de récupérer un outil de développement
(JDK ou SDK) sur http://www.oracle.com/technetwork/java/javase/downloads/index.html,
puis de lancer le programme d’installation. Sous linux vous pouvez l’installer par exemple dans
/usr/local pour le rendre accessible à tout le monde, ou sous votre compte.
Ensuite nous allons principalement utiliser les programmes situés dans le répertoire bin -
/Chemin Installation/jdk1.8.0 10/bin (/Chemin Installation correspondant au répertoire
d’installation de java), par exemple si vous avez récupéré la version 10 du jdk 1.8 - javac, java,
javadoc, jar, . . .). Pour faciliter leur utilisation vous pouvez mettre le répertoire bin dans votre
variable d’environnement PATH (cf le cours système). Notez que sous certains systèmes linux/unix,
certaines distributions java sont pré-installées, mais ne correspondent pas aux versions officielles
fournies par Sun Microsystems.
Pour écrire nos programmes nous aurons seulement besoin d’un éditeur (par exemple emacs
sous linux, ou tout un autre éditeur si vous le souhaitez) et d’un terminal (xterm).
Vous pourrez éventuellement avoir de besoin de configurer une variable d’environnement CLASSPATH
qui indique les répertoires où sont situés les programmes java. Ce n’est pas obligatoire, par défaut
les programmes sont cherchés dans le répertoire courant et il est possible d’indiquer des répertoires
en spécifiant des options aux différents programmes disponibles dans le jdk (voire la documentation
officielle indiquée dans les liens ci-dessous).
Quand vous serez plus expérimenté (par exemple à partir du 4ème TP), vous pourrez essayé
d’autres éditeurs facilitant le développement de programmes java tels qu’eclipse ou NetBeans. A
vous de les essayer et éventuellement d’en adopter un. Néanmoins, il est d’abord conseillé d’utiliser
un éditeur non spécifique pour commencer, puis de passer à un éditeur spécialisé une fois que l’on
maı̂trise les bases du langage.

Pour ceux qui souhaitent essayer leurs programmes sous windows, la procédure d’installation est
la même. Il peut cependant être nécessaire de créer une variable d’environnement supplémentaire.
Voici la procédure à suivre.
— Vérifier que la variable d’environnement JAVA HOME est spécifiée.
— Si ce n’est pas le cas allez dans Menu Démarrer → Settings → Control Panel → System
→ Advanced, menu variables d’environnement. Entrez le nom de JAVA HOME puis sa valeur
(par exemple C:2sdk1.8.0 10/).
— Pour vérifier tapez echo %JAVA HOME dans une console MS-DOS.
Vous pouvez utiliser la même méthode pour modifier le contenu de la variable PATH ou confi-
gurer CLASSPATH. En guise de terminal vous utiliserez alors une console MS-DOS.
2.6. TYPES, EXPRESSIONS ET STRUCTURES DE CONTRÔLE FONDAMENTALES 15

2.6 Types, expressions et structures de contrôle fondamen-


tales
Cette partie a pour objectif de présenter rapidement l’utilisation des types, des expressions
et des instructions de contrôle en JAVA. Les éléments abordés ne sont pas forcément toujours
caractéristiques du langage JAVA mais sont nécessaires pour l’utilisation de n’importe quel lan-
gage de programmation. Lorsque certaines situations sont spécifiques au langage JAVA, nous le
précisons.

2.6.1 les types primitifs


Ils servent à définir les champs (attributs) de toutes les classes que vous pourrez définir, ainsi
que les variables que vous pourrez utiliser dans les différentes méthodes. Attention, les types
primitifs ne sont pas des classes.

La notion de type

La mémoire centrale est un ensemble de ” positions binaires ” appelées. Ces bits sont regroupés
en octets, un octet correspondant à 8 bits. Les ordinateurs (actuels) ne savent représenter et traiter
que des informations représentées sous forme binaire. Il est donc nécessaire de savoir comment une
information a été codée pour pouvoir attribuer une signification à une suite de bits d’un emplace-
ment de la mémoire.

Il existe 4 catégories de type primitif en JAVA :


— nombres entiers
— nombres flottants
— caractères
— booléens

le type entier (int)

Il sert à représenter les nombres entiers relatifs. Il existe quatre sortes d’entier permettant de
représenter des valeurs différentes. Le tableau suivant décrit chacun de ces types avec le nom du
type, la taille occupée en mémoire en octets, la valeur minimale et la valeur maximale possible
avec la constante correspondante dans le langage.

Nom Taille Valeur minimale Valeur maximale


byte 1 -128 127
Byte.MIN VALUE Byte.MAX VALUE
short 2 -32768 32767
Short.MIN VALUE Short.MAX VALUE
int 4 -2,147,483,648 2,147,483,647
Integer.MIN VALUE Integer.MAX VALUE
long 8 -9,223,372,036,854,775,808 9,223,372,036,854,775,807
Long.MIN VALUE Long.MAX VALUE

Les constantes peuvent s’écrire sous forme décimale (548, -78, +5), ou sous forme hexadécimale
en précisant la valeur de 0x ou 0x (0x1A 0X12), ou octale en faisant précéder la valeur de 0 (032,
05).
Une constante entière est forcément de l’un des types int ou long. On peut forcer une constante
à être du type long en faisant suivre sa valeur de la lettre l ou L, exemple 25L. Le compilateur
rejettera toute constante ayant une valeur supérieure la capacité du type long.
16 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA

Les flottants/réels (float ou double)

Il en existe deux sortes. Le tableau suivant décrit chacun d’eux avec leur nom, la taille occupée
en mémoire en octets, la précision correspondant au nombre de chiffres significatifs pris en compte
dans la partie décimale, la valeur minimale et la valeur maximale possible avec la constante
correspondante dans le langage.

Nom Taille Précision Valeur absolue minimale Valeur absolue maximale


float 4 7 −1.40239046 × 10−45 3, 40282347 × 1038
Float.MIN VALUE Float.MAX VALUE
double 8 15 4.9506564584124654 × 10− 324 1, 797693134862316 × 10308
Double.MIN VALUE Double.MAX VALUE

Pour les constantes, il existe deux notations :


— la notation décimale : 12.43 -0.38 -.38 4. .27
— la notation exponentielle : 4.25E4 ou 4.25e+4 pour 4.25 × 104
54.2e-32 pour 54.2 × 10−32
48e13 pour 48 × 1013
Par défaut toutes les constantes créées sont de type double, pour forcer à avoir un type float
on fait suivre la valeur de la lettre f, exemple : 12.5f

Le type caractère (char)

Les caractères, désignés par le type char, sont codés sur 2 octets, notation d’une constante
caractère : ’a’, ’B’, ’é’, ’+’. Exemple
char lettre=’a’;
Certains caractères avec notation spéciale :
— ’\b’ pour le retour arrière (backspace)
— ’\t’ pour une tabulation
— ’\n’ pour un saut de ligne
— ’\f’ pour un saut de page
— ’\r’ pour un retour chariot
— ’\"’ pour un guillemet
— ’\’’ pour une apostrophe
— ’\\’ pour \.

Le type booléen (boolean)

Il sert à représenter une valeur logique de type vrai ou faux, il existe deux valeurs possibles pour
un booléen en java : true et false (pour vrai et faux). Exemple boolean est ordonne=false;.
Attention, en JAVA, les valeurs logiques ne sont pas représentées par des valeurs mais par un
type spécifique : le type boolean, toute utilisation de valeur numérique dans des tests provoquera
une erreur !

Initialisations et constantes

Variables Exemples : int n=15; également équivalent à int n; n=15;.


En JAVA, les déclarations peuvent apparaı̂tre à n’importe quel endroit du programme. D’un
point de vue méthodologique, il est souvent conseillé d’effectuer la déclaration de toutes les
variables (importantes) ensembles au début puis de les initialiser avant de les utiliser.
N.B. Les variables n’ayant pas reçu de valeur ne peuvent pas être utilisées sous peine d’aboutir
à une erreur de compilation. Il est donc important de penser à initialiser les variables dès leur
création.
2.6. TYPES, EXPRESSIONS ET STRUCTURES DE CONTRÔLE FONDAMENTALES 17

Constantes : le mot clé final Pour indiquer qu’une variable ne peut pas être modifiée pendant
l’exécution d’un programme, on peut utiliser le mot clé final :
final int nombre de mois=12;
Toute modification de la variable nombre de mois, de valeur initiale 12, sera rejetée par le compi-
lateur.

2.6.2 Opérateurs et expressions


Opérateurs arithmétiques
Il existe cinq opérateurs principaux :
— + : addition a+b
— - : soustraction a-b
— / : division a/b
— * : multiplication a*b
— % : modulo, a % b calcule le reste de la division de a par b.
Le problème de la conversion de type :
float x; float res;
int n; int p;
res=n*x+p;
La variable n étant de type int, x de type float, le compilateur va convertir n en float puis faire le
calcul. Le résultat final sera de type float, comme le montre la Figure 2.1.
n * x + p

int float long

float
*

float + float

float

Figure 2.1 – Evaluation de res=n*x+p;

La hiérarchie de conversion permettant de ne pas dénaturer la valeur initiale : int → long


→ float → double.
Note : par défaut, pour les types byte, char, short, si un de ces types apparaı̂t dans une expression,
la valeur est d’abord convertie en int. Exemple :
short p1=1, p2=1 ,p3=1;
float x=2;
L’évaluation de p1*p2+p3*x est faite comme le montre la Figure 2.2.
p1 * p2 + p3 * x

int int int

* float * float

int float
float +

float

Figure 2.2 – Evaluation de p1*p2+p3*x


18 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA

Opérateurs de comparaison
Ils servent à faire des comparaisons relatives et sont principalement utilisés dans les tests
conditionnels.
— < : inférieur strictement à
— <= : inférieur ou égal à
— > : supérieur à
— >= : supérieur ou égal à
— == : égal à
— ! = : différent de.
Ces opérateurs peuvent s’utiliser avec des expressions : 2 ∗ a > b + 5, x + y < (a + 2) ∗ 5, . . .
Note : ordre sur les caractères 0 00 <0 10 < . . . <0 90 <0 A0 <0 B 0 < . . . <0 Z 0 <0 a0 < . . . <0 z 0 .

Opérateurs logiques
Ces opérateurs permettent de manipuler des valeurs logiques.
— ! : négation
— & : ”ET”
— ∧ : ”OU” exclusif
— | : ”OU” inclusif
— && : ”ET” avec court-circuit
— || : ”OU” inclusif avec court-circuit
Exemples :
— (a < b) && (c < d), (a < b) & (c < d) : ces deux expressions prennent la valeur true (vrai)
si les deux expressions a < b et c < d sont toutes les deux vraies, la valeur false (faux) dans
le cas contraire.
— (a < b) || (c < d), (a < b) | (c < d) : prend la valeur true si l’une au moins des deux
conditions a < b ou c < d est vraie, la valeur false dans le cas contraire.
— (a < b) ∧ (c < d) prend la valeur true si une et une seule des deux conditions a < b et
c < d est vraie, la valeur false dans le cas contraire.
— !(a < b) prend la valeur true si la condition a < b est fausse, la valeur false dans le cas
contraire. Cette expression possède en fait la même valeur que a >= b.
— Les deux opérateurs && et || possèdent une propriété intéressante : leur second opérande
(celui qui figure à droite de l’opérateur) n’est évalué que si la connaissance de sa valeur
est indispensable pour décider si l’expression est vraie ou fausse. Par exemple, si on a
l’expression (a < b)&&(c < d), on commence par évaluer (a < b), si le résultat est faux
on n’évalue pas c < d puisque le résultat est déjà connu. Les opérateurs ∧ et | évaluent
toujours les deux opérandes, il est donc plutôt conseillé d’utiliser les opérateurs && et ||.

Opérateurs d’affectation
— = : exemples c=b+3; c=i;. L’opérateur possède une associativité de gauche à droite, ainsi
dans l’expression i = j = 5; on évalue j=5 d’abord, puis i=j, i et j ont à la fin la même
valeur 5.
Attention aux problèmes de conversion, supposons que nous ayons une variable de type
int n et une variable de type float x, l’expression n=x+5; est rejetée par le compilateur.
Il faut en fait écrire n=(int) x + 5;. Il est nécessaire de faire une conversion explicite.
Les conversions pour lesquelles il n’y a pas besoin de faire une conversion explicite sont les
suivantes :
— byte → short → int → long → float → double

— char → int → long → float → double


— L’incrémentation et la décrémentation. Ces opérations consistent respectivement à aug-
menter une variable (en général entière) 1 et à diminuer une variable de 1 (i = i-1; n =
n+1;). Ces opérations sont définies par les opérateurs ++ et -- : i--; n++; ou --i; ++n;.
2.6. TYPES, EXPRESSIONS ET STRUCTURES DE CONTRÔLE FONDAMENTALES 19

Il existe cependant un différence suivant où est placé l’opérateur lors de l’évaluation d’une
expression, s’il est placé avant(on parle d’opérateur préfixé) la variable l’opération est ef-
fectuée avant l’évaluation de l’expression, s’il est placé après (on parle d’opérateur postfixé)
l’opération est effectuée après.
— n= ++i -5; : on affecte d’abord la valeur 6 à i puis la valeur 1 à n.
— n= i++ - 5; : on affecte d’abord la valeur 0 à n puis la valeur 6 à i.
— Affectation élargie, les instructions suivantes sont équivalentes :
— i = i + k; et i+=k;
Il existe la même chose avec les opérateurs *, / et -.

Conversion de types
Lorsque l’on désire convertir un type vers autre qui a une taille de représentation inférieure,
les règles suivantes s’appliquent :
— entier vers entier (long vers int, short vert byte, . . .) les octets les moins significatifs sont
conservés.
— double vers float : arrondi au plus proche.
— flottant vers entier : il y a d’abord un arrondi au plus proche dans le type long ou int, puis
on effectue la conversion en conservant les octets les moins significatifs.

Opérateur conditionnel
condition ? etape1 : etape2; : si condition est vraie alors etape1 sinon etape2. Exemple :
max = a<b ? b :a;
Si a < b, alors la variable max reçoit la valeur de la variable b, sinon elle reçoit la valeur de la
variable a.

Priorité des opérateurs


Opérateurs associativité
() [] . ++(postfixé) –(postfixé) g à d
+(unaire) -(unaire) ++(préfixé) –(préfixé) (unaire) ! cast new d à g
/% g à d
+- g à d
<< >> >>> g à d
< <= > >= instanceof g à d
== != g à d
& g à d
∧ g à d
— g à d
&& g à d
— g à d
?: g à d
= += -= *= /= %= <<= >>= >>>= & = | = ∧ = d à g

2.6.3 Les structures de contrôle


Dans cette partie, les crochets [] signifient que ce qu’ils renferment est facultatif. Il servent soit
à exprimer le fait que pour lier plusieurs instructions à une structure de contrôle il est nécessaire
de mettre ces instructions dans un bloc entre accolades ({}) (si on n’a besoin que d’une seule
instruction on n’a pas besoin de bloc) ; soit à indiquer qu’une instruction facultative.
Le terme condition correspond à une expression qui peut être évaluée sous forme logique (vrai
ou faux).
20 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA

if
L’instruction if (si) est une instruction de choix.

if(condition)
[{]
instruction\_1
[instruction\_2
...
instruction\_n
}]
[else [{]
instruction\_1
[instruction\_2
...
instruction\_n
}]

Exemple :

public classTva {
public static void main(String [] args)
{
double taux_tva=21.6;
double ht, ttc, net, taux_remise, remise;

ht=200.5;
ttc=ht * (1.0 + taux_tva/100.0);
if(ttc < 1000.0)
taux_remise=0.;
else if(ttc < 2000)
taux_remise=1.;
else if(ttc < 5000){
taux_remise=2.;
System.out.println("Message: Prix ttc entre 2000 et 5000");
}else{
taux_remise=5.;
System.out.println("Message: Prix ttc superieur a 5000");
}

remise = ttc * taux_remise/100;


net = ttc - remise;

System.out.println("Prix ttc: "+ttc);


System.out.println("Remise: "+remise);
System.out.println("Net a payer: "+net);
}
}

switch
L’instruction switch (branchement) est une instruction de choix, permettant de tester plusieurs
valeurs d’une expression. L’expression peut être de type byte, short, char ou int.
Syntaxe :
2.6. TYPES, EXPRESSIONS ET STRUCTURES DE CONTRÔLE FONDAMENTALES 21

switch(expression)
{
case constante_1: [suite d’instructions1]
case constante_2: [suite d’instructions2]

case constante_n: [suite d’instructionsn]


[default: suite d’instructions]
}
Exemple :
public class ExSwitch{
public static void main(String [] args)
{
int n;

n=Integer.parseInt(args[0]);

switch(n)
{
case 0: System.out.println("zero");
break;

case 1: System.out.println("un");
break;

case 3: System.out.println("trois");

default: System.out.println("Rien");
System.out.println("Fin");
}
System.out.println("Fin du programme");
}
}

while
Il s’agit d’une boucle tant que qui exécute un ensemble d’instructions tant qu’une condition
est vraie.
Syntaxe :
while(condition)
[{]
instruction_1
[instruction_2
...
instruction_n
}]
Exemple
public class Boucle{
public static void main(String [] args)
{
int x=1;
System.out.println("Avant la boucle");
22 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA

while(x<4)
{
System.out.println("Dans la boucle, la valeur de x est "+x);
x=x+1;
}
System.out.println("Après la boucle");
}
}

do-while
Il s’agit d’une boucle faire-tant que similaire à la boucle while sauf que la condition est évaluée
après chaque parcours de boucle. La boucle do-while est exécutée au moins une fois, alors que la
boucle while peut ne jamais être exécutée.
Syntaxe :
do [{]
instruction_1
[instruction_2
...
instruction_n }] while(condition);
N.B. Il y a un point virgule à la fin de l’instruction !
Exemple :
public class Boucle2{
public static void main (String [] args){
int x=1;
System.out.println("Avant la boucle");
do{
System.out.println("Dans la boule, la valeur de x est "+x);
x=x+1;
}while(x<4);

System.out.println("Après la boucle");
}
}

for
L’instruction for est une boucle (pour) dont la syntaxe est divisée en trois expressions.
Syntaxe :
for([initialisation] ; [condition] ; [incrémentation])
[{]
instruction_1
[instruction_2
...
instruction_n
}]
— initialisation est une déclaration ou une suite d’expressions quelconques séparées par
des virgules, cette partie est évaluée une seule fois avant d’entrer dans la boucle.
— condition est une expression booléenne (logique) quelconque, cette partie conditionne la
poursuite de la boucle et est évaluée avant chaque parcours.
— incrémentation est une suite d’expressions quelconques séparées par des virgules, cette
partie est évaluée à la fin de chaque parcours.
2.6. TYPES, EXPRESSIONS ET STRUCTURES DE CONTRÔLE FONDAMENTALES 23

Exemple classique :

public class ExFor1


{
public static void main (String args [])
{
int i;
for(i=1; i<=5; i++)
{
System.out.println("bonjour");
System.out.println(i + "fois");
}
}
}

Remarque : l’exemple précédent est équivalent à

public class ExFor2


{
public static void main (String args [])
{
int i;

i=1;
while(i<=5)
{
System.out.println("bonjour");
System.out.println(i+"fois");
i++;
}
}
}

Autre exemple :

public class ExFor3


{
public static void main(String args [])
{
int i,j;
for(i=1, j=3;i<=5; i++, j+=i)
{
System.out.println("i= " +i+ "j= " +j);
}
}
}

Ce dernier exemple pourrait également être écrit de la manière suivante :

public class ExFor4


{
public static void main(String args [])
{
for(int i=1, j=3;i<=5; i++, j+=i)
{
System.out.println("i= " +i+ "j= " +j);
24 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA

}
}
}

Les instructions break et continue


Ces instructions s’emploient principalement au sein de boucles.
L’instruction break (casser) sert à interrompre le déroulement d’une boucle en passant à
l’instruction située après la boucle. L’exécution de cette instruction est conditionnée par un choix.
Exemple :
public class ExBreak
{
public static void main (String args [])
{
int i;
for(i=1;i<=10;i++)
{
System.out.println("debut tour"+i);
System.out.println("bonjour");

if(i==3) break;

System.out.println("fin tour"+i);
}
System.out.println("apres ma boucle")
}
}
Le résultat du programme précédent est le suivant :
debut tour 1
bonjour
fin tour 1
debut tour
bonjour
fin tour 2
debut tour 3
bonjour
apres la boucle
En cas de boucles imbriquées, l’instruction break fait sortir de la boucle la plus interne.
L’instruction continue permet de passer directement au tour de boucle suivant. Exemple.
public class ExContinue
{
public static void main (String args[])
{
int i;
for(i=1; i<=5; i++)
{
System.out.println("debut tour"+i);
if (i<4) continue;
System.out.println("fin tour"+i);
}
System.out.println("apres la boucle");
2.7. UNE PREMIÈRE INTRODUCTION AU TYPE STRING 25

}
}

Exemple d’exécution :

debut tour 1
debut tour 2
debut tour 3
debut tour 4
fin tour 4
debut tour 5
fin tour 5
apres la boucle

2.6.4 Les éléments spécifiques au langage JAVA


Parmi les points abordés ci-dessus, quatre sont plutôt spécifiques au langage JAVA.
— Le mot clé final qui permet de déclarer des constantes à l’aide de variables.
— Le type byte qui permet de représenter des entiers entre -128 et 127.
— Le type boolean pour représenter les valeurs true (vrai) et false (faux). Tous les tests
conditionnels doivent être compatibles avec le type boolean. Mettre une valeur entière dans
un test provoque une erreur de compilation en JAVA (par exemple if(1)... est interdit
en JAVA).
— Les conversions de type doivent être explicites en JAVA chaque que l’on veut convertir un
type général vers une représentation plus petite. Exemple :
int n=2;
float x=1.0;
...
n=(int) x;
Un autre élément est traité de manière spécifique en JAVA : le type chaı̂ne de caractères
(String). Une introduction est proposée dans la section suivante.

2.7 Une première introduction au type String


En JAVA, les chaı̂nes de caractères sont définies par un type spécifique : le type String. Nous
présentons ici une courte introduction, nous reviendrons sur ce type plus tard.

Déclaration

String chaineSalutation = "bonjour";

Une chaı̂ne de caractère constante se déclare toujours entre guillemets "... ".

Connaı̂tre la longueur d’une chaı̂ne

int l = chaineSalutation.length();

Accès à un caractère

char cara1 = chaineSalutation.charAt(0);


char cara1 = chaineSalutation.charAt(2);

La variable cara1 contient le caractère b, la variable cara2 contient le caractère n.


26 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA

Concaténation : l’opérateur +

String ch1 = "Bonjour";


String ch2 = " à tous";
String ch = ch1 + ch2;

La variable ch contient la chaı̂ne ”Bonjour à tous”.

Impression d’une chaı̂ne de caractères

System.out.println(chaineSalutation);
System.out.println(ch1+ch2);
System.out.println(ch);

Comparaison de chaı̂nes La méthode equals qui teste l’égalité de deux chaı̂nes de caractères :
ch1.equals(ch2) ou ch1.equals("Bonjour").
La méthode compareTo pour comparer deux chaı̂nes de caractères dans l’ordre lexicographique
(alphabétique) : ch1.compareTo(ch2)
— renvoie un entier strictement négatif si ch1 est située avant ch2 dans l’ordre lexicographique
— renvoie un entier strictement positif si ch1 est située après ch2 dans l’ordre lexicographique
— 0 si ch1 contient la même chaı̂ne que ch2.

2.8 Règles d’écriture d’un programme JAVA


Ce document a pour objectif de synthétiser les règles d’écriture généralement utilisées pour le
développement d’applications en JAVA.

2.8.1 Les identificateurs


Les identificateurs sont des suites de caractères servant à désigner les entités manipulées par
un programme (variables, méthodes, classes, objets, . . .). En JAVA, un identificateur est formé de
lettres et de chiffres ou du caractère . Le premier caractère est forcément une lettre A-Z, a-z ou
éventuellement . Il n’existe aucune limitation sur le nombre de caractères. Exemples :
ligne, valeur 5, total, 56, ma variable 1, i, MaClasse, ...
Attention, on distingue les minuscules des majuscules (ligne 6= Ligne).

Certaines conventions sont traditionnellement utilisées :


— Les noms de variables et de méthodes sont écrits en minuscule sauf s’ils sont formés de la
juxtaposition de plusieurs mots, auquel cas chaque sous-mot, sauf le premier, comporte une
majuscule à la première lettre. Exemples : valeur, nombreValeur, tauxEmprunt,
calculNombreReponsesExactes, getX2, ...
— Les noms de classe suivent la même règle, mais leur première lettre est écrite en majuscule.
Exemples : PremierProgramme, Clavier,
CalculMoyenne, ...
Remarque : cette convention permet de distinguer le fait que System est une classe et que
out n’en est pas une (System.out.println).
— Des identificateurs successifs doivent être séparés par un espace ou un saut de ligne quand
il n’y a pas de séparateur ou de syntaxe particulière. Exemple : x = a + 5; y += 4; Une
virgule est utilisée pour faire plusieurs déclarations d’un même type. Exemple :
int x, y;
float n, compteur, total, valeur;
2.8. RÈGLES D’ÉCRITURE D’UN PROGRAMME JAVA 27

2.8.2 Commentaires
Il existe trois types de commentaires en JAVA.
— les commentaires commençant par /∗ et se terminant par ∗/, exemple : /* Ceci est un
commentaire usuel*/
— les commentaires de fin de ligne //, exemple :
int valeur; // valeur avec commentaire de fin de ligne
— les commentaires de documentation commençant par / ∗ ∗ et finissant par ∗/. C’est un cas
particulier des commentaires usuels puisqu’ils commencent de manière légèrement différente
par rapport à ces derniers. Leur intérêt est de pouvoir être extrait automatiquement pour
faire de la documentation avec, par exemple, le programme javadoc.

2.8.3 Mots clés réservés


Les mots suivant sont des mots clés réservés pour le langage et ne peuvent être utilisés

abstract boolean break byte case


catch char class const continue
default do double else extends
final finally float for goto
if implements import instanceof int
interface long native new package
private protected public return short
static super switch synchronized this
throw throws transient try void
volatile while

2.8.4 Remarques globales


— Les instructions se terminent par un point virgule ;.
— Les blocs de code sont définis entre accolades {}.
— On déclare une variable avec un nom et un type : int x;.
— Un opérateur d’affectation est avec un seul signe : =.
— Un opérateur d’égalité est avec deux signes : ==.
— Lorsque l’on écrit du code, il est impératif de respecter les règles d’indentation : les ins-
tructions à l’intérieur d’un bloc doivent être décalées à droite, c’est également lorsque l’on
utilise une structure de contrôle. La définition de méthodes et d’attributs de classes doivent
être indentés par rapport à la définition de la classe elle-même.
— Pensez à commenter votre code et à bien décrire ce que font vos fonctions et à quoi servent
vos classe !
— ...

2.8.5 Commentaires sur le rendu d’un programme Java


Pour rendre un projet vous devez construire une archive (zip, tar.gz) contenant les différents
éléments de votre projet. Une archive correctement constituée devrait contenir :
— une bonne documentation comprenant
— un Fichier README contenant des informations pour lancer le projet, l’installer, les pré-
requis éventuels et qui documente le contenu de l’archive (liste des fichiers et différents
répertoires et ce qu’il y a dedans).
— un manuel utilisateur indiquant comment utiliser le logiciel
— un manuel technique sur la conception du projet (notamment à destination des développeurs)
contenant - par exemple - des schémas UML (au moins une description du code consti-
tuant le projet), les points à améliorer, les bugs, les difficultés, les choix .... Pour des
28 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA

projets java, la documentation technique peut idéalement être complétée par une java-
doc (important pour continuer le développement plus tard).
— une architecture correcte : idéalement à la racine on a le README et que des sous-
répertoires contenant la documentation, le code exécutable, le code source, les drivers,
plus d’autres choses tout le temps dans un sous-répertoire spécifique et documenté dans
le README. Les fichiers sources sont séparés des exécutables, soit dans 2 répertoires
différents, soit - idéalement - dans un répertoire contenance uniquement les sources et les
exécutables sont stockés dans une archive .jar On mettra dans des répertoires spécifiques
des fichiers de log, de sauvegarde, les images, ...
— Un code de bonne qualité. Le code doit respecter les conventions de nommage java, avoir
des noms de variables explicites, être indenté correctement, et bien commenté. On peut
distinguer des commentaires généraux décrivant le contenu d’une classe et son objectif,
et des commentaires décrivant une méthode et ce qu’elle fait (on les retrouve dans la
javadoc). Ensuite des commentaires internes qui se situent à l’intérieur des méthodes pour
expliquer des des attributs d’une classe, des variables de méthode , des algorithmes, des
choses techniques .... Le code importé d’ailleurs doit être signalé dans la documention
technique par exemple et la provenance doit être indiquée. On peut même signaler pourquoi
on a eu besoin de ce code.
— L’archive DOIT être nettoyée et ne pas posséder de fichiers inutiles pour l’utilisateur
(fichiers de log, de sauvegardes, de tests, inutilisés, ...)
Ces remarques sont d’ordre générales mais il est pertinent d’essayer de les respecter. Le nom
de l’archive doit correspondre au nom du projet, lorsque c’est un travail à rendre à un enseignant
il est de bon ton d’ajouter le nom de la personne envoyant le projet. En général, lorsque que l’on
décompresse l’archive, celle-ci doit créer un dossier contenant tous les éléments.

Lorsque que vous envoyez une archive par courrier électronique, il est parfois conseillé d’indi-
quer dans le message le nom de l’archive envoyée (sa taille notamment si elle est volumineuse).
Il est également judicieux de commencer votre message par une formule de salutation (du genre
bonjour) et de terminer par une formule de politesse (Bonne réception, cordialement, ...) et veillez
à ne pas oublier de mettre votre nom à la fin du message ! Pensez également à mettre un sujet
pertinent au message et à vous mettre en copie (cc) notamment lorsque ce sont des projets notés
à rendre.

Je vous conseille aussi pour votre activité professionnelle, dans la mesure du possible, d’utiliser
une adresse électronique qui ne soit pas trop fantaisiste.

2.8.6 Liens sur la programmation JAVA


Voici quelques liens qui pourront vous permettre de compléter vos connaissances en JAVA.
Il existe de nombreux liens, en voici une liste non exhaustive pour commencer vos bookmarks
personnels.
— http://www.oracle.com/technetwork/java/api-141528.html : informations sur les api
standards de java, par versions.
— http://www.oracle.com/technetwork/articles/java/index-141532.html documenta-
tion sur les différentes versions de java.
— http://ibiblio.org/javafaq : des nouvelles quotidiennes sur java, un cours en HTML,
de nombreux liens
— http://www.java-channel.org : site modifié quotidiennement, contenant des liens vers
d’autres sites.
— http://www.javaworld.com : nouvelles et articles techniques
— http://java.developpez.com/faq/java/ : FAQ en français
— http://www.eclipse.org : un environnement de développement (IDE - Integrated Deve-
lopment Environment) qui facilite l’écriture de programmes java.
2.9. EXERCICES DE COURS 29

2.8.7 Bibliographie
Voici quelques références bibliographiques, ce cours est inspiré de certaines d’entre elles, ce
sont des choix non exhaustifs et subjectifs.
— Programmer en Java. Claude Delannoy. Editions Eyrolles.
— Java Tête la première. Kathy Sierra et Bert Bates. Editions O’Reilly.
— Java in a nutshell - manuel de référence. David Flanagan. Editions O’Reilly.
— The Java Programming Language Second Edition. Ken Arnold, James Gosling. Addison
Wesley, 1998.
— Java par la Pratique. Patrick Niemeyer, Joshua Peck. O’Reilly.

2.9 Exercices de cours


2.9.1 Compilateur
Voici le contenu de trois fichiers JAVA, indiquez pour chacun d’eux si ces fichiers peuvent
se compiler ou dans le cas contraire indiquez comment les corriger. Dites finalement ce que font
chacun de ces programmes.
1. public class Exercice1b{
public static void main(String [] args)
{
int x=1;
while(x<10){
if(x>3)
{
System.out.println("grand x");
}
}
}
}
2. public static void main (String [] args)
{
int x=5;
while(x>1)
{
x=x-1;
if(x<3)
{
System.out.println("petit x");
}
}
}
3. public class Exercice1b{
int x=5;
while(x>1)
{
x=x-1;
if(x<3){
System.out.println("petit x");
}
}
}
Solution :
30 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA

1. Ce fichier compile et s’exécute, mais il faut ajouter une ligne au programme dans la boucle
while sinon il boucle sans fin. On peut, par exemple, ajouter x = x + 1; juste après le
while et avant le if.
2. Ce fichier ne compile pas, il manque une déclaration de classe pour mettre la méthode main
dedans, par exemple public class Test{ et il faudra rajouter une accolade } à la fin.
3. Ce fichier ne compile pas, une classe doit avoir au moins une méthode (par forcément une
méthode main).

2.9.2 Mots croisés


1 2 3

4 5

6 7

8 9

10

11 12 13

14

15 16

17 18

19

HORIZONTALEMENT VERTICALEMENT
4 Commande pour executer un programme 1 Patrons de classes (-)
java (-) 2 Pas entier (-)
8 Entier (-) 3 Tout le monde peut y accéder (-)
9 Préfixe (-) 5 Tant que (-)
10 Acronyme d’un fabricant de puces (-) 6 Transformer en bytecode (-)
11 Pour afficher quelque chose (-) 7 Inconstance (-)
15 A beaucoup de caractères (-) 9 Vaut zéro ou un (-)
16 Annoncer une nouvelle classe ou une 12 Modificateur de main (-)
méthode (-)
13 Ma méthode principale (-)
17 Elément de programme (-)
14 Définit un comportement (-)
19 Elle a une ligne (-)
18 Machine fonctionnant au bytecode (-)
Chapitre 3

La notion d’objets : définition et


utilisation

3.1 Introduction au développement objets


Dans une entreprise, un projet est soumis avec les spécifications suivantes :

3 formes doivent s’afficher sur une interface graphique : un cercle, un carré et un triangle.
Lorsque l’utilisateur cliquera sur un bouton, la forme pivotera de 360 degrés dans le sens des
aiguilles d’une montre (tour complet sur elle-même), et le système jouera un son d’un fichier
WAV (format de son) spécifique à cette forme.
2 prototypes sont proposés pour réaliser le programme : une en programmation impérative et une
en programmation objets avec une classe pour chaque forme.

Programmation impérative
Programmation Objets
tourner(typeForme){ Carre Cercle Triangle
//faire tourner la forme tourner(){ tourner(){ tourner(){
//de 360 degrés //code pour //code pour //code pour
} //faire tourner //faire tourner //faire tourner
//un carré //un cercle //un triangle
jouerSon(typeForme){ } } }
//utiliser typeForme
//pour chercher jouerSon(){ jouerSon(){ jouerSon(){
//quel son WAV jouer //code pour //code pour //code pour
//puis le jouer //jouer le son //jouer le son //jouer le son
} //d’un carré //d’un cercle //d’un triangle
} } }

En programmation objet, on va créer ”un objet” par type de forme demandée, et on associera à
chaque forme ses fonctionnalités propres (tourner, jouer un son). En programmation impérative,
on utilisera des procédures génériques et les choix des opérations à effectuer se feront à l’aide de
tests (par exemples des if). Les 2 solutions répondent au problème, mais la modélisation objet
est plus rigoureuse et offre de meilleures garanties en cas de modifications futures.

Justement, supposons que les spécifications soint ensuite modifiées par le commanditaire du projet :

31
32 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

Il y aura en plus une forme d’amibe à l’écran, au milieu des autres. Lorsque l’utilisateur cliquera
sur l’amibe elle tournera comme les autres formes, mais jouera un son .AIF

Ceci implique des modifications dans le code

Programmation impérative
tourner(typeForme){
//faire tourner la forme Programmation Objets
//de 360 degrés
} Amibe
tourner(){
//code pour
jouerSon(typeForme){
//faire tourner
//Si la forme n’est pas
//l’amibe
//une amibe
}
//utiliser typeForme
//pour chercher
jouerSon(){
//quel son WAV jouer
//code pour
//puis le jouer
//jouer le son
//sinon
//de l’amibe
//jouer le son AIF
}
//de l’amibe
}

En programmation impérative on va être obligé de modifier le code, ce qui peut provoquer l’ajout
d’erreurs (bugs). En programmation objet, on va rajouter un nouvel objet de forme amibe et lui
associer ses comportements propres. On n’a pas à modifier le code existant déjà validé.

Problème : le centre de rotation diffère des autres.

(a) Une amibe. (b) Les centres de rotation ne sont pas les mêmes.

Ceci implique une nouvelle modification pour tourner les formes.


3.1. INTRODUCTION AU DÉVELOPPEMENT OBJETS 33

Programmation impérative
tourner(typeForme, centreX, centreY){
//si la forme n’est pas
//une amibe
//calculer le centre
//sur la base d’un
//rectangle puis Programmation Objets
//faire tourner la
//forme de 360 degrés Amibe
//sinon int centreX;
//utiliser centreX et int centreY;
tourner(){
//centreY comme centre
//faire tourner
//de rotation puis faire
//l’amibe en utilisant
//tourner la forme
//centreX et centreY
}
}
jouerSon(typeForme){
jouerSon(){
//Si la forme n’est pas
//code pour
//une amibe
//jouer le son
//utiliser typeForme
//de l’amibe
//pour chercher
}
//quel son WAV jouer
//puis le jouer
//sinon
//jouer le son AIF
//de l’amibe
}

En programmation objets on n’a pas besoin de modifier le code des autres formes (carré, triangle,
cercle). On peut même aller plus loin et créer une classe contenant les parties communes, puis des
sous-classes contenant les parties spécifiques. Si la classe Forme contient une fonctionnalité donnée
toutes ses sous-classes en héritent automatiquement. La classe Triangle, par exemple, suit les
directives de la classe forme, pour la classe Amibe on a modifié les parties de code nécessaires.

Forme Super−classe

tourner(){.
//code
}

jouerSon(){
//code
}

Héritage

Carre Triangle Cercle Amibe Sous−classes

tourner(){
//code de
//l’amibe
}
jouerSon(){
//code de
// l’amibe
}

Figure 3.1 – Conception générale à l’aide d’une hiérarchie de classes.


34 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

3.2 La notion de classe


Les classes ne sont pas des objets, mais servent à les construire. Elles sont des patrons
d’objets, indiquant à la JVM comment produire un objet d’un type donné. Chaque objet peut
affecter ses propres valeurs aux variables d’instances définies dans la classe à partir de laquelle il
a été créé.

Analogie : un objet peut être comparé à des fiches d’un carnet d’adresses, la classe correspond
à la structure d’une fiche (nom, téléphone, adresse, . . .), les objets correspondent aux personnes
stockées dans les fiches.

3.2.1 Exemple de classe :


/**
*Classe de définition d’un point dans le plan
*/
public class Point
{
private int x; //abscisse
private int y; //ordonnée

public void initialise(int abs,int ord)


{
x=abs;
y=ord;
}

public void deplace(int dx,int dy)


{
x+=dx;
y+=dy;
}

public void affiche()


{
System.out.println("Je suis un point de coordonnées "+x+" "+y);
}
}

De part la définition de cette classe Point, nous pouvons noter deux choses :
— 2 variables d’instances sont définies x et y. Le mot private indique que l’on ne peut avoir
accès à ces variables qu’à l’intérieur de la classe. (On appelle parfois ces variables champs
ou attributs)
— 3 méthodes sont définies initialise, deplace et affiche. Le mot public indique que ces
méthodes sont utilisables depuis un programme quelconque. Ces 3 méthodes ne possèdent
pas de valeur de retour, dans ce cas on utilise le mot clé void dans la déclaration. Notons,
de plus, que la méthode initialise – par exemple – possède 2 arguments abs et ord, tous
deux de type entier. (Note : il est possible d’avoir des méthodes private dans une classe,
dans ce cas elles ne pourront être utilisées que dans la classe)

3.2.2 Création d’un objet à l’aide d’une classe


On déclare un objet de type Point en définissant une variable de type Point de la manière
suivante :
3.2. LA NOTION DE CLASSE 35

Point a;
ceci implique la création d’une référence de type Point nommée a, à ce stade aucun objet n’a été
créé.
Pour créer un objet, il faut utiliser l’opérateur new :
Point a = new Point();

? x
a ? y

Un objet point a été créé, mais x et y n’ont pas reçu de valeur, par défaut ces variables sont initia-
lisées à zéro. Ensuite on peut initialiser les valeurs de l’objet en utilisant la méthode initialise :
a.initialise(3,5);

3 x
a 5 y

3.2.3 Exemple d’utilisation dans un programme complet


public class TestPoint{

public static void main(String [] args)


{
Point a;
Point b;

a = new Point();
b = new Point();

a.initialise(3,5);
a.affiche();
a.deplace(2,0);
a.affiche();
b.initialise(6,8);
b.affiche();
}
}

Si on l’exécute, ce programme produira le résultat suivant :

Je suis un point de coordonnées 3 5


Je suis un point de coordonnées 5 5
Je suis un point de coordonnées 6 8

Remarques : Pour utiliser ce programme, il faut créer 2 classes, on enregistre la classe Point
dans un fichier Point.java et la classe TestPoint dans un fichier TestPoint.java.
En règle générale, on sépare la fonction d’utilisation (c’est-à-dire le main) dans une classe à
part de manière à faciliter la réutilisation des classes déjà construites.
Pour utiliser notre programme il faut :
1. compiler le fichier Point.java ;
2. compiler le fichier TestPoint.java, il ne sera possible d’utiliser le programme que si le
fichier Point.class existe ;
3. lancer le programme TestPoint.
36 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

Java impose au minimum que :


— Un fichier source peut contenir une ou plusieurs classes, mais exactement une seule doit
être publique.
— La classe contenant la méthode main doit obligatoirement être publique.
— Une classe n’ayant aucun attribut d’accès (public/private) est accessible à toutes les classes
du même paquetage, et donc a fortiori du même fichier source. (Nous verrons les paquetages
plus tard).

3.3 La notion de constructeur


Un constructeur permet d’automatiser le mécanisme d’utilisation d’un objet en évitant d’ap-
peler une méthode spécifique (comme la méthode initialise de la classe Point).
Un constructeur est une méthode sans valeur de retour, portant le même nom que la
classe pouvant définir tout un tas d’actions utiles au bon fonctionnement d’un objet. Le construc-
teur peut disposer d’un nombre quelconque d’arguments.

Exemple

public class Point


{
private int x;
private int y;

public Point(int abs,int ord) //constructeur


{
x=abs;
y=ord;
}

public void deplace(int dx,int dy)


{
x+=dx;
y+=dy;
}

public void affiche()


{
System.out.println("Je suis un point de coordonnées "+x+" "+y);
}
}

Nous créons ensuite un point via Point a = new Point(1,3);


Voici quelques lignes de code qui pourraient être mises dans une méthode main pour tester le
programme.

Point a;
Point b = new Point(6,8);

a = new Point(3,5);
a.affiche();
a.deplace(2,0);
a.affiche();
b.affiche();
3.3. LA NOTION DE CONSTRUCTEUR 37

Attention : dans notre nouvelle classe, l’utilisation de a = new Point(); n’est plus possible
et provoque une erreur. Si l’on désire conserver un constructeur sans arguments, il faut écrire 2
constructeurs.

Lors de la construction d’un objet, il se passe, dans l’ordre :


1. initialisation par défaut des champs de l’objet
2. initialisation explicite des champs donnée lors de leur déclaration
3. exécution du corps du constructeur.
Liste des initialisations par défaut des champs d’un objet en fonction de leur type. Ces valeurs
correspondent en général à une valeur nulle :
— boolean → false
— char → null
— entier → 0
— flottant → 0.0 (ou 0.f)
— objet quelconque → null
Cas des champs déclarés avec l’attribut final. Ces champs ne doivent être déclarés qu’un
seule fois. Toute tentative de modification ultérieure aboutira à une erreur de compilation. Un
champ final doit être initialisé au plus tard par le constructeur. Il n’est pas permis de compter
sur l’initialisation par défaut d’un tel champ.

Exemple de programme récapitulatif


public class Init{
public static void main(String [] args)
{
A a = new A();
a.affiche();
}
}

class A
{
private np; //initialisation de type 1
private int n=20, p=10; //initialisation de type 2

public A()
{
np = np * p; //ou np=n*n: initialisation de type 3
n = 5;
}

public void affiche()


{
System.out.println("n="+n+", p="+p+", np="+np);
}
}
Cependant, il est préférable d’effectuer les initialisations par le constructeur pour que l’utilisateur
n’ait pas à s’interroger sur l’ordre des différentes opérations.
public A()
{
n=20, p=10;
np = n * p;
38 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

n=5;
}

3.4 Affectation et comparaison d’objets


Nous venons de voir qu’il existe des variables de type ”classe” (Point a;) destinées à contenir
des références sur des objets. Une référence est simplement une autre valeur de variable possible.
Pour ces variables, les affectations portent sur les références et non les objets.

3 x
a 5 y

Point a,b; 2 x
b 0 y
a = new Point(3,5);
b = new Point(2,0);

Exemple Si maintenant nous ajoutons :

3 x
a 5 y

2 x
b 0 y
a = b;

On recopie en fait dans a la référence de b, a et b désignent alors le même objet et non la


même valeur.

Second exemple :

Point a,b,c;
a = new Point(1,10);
b = new Point(2,20);
c = a;
a = b;
b = c;

Il y a 2 objets et 3 variables de type Point. Le schéma suivant représente le résultat final du code
ci-dessous.

1 x
a 10 y

b
2 x
c 20 y

3.4.1 La référence null


Si on effectue une déclaration sans initialisation, par exemple Point p;, par défaut p est
initialisé à null (rien). On ne peut pas avoir accès aux champs de l’objet ni utiliser ses méthodes.
Dans ce cas, une erreur (exception) de type NullPointerException sera levée à l’utilisation.
3.5. LE RAMASSE-MIETTES 39

null

On peut cependant tester la non nullité d’une référence à un objet.

if(p!=null)
{
p.affiche();
}

3.5 Le ramasse-miettes
Nous avons vu que grâce à l’opérateur new, la JVM alloue un emplacement mémoire à l’objet et
l’initialise. Il n’existe pas de mécanisme de destruction d’objets. Il existe par contre un mécanisme
de gestion automatique de la mémoire connu sous le nom de ramasse-miettes (en anglais garbage
collector). Son principe est le suivant :
— A tout instant, la JVM connaı̂t le nombre de références à un objet donné (tout est géré par
références en Java).
— Lorsqu’il n’existe plus aucune référence sur un objet, on est certain que le programme ne
pourra plus y accéder. Il est donc possible de libérer l’emplacement correspondant. Cepen-
dant, pour des raisons d’efficacité ( ? ?), Java n’impose pas que ce soit fait immédiatement.
On dit que l’objet est candidat au ramasse-miettes.

Note : La zone mémoire utilisée pour allouer de l’espace à de nouveaux objets s’appelle le tas.
Tous les objets résident sur le tas. Avant qu’un objet ne soit candidat au ramasse-miettes, Java
appelle la méthode finalize, qui est une méthode permettant d’effectuer des opérations lors de
la destruction de l’objet.

3.6 L’encapsulation de données


L’encapsulation de données est une convention de manipulation des variables d’instances en
Java. Il est plus que fortement conseillé de suivre cette démarche, elle s’avère même obligatoire
pour l’écriture d’un code correct et lisible dans certaines structures.

En général, une bonne programmation objet protège les données relatives à un objet : les
variables définies dans une classe. Pour cela, on déclare ces champs private, et on utilise des
méthodes déclarées public pour y accéder.
Par exemple, pour la classe Point, on rajoutera les méthodes suivantes

public int getX(){return x;}

public int getY(){return y;}

public void setX(int abs){ x = abs;}

public void setY(int ord){ y = ord;}

public void setPosition(int abs, int ord)


{
x = abs;
40 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

y = ord;
}

Suivant les cas, il peut-être préférable d’avoir une seule méthode de changement de variables
lorsque que la modification d’une variable peut en affecter une autre.

Autre exemple :

public class BonChien{


private int taille;

public BonChien()
{
taille = 20;
}

public int getTaille()


{
return taille;
}

public void setTaille(int t)


{
taille = t;
}

public void aboyer()


{
if(taille > 60)
{
System.out.println("Grr ! Grr !");
}else if(taille > 20){
System.out.println("Ouaf ! Ouaf !");
}else{
System.out.println("Kai ! Kai !");
}
}
}

public class TestBonChien{


public static void main(String [] args)
{
BonChien un = new BonChien();
BonChien deux = new BonChien();

un.setTaille(70);
deux.setTaille(8);
System.out.println("Taille de un : "+un.getTaille());
un.aboyer();
System.out.println("Taille de deux : "+deux.getTaille());
deux.aboyer();
}
}
3.7. CHAMPS ET MÉTHODES STATIQUES 41

3.7 Champs et méthodes statiques


En Java on peut définir des champs qui n’existent qu’en un seul exemplaire pour toutes les
instances de la classe, au lieu d’exister dans chacune des instances. Il s’agit en quelque sorte de
données globales partagées par toutes les instances d’une même classe. On parle de champs
(ou variables) de classe ou de champs (ou variables) statiques, nous verrons plus tard qu’il existe
aussi des méthodes statiques.

3.7.1 Variables statiques


Exemple classique

class A
{
public int n;
public float y;

public A()
{
n = 3;
y = 5.5;
}
}

Chaque objet de type A possède ses propres champs n et y.


Si on crée 2 objets, leur occupation en mémoire peut se représenter de la manière suivante :

3 a1.n
a1 5.5 a1.y

A a1 = new A(); 2 a2.n


A a2 = new A(); a2 0.5 a2.y
a2.n -= 1;
a2.y -= 5.0;

Avec un champ statique

class B
{
public static int n = 2;
public float y;

public B()
{
y = 5.5;
}
}

b1.n ou B.n 2 b2.n ou B.n


B b1 = new B(); b1 5.5 0.5 b2
B b2 = new B(); b1.y b2.y
b2.y -= 5.0;
42 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

b1.n et b2.n désignent le même champ, il est cependant préférable de s’y référer par B.n. n
est une variable statique de la classe B.

Exemple :
public class ExObjet
{
private static long nb = 0;

public ExObjet()
{
System.out.print("++ creation de l’objet ExObjet numero "+nb+" ");
nb++;
System.out.println("il y a maintenant "+nb+" objet(s)");
}
}

public class TestExObjet


{
public static void main(String args [])
{
ExObjet a,b,c;
System.out.println("Avant la creation de l’objet a");
a = new ExObjet();
System.out.println("Après la creation de l’objet a");
System.out.println("Avant la creation de l’objet b");
b = new ExObjet();
System.out.println("Après la creation de l’objet b");
c = new ExObjet();
System.out.println("Après la creation du dernier objet c");
}
}
Résultat du programme
Avant la creation de l’objet a
++ creation de l’objet ExObjet numero 0 il y a maintenant 1 objet(s)
Après la creation de l’objet a
Avant la creation de l’objet b
++ creation de l’objet ExObjet numero 1 il y a maintenant 2 objet(s)
Après la creation de l’objet b
++ creation de l’objet ExObjet numero 2 il y a maintenant 3 objet(s)
Après la creation du dernier objet c

Remarque lorsque l’on compte le nombre d’objets, on ne prend pas en compte les éventuels
objets détruits par le ramasse-miettes (il faudrait utiliser la méthode statique finalize).

Les champs statiques peuvent être initialisés avant l’appel à un constructeur. Il peut même
ne pas y avoir de constructeur, dans ce cas la seule initialisation possible est une initialisation
explicite lors de la déclaration.

3.7.2 Les méthodes statiques


Les méthodes statiques jouent un rôle indépendant d’un objet quelconque. Elles peuvent être
appelés sans qu’un objet de la classe n’ait été créé.
3.7. CHAMPS ET MÉTHODES STATIQUES 43

Exemple

public class B2
{
private float x;
private static int n;

public static void f() //méthode de classe ou statique


{
//ici on ne peut pas accéder à x, mais on peut accéder à n
}
}

Exemple

class ExObjet2
{
private static long nb = 0;

public ExObjet2()
{
System.out.print("++ creation de l’objet ExObjet2 numero "+nb+" ");
nb++;
System.out.println("il y a maintenant "+nb+" objet"+pluriel());
}

public static long getNb()


{
return nb;
}

public static String pluriel()


{
if(nb>1){
return "s";
}
return "";
}
}

public class TestExObjet2


{
public static void main(String [] args)
{
ExObjet2 a,b,c;
System.out.println("Avant la creation de l’objet a, NB objet" +
ExObjet2.pluriel() + ":" + ExObjet2.getNb());
a = new ExObjet2();
System.out.println("Après la creation de l’objet a, NB objet" +
ExObjet2.pluriel() + ":"+ExObjet2.getNb());
System.out.println("Avant la creation de l’objet b, NB objet" +
ExObjet2.pluriel() + ":"+ExObjet2.getNb());
b = new ExObjet2();
System.out.println("Après la creation de l’objet b, NB objet" +
44 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

ExObjet2.pluriel() + ":"+ExObjet2.getNb());
c = new ExObjet2();
System.out.println("Après la creation du dernier objet c, NB objet" +
ExObjet2.pluriel() + ":"+ExObjet2.getNb());
}
}

Résultat du programme

Avant la creation de l’objet a, NB objet: 0


++ creation de l’objet ExObjet numero 0 il y a maintenant 1 objet
Après la creation de l’objet a, NB objet: 1
Avant la creation de l’objet b, NB objet: 1
++ creation de l’objet ExObjet numero 1 il y a maintenant 2 objets
Après la creation de l’objet b, NB objets: 2
++ creation de l’objet ExObjet numero 2 il y a maintenant 3 objets
Après la creation du dernier objet c, NB objets: 3

Les méthodes statiques s’avèrent utiles


— pour permettre aux objets de disposer d’informations ”collectives”,
— fournir des services n’ayant une signification que pour la classe elle-même (par exemple
pour l’identification d’une classe),
— fonctionnalités n’étant pas liées à un objet quelconque (par exemple la méthode pluriel,
ou des méthodes mathématiques comme le calcul d’un cosinus ou d’une racine carré avec
la classe Math).

3.8 Surdéfinition/surcharge de méthodes


On parle de surcharge lorsqu’un même symbole possède plusieurs significations différentes
choisies en fonction du contexte d’utilisation. Par exemple, l’opérateur + peut, suivant les cas,
correspondre à une somme d’entiers, de flottants ou une concaténation de chaı̂nes de caractères.
Ceci s’applique également en Java aux méthodes. Plusieurs méthodes peuvent porter le même
nom pour peu que le nombre et le type de leurs arguments permettent au compilateur d’effectuer
son choix.

Exemple introductif : une classe Point avec 3 méthodes deplace

public class Point


{
private int x,y;

public Point(int abs, int ord) //constructeur


{
x = abs;
y = ord;
}

public void deplace(int dx, int dy)


{
x += dx;
y += dy;
}

public void deplace(int dz)


3.8. SURDÉFINITION/SURCHARGE DE MÉTHODES 45

{
x += dz;
}

public void deplace(short dz)


{
y += dz;
}
}

public class TestSurdef


{
public static void main(String [] args)
{
Point a = new Point(1,2);
a.deplace(1,3); //appel de deplace(int ,int )
a.deplace(2); //appel de deplace(int)
short p = 3;
a.deplace(p); //appel de deplace(short)
byte b = 2;
a.deplace(b); //appel de deplace(short)
}
}

Si jamais il y a une ambiguı̈té lors de l’appel, il y aura erreur de compilation.

Remarques importantes :
— Le type de la valeur de retour n’intervient pas dans le choix de la méthode surchargée.
— On peut surcharger des méthodes statiques.
— L’attribut final n’a aucune influence sur le choix d’une méthode surchargée.
public void deplace (int dx) { ... }
public void deplace (final int dx){ ... } //erreur de compilation !!!

3.8.1 Surdéfinition de constructeurs


Exemple

public class Point


{
private int x,y;

public Point()
{
x = 0;
y = 0;
}

public Point(int a)
{
x = y = a;
}

public Point(int abs, int ord)


{
46 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

x = abs;
y = ord;
}

public Point(Point a)
{
x = a.getX();
y = a.getY();
}

public int getX()


{
return x;
}

public int getY()


{
return y;
}
}
Utilisation
Point a = new Point(1,3);
Point d = new Point(2);
Point e = new Point(d);

3.9 La copie d’objets


L’affectation de variables d’un type objet quelconque se limite à la recopie de référence et ne
provoque pas la recopie de la valeur des objet.
En général pour copier efficacement un objet, on crée une méthode spécifique.
public class Point
{
private int x,y;
.
.
.
public Point copie()
{
Point p = new Point(x,y);

return p;
}
}
Utilisation
Point a = new Point(1,2);
Point b = a.copie();
Cette démarche est utilisable tant que la classe concernée ne comporte pas des variables qui
sont de type objet (par exemple lorsque dans une classe Point on a un champ qui est aussi une
variable de type Point). Il faut alors décider si la copie soit porter sur les objets référencés ou
juste sur les références.
3.10. AUTORÉFÉRENCEMENT : LE MOT CLÉ THIS 47

— Copie superficielle d’un objet : on copie la valeur de tous les champs, y compris ceux qui
sont d’un type classe.
— Copie profonde : on recopie la valeur des champs d’un type primitif, mais pour les champs
de type classe on crée une nouvelle référence à un nouvel objet de même type et de même
valeur.

Exemple : permutation des valeurs de 2 objets

public class Point


{
private int x,y;
.
.
.
public void permute(Point a)
{
Point c = new Point(0,0);
c.setX(a.getX());
c.setY(a.getY());

a.setX(x);
a.setY(y);

x = c.getX();
y = c.getY();
}
}

3 x
a 5 y
Point a = new Point(3,5);
Point b = new Point(0,2);
2 x
b 0 y

2 x 2 x
a 0 y 0 y

a.permute(b) 3 x
b 5 y

A la fin de la méthode permute, le point référencé par c devient candidat au ramasse-


miettes.

3.10 Autoréférencement : le mot clé this


Il permet de faire référence à l’instance de l’objet courant dans sa globalité au sein d’une
méthode.

Exemple : Méthode coı̈ncide pour savoir si un point coı̈ncide avec un autre.


48 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

public class Point


{
private int x,y;
.
.
.
public boolean coincide(Point pt)
{
return ((pt.getX() == this.x) && (pt.getY() == this.y));
}
}

Autre exemple : Voici le constructeur classique que nous avons écrit :


public Point(int abs,int ord)
{
x = abs;
y = ord;
}
Ce constructeur peut se ré-écrire de la manière suivante en utilisant un auto-référencement :
public Point(int x,int y)
{
this.x = x;
this.y = y;
}
Le mot clé this permet d’employer des noms d’arguments identiques à des noms de champs
(ce qui peut éviter l’utilisation de nouveaux identificateurs).

Il est également possible de s’en servir pour appeler un autre constructeur au sein de la même
classe.
public class Point
{
private int x,y;

public Point(int x,int y)


{
this.x = x;
this.y = y;
System.out.println("Constructeur avec 2 arguments "+x+" et "+y);
}

public Point()
{
this(0,0); //appel du constructeur precedent Point(0,0)
System.out.println("Constructeur sans argument");
}

}
ATTENTION : l’appel de this(0,0) doit obligatoirement être la première instruction
du constructeur.
Les instructions :
3.11. REMARQUES SUR LA DÉFINITION DE MÉTHODES 49

Point a = new Point(1,2);


Point b = new Point();

provoquent le résultat suivant :

Constructeur avec 2 arguments 1 et 2


Constructeur avec 2 arguments 0 et 0
Constructeur sans argument

3.11 Remarques sur la définition de méthodes


3.11.1 Méthodes de type procédures et fonctions
Si une méthode ne fournit aucun résultat, le mot clé void figure dans son en-tête à la place du
type de valeur de retour. Une telle méthode sera appelée une procédure.

Si une méthode fournit un résultat, elle porte alors le nom de fonction. Le type de sa valeur
de retour doit être indiqué dans son en-tête, l’instruction return doit également être utilisée pour
retourner le résultat.

Exemple : une méthode distance qui calcule la distance du point par rapport à l’origine (0, 0)
que l’on pourrait ajouter à la classe Point.

public class Point


{
private int x,y;
.
.
.
public double distance()
{
double d;
d = Math.sqrt(x*x + y*y);
return d;
}
}

Utilisation de cette méthode :

Point a = new Point(2,5);


double r;

r = a.distance();
System.out.println("La distance par rapport à l’origine est de "+r);

Il est également possible de ne pas utiliser la valeur de retour, dans ce cas on appelle juste la
méthode :

a.distance();

3.11.2 Les arguments


Ils figurent dans l’en-tête de la définition d’une méthode et peuvent être utilisés dans le corps
de la méthode.
50 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

public void f(int arg1,int arg2,int arg3)


{
...
}

On peut théoriquement changer la valeur des arguments, mais c’est assez déconseillé.

void f(final int n,double x)


{
...
x = 2.5 //correct mais déconseillé
}

Il également déconseillé de modifier la valeur des arguments comme le montre la méthode


précédente. Dans le cas d’arguments de type primitif, les modifications à l’intérieur de la méthode
ne sont plus prises en compte à la sortie de la méthode. On conseille en général de créer une
variable locale supplémentaire et de copier la valeur de l’argument dedans avant de l’utiliser.
Notez que dans la méthode précédente, toute modification de la valeur de n provoque une
erreur de compilation.

3.11.3 Les variables locales


void f(int n)
{
float x; //variable locale à f
float n; //INTERDIT en Java
...
}

void g()
{
double x; //variable locale à g, indépendante de la variable de f
...
}

L’emplacement mémoire d’une variable locale est alloué au moment ou l’on entre dans la
méthode, il est libéré lorsque l’on en sort.

N.B. Les variables locales n’ont pas d’initialisation par défaut.

Il est possible de créer des variables locales à un bloc (par exemple dans une boucle for) qui ne
seront valides que dans ce bloc.

public void f()


{
int i;
...
for(i=0;i<5;i++)
{
int p; //p n’est connu que dans le bloc du for
...
}
...
}
3.12. LES PAQUETAGES (PACKAGES) 51

3.11.4 La récursivité
La récursivité de méthodes peut prendre 2 formes :
— directe : une méthode comporte dans sa définition au moins un appel à elle-même,
— croisée : l’appel d’une méthode entraı̂ne l’appel d’une autre méthode qui a son tour appelle
la méthode initiale.
La récursivité peut s’appliquer aussi bien aux méthodes statiques qu’aux autres méthodes.

Exemple : Calcul de la factorielle d’un nombre par une méthode statique (pour n = 8 le résultat
est 40320).

public class Util


{
public static long fact(long n)
{
if(n>1)
return (fact(n-1) * n);
return 1; //notez que si n<0 fact n’est en fait pas définie
}
}

public class TestFact


{
public static void main(String [] args)
{
int n = Integer.parseInt(args[0]);

System.out.println("La factorielle de "+ n +" est "+ Util.fact(n));

}
}

Note : A chaque appel de fact, il y a une allocation mémoire pour les variables locales, la valeur
de retour et l’argument. Chaque nouvel appel de fact entraı̂ne donc une telle allocation sans que
les emplacements précédents n’aient été libérés (empilement d’espace mémoire). Ce n’est qu’à la
première instruction return qu’il y a un dépilement des arguments.

3.12 Les paquetages (packages)


Une paquetage est un regroupement logique, sous un identificateur commun, d’un ensemble de
classes. La notion de paquetage se rapproche de la notion de bibliothèque (ou librairie) que l’on
trouve dans d’autres langages. Elle facilite le développement et la cohabitation de logiciels.

3.12.1 Attribution d’une classe à un paquetage


Un paquetage est caractérisé par un nom qui est soit un simple identificateur soit une suite
d’identificateur séparés par des points. Exemple :

MesClasses
Utilitaires.Mathematiques
Utilitaires.Tris

L’utilisation du point . implique une hiérarchie logique de package. Ainsi, dans l’exemple
précédent, les packages Mathématiques et Tris sont en dessous du package Utilitaires.
52 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

Utilitaires

Mathematiques Tris

L’attribution à un package se fait au niveau du fichier source. Toutes les classes d’un même
fichier appartiendront toujours à un même package. On place en début de fichier une instruction
de la forme :

package nompaquetage;

La plupart des environnements imposent des contraintes quant à la localisation des fichiers
correspondant à un package. En particulier un paquetage de nom X.Y.Z se trouvera toujours
dans un sous-répertoire de nom X.Y.Z (les répertoires de niveau hiérarchique supérieur peuvent
être quelconques). Deux paquetages X.Y.Z et X.Y.U n’ont pas l’obligation d’être dans le même
répertoire.
Avec le JDK (ou SDK) de Sun la recherche d’un package se fait dans les répertoires indiqués
dans la variable d’environnement CLASSPATH.

S’il n’y a pas de paquetage indiqué dans un fichier source, le compilateur considère que ceux-ci
appartiennent au paquetage par défaut.

Si l’on ne souhaite pas utiliser la variable d’environnement CLASSPATH, il est possible de spécifier
les chemins où se trouvent les paquetages à l’aide de l’option -classpath des commandes java et
javac. Par exemple si une classe Toto utilise des packages ou des classes situées dans les répertoires
~/MesPackages et /usr/local/packages standard, on utilisera les commandes suivantes :

javac -classpath ~/MesPackages;/usr/local/packages_standard Toto.java


java -classpath ~/MesPackages;/usr/local/packages_standard Toto

Ceci implique si le package affaires de toto est utilisé, le dossier contenant les éléments de
ce package doit être situé soit dans ~/MesPackages/affaires_de_toto ou dans
/usr/local/packages standard/affaires de toto ; évidemment une seule des 2 solutions est
valable.

3.12.2 Utilisation d’une classe située dans une paquetage


Il faut :
— citer le nom du paquetage avec le nom de la classe,
— utiliser une instruction import en indiquant soit une classe particulière d’un paquetage,
soit un paquetage entier.

Avec une citation de nom de classe. Si vous avez attribué la classe Point au package
MesClasses, vous pouvez l’utiliser en la nommant MesClasses.Point, exemple :

MesClasses.Point p = new MesClasses.Point(2,5);


...
p.affiche(); //le nom de la classe n’est plus requis ici

En important une classe avec l’instruction import

import MesClasses.Point, MesClasses.Cercle;

Il est ensuite possible d’utiliser les classes Point et Cercle sans avoir à mentionner leurs noms.
3.13. LES TABLEAUX 53

En important un package en entier.

import MesClasses.*;

Toutes les classes du package MesClasses et les classes des packages hiérarchiquement en dessous
sont alors directement utilisables. Notez que si vous tapez uniquement import MesClasses; les
classes du package MesClasses seront directement utilisables, mais pas les packages contenus dans
MesClasses.

Il existe plusieurs paquetages standard fournis avec Java, par exemple le package Math ou
encore le package java.lang qui est automatiquement ajouté par le compilateur.

3.12.3 Droits d’accès


— Avec le mot clé public, une classe est accessible à partir de toutes les autres classes
(éventuellement via une instruction import).
— Sans le mot clé public, une classe n’est accessible qu’aux classes du même paquetage.
Tant que l’on travaille avec le paquetage par défaut, l’absence du mot public n’a guère
d’importance.
Note : le mot clé private n’a pas de sens au niveau d’une classe.

3.13 Les tableaux


En programmation, un tableau désigne un ensemble d’éléments de même type identifiés par
un nom unique. Chacun des éléments étant ensuite repéré par un indice précisant sa position au
sein de l’exemple.
En Java, les tableaux sont considérés comme des objets, les tableaux à plusieurs indices s’ob-
tiennent par composition de tableaux.

3.13.1 Déclaration et création


Imaginons que nous voulions créer un tableau d’entiers, deux déclarations sont possibles :

int t[];
int []t;

La différence entre les 2 déclarations a une importance lorsque l’on souhaite déclarer plusieurs
tableaux :

int [] t1,t2; //déclaration de 2 tableaux d’entiers


int t1[], t2[]; //meme chose
int t1[], n, t2[]; //2 tableaux t1 et t2, n est un entier
Point a, tp[], b; //a et b sont de type Point, tp est un tableau
// d’objets Point

N.B. : une déclaration de tableau ne doit pas préciser de dimension.

Pour créer un tableau on utilise l’opérateur new comme pour des objets classiques. Voici un
exemple montrant comment créer un tableau de 10 entiers :

int t[] = new int[10];

Il est également possible de fournir une liste d’expression entre accolades. Voici un exemple de
création d’un tableau d’entiers à 5 éléments.

int t[] = {1, 2, 7, 10, 0};


54 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

L’instruction précédente équivaut aux instructions suivantes :

int [] t = new int[5];


t[0] = 1;
t[1] = 2;
t[2] = 7;
t[3] = 10;
t[4] = 0;

Attention. Une fois un tableau créé, on ne peut plus modifier sa taille. Par contre, on peut créer
un nouveau tableau et - si besoin - recopier les valeurs du tableau initial. La taille d’un tableau
est toujours positive.

La taille d’un tableau Il est possible d’avoir accès à la taille d’un tableau à l’aide du champ
length.

int t[] = new int[6];


System.out.println("taille de t "+t.length); //affiche 6
t = new int[2];
System.out.println("taille de t "+t.length); //affiche 2

Ici length est vu comme un champ et non comme une méthode.

Accès aux éléments d’un tableau Un élément est désigné en plaçant entre crochets sa position
après le nom du tableau. Une position est désignée par une expression entière comprise entre 0 (le
premier élément du tableau) et la taille du tableau-1 (dernier élément du tableau).

Exemple : calcul d’une moyenne de notes

public class TestCalculMoyenne


{
public static void main(String [] args)
{
int i, nbNotes, nbSupMoy;
double somme, moyenne, notes[];

if(args.length == 0)
{
System.exit(0);
}

notes = new double[args.length];


nbNotes = args.length;

for(i=0; i< nbNotes; i++)


{
notes[i] = Double.parseDouble(args[i]);
}

somme=0.0;
for(i=0; i< nbNotes; i++)
somme += notes[i] ;

moyenne = somme / (double) nbNotes;


3.13. LES TABLEAUX 55

System.out.println("Moyenne des notes :"+moyenne);

for(i=0, nbSupMoy=0; i < nbNotes; i++)


{
if(notes[i] > moyenne)
nbSupMoy++;
}

System.out.println(nbSupMoy + " notes sont supérieures à cette moyenne");


}
}

Affectation de tableaux. Exemple.

int [] t1 = new int[3];


for(int i=0; i<t1.length; i++)
t1[i] = i;
int [] t2 = new int[2];
for(int i=0; i<t2.length; i++)
t2[i] = 10+ i;

La situation peut-être schématisée comme ceci :

0
t1 1
2

10
t2 11

Si maintenant on exécute :

t1 = t2; //la référence contenue dans t2 est recopiée dans t1

Nous aboutissons alors à cette situation :

0
t1 1
2

10
t2 11

Maintenant si l’on exécute les instructions suivantes :

t1[1] = 5;
System.out.println(t2[1]); //affiche 5 !!!

L’ancien objet référencé par t1 devient candidat au ramasse-miettes. Lors de l’affectation de


références de tableau, il n’y a aucune recopie des valeurs des éléments du tableau.
56 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

Exemple de tableau d’objets

public class Point


{
private int x,y;

public Point(int x,int y)


{
this.x = x;
this.y = y;
}

public void affiche()


{
System.out.println("Point : "+x+", "+y);
}
}

public class TabPoint


{
public static void main(String [] args)
{
Point [] tp;
tp = new Point[3];
tp[0] = new Point(1,2);
tp[1] = new Point(4,5);
tp[2] = new Point(8,9);
for(int i=0; i<tp.length; i++)
tp[i].affiche();
}
}

Résultat :

Point : 1, 2
Point : 4, 5
Point : 8, 9

3.13.2 Tableau en argument ou en retour


Lorsque l’on transmet un nom de tableau en argument d’une méthode, on transmet en fait une
copie de la référence au tableau. La méthode agit directement sur le tableau concerné et non sur
une copie.
Voici un exemple de manipulations de tableau par des méthodes statiques.

class Util
{
static void raz(int t[])
{
for(int i=0; i<t.length; i++)
t[i] = 0;
}

static void affiche (int t[])


{
3.13. LES TABLEAUX 57

for(int i=0; i<t.length; i++)


System.out.print(t[i] + " ");
System.out.println();
}
}

public class TabArg


{
public static void main(String [] args)
{
int t[] = {1, 3, 5, 7};
System.out.print("t avant : ");
Util.affiche(t);
Util.raz(t);
System.out.print("t apres : ");
Util.affiche(t);
}
}
Résultat.
t avant: 1 3 5 7
t apres 0 0 0 0

La même chose s’applique à un tableau fourni en valeur de retour. Par exemple, la méthode
suivante fourni un tableau formé des n premiers entiers :
public static int[] suite(int n)
{
int [] res = new int[n];
for(int i=0; i<t.length; i++)
res[i]=i+1;
return res;
}
Un appel à suite fournira une référence à un tableau dont on pourra éventuellement modifier
la valeur des éléments.

3.13.3 Les tableaux à plusieurs indices


De nombreux langages disposent de la notion de tableau à plusieurs indices. Par exemple, un
tableau à deux indices permet de représenter une matrice mathématique.
Java ne dispose pas d’une telle notion. Néanmoins, il permet de la simuler en créant des
tableaux de tableaux dont les éléments sont eux-mêmes des tableaux. Cette possibilité s’avère en
fait très riche puisqu’elle peut permettre de créer des tableaux irréguliers (par exemple lorsque les
différentes lignes ont un nombre de colonnes qui varie).

Premier exemple. Ces trois déclarations sont équivalentes pour un tableau à 2 dimensions :
int t [] [];
int [] t [];
int [] [] t;
Elles déclarent une référence à un tableau, dans lequel chaque élément est lui-même une référence
à un tableau d’entiers. Notez que pour l’instant aucun tableau n’existe encore.
Considérons la déclaration :
58 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

int t [] [] = { new int [3], new int [2] };


t comporte deux éléments de sorte que le premier soit un tableau de 3 entiers et que le deuxième
soit un tableau de 2 entiers. On aboutit à la situation suivante (les éléments des tableaux d’entiers
sont comme d’habitude initialisés à 0) :

0
0
0

t
0
0

On constate alors que :


— la notation t[0] désigne la référence au premier tableau de 3 entiers,
— la notation t[0][1] désigne le deuxième élément de ce tableau (pour rappel les indices
commencent à 0),
— la notation t[1] désigne la référence au deuxième tableau de 2 entiers,
— la notation t[0][i-1] désigne le ième élément de ce tableau pour i compris entre 1 et 2,
— l’expression t.length vaut 2,
— l’expression t[0].length vaut 3,
— l’expression t[1].length vaut 2.

Second exemple.
int t [] [];
t = new int [2] [];
int [] t1 = new int[3];
int [] t2 = new int[2];
t[0] = t1;
t[1] = t2;
La situation peut s’illustrer comme ceci :

t1

0
0
0

t
0
0
t2

Troisième exemple.
class Util
{
static void raz(int t[] [])
{
int i,j;
3.13. LES TABLEAUX 59

for(i=0;i<t.length;i++)
for(j=0;j<t[i].length;j++)
t[i][j]=0;
}

static void affiche(int t[] [])


{
int i,j;

for(i=0;i<t.length;i++)
{
System.out.println("ligne de rang "+i+" = ");
for(j=0;j<t[i].length;j++)
System.out.println(t[i][j] + " ");
System.out.println();
}
}

public class TestTab2


{
public static void main(String arg[])
{
int t [][] = {{1,2,3}, {11, 12}, {21, 22, 23, 24} }
System.out.println("t avant raz :");
Util.affiche(t);
Util.raz(t);
System.out.println("t apres raz :");
Util.affiche(t);
}
}
Résultat.
t avant raz
ligne de rang 0= 1 2 3
ligne de rang 1= 11 12
ligne de rang 2= 21 22 23 24
t apres raz
ligne de rang 0= 0 0 0
ligne de rang 1= 0 0
ligne de rang 2= 0 0 0 0

Cas des tableaux réguliers. Si on souhaite créer une matrice de N L lignes et N C colonnes
on peut toujours procéder comme ceci :
int t[][] = new int [NL] [];
int i;
for(i=0; i<NL; i++)
t[i] = new int [NC];
On peut également écrire en Java :
int t[][] = new int [NL] [NC];
60 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

3.14 Exercices de cours


3.14.1 Compilateur
Voici le contenu de deux fichiers JAVA, indiquez pour chacun d’eux si ces fichiers peuvent
se compiler ou dans le cas contraire indiquez comment les corriger. Dites finalement ce que font
chacun de ces programmes.
1. Premier fichier :
class TapeDeck
{
private boolean canRecord = false;

void playTape()
{
System.out.println("lecture de la bande");
}

void recordTape()
{
System.out.println("enregistrement de la bande");
}

public boolean getCanRecord()


{
return canRecord;
}

public void setCanRecord(boolean canRecord)


{
this.canRecord=canRecord;
}
}

public class TapeDeckTestDrive{


public static void main(String [] args)
{
t.setCanRecord(true);
t.playTape();

if(t.getCanRecord()==true){
t.recordTape();
}
}
}
2. Deuxième Fichier :
class DVDPlayer{
private boolean canRecord=false;

void recordDVD()
{
System.out.println("enregistrement du DVD");
}
3.14. EXERCICES DE COURS 61

public boolean getCanRecord()


{
return canRecord;
}

public void setCanRecord(boolean canRecord)


{
this.canRecord=canRecord;
}
}

public class DVDPlayerTestDrive{


public static void main(String [] args){
DVDPlayer d = new DVDPlayer();
d.setCanRecord(true);
d.playDVD();

if(d.getCanRecord() == true){
d.recordDVD();
}
}
}
Réponses :
1. Le fichier ne compile pas, il manque la création d’une instance TapeDeck, il faut donc créer
un objet. Pour cela il faut rajouter juste après la déclaration de la méthode main, et avant
la ligne t.canRecord = true; la ligne TapeDeck t = new TapeDeck();
2. Le fichier ne compile pas, il manque la méthode playDVD() dans la classe DVDPlayer, on
pourra par exemple rajouter les lignes suivantes juste avant la fin de la classe :
void playDVD()
{
System.out.println("lecture du DVD");
}
Remarque importante : pour simplifier l’exercice et pouvoir le faire dès le début, il n’y a pas
de droits d’accès indiqués pour les variables d’instances, elles sont donc publiques par défaut. Ceci
n’est pas une bonne pratique : il vaut mieux déclarer les variables privées et utiliser l’encapsula-
tion. A vous donc d’améliorer la correction de l’exercice !

3.14.2 Qui suis-je ?


Associer à chaque ligne ci-dessous un ou plusieurs mots parmi : Classe, Méthode, Objet,
Variable d’instance.
1. Je résulte de la compilation d’un fichier .java :
2. Mes variables d’instances peuvent être différentes de celles de mes copains :
3. Je me comporte comme un patron :
4. J’aime faire des choses :
5. Je peux avoir plusieurs méthodes :
6. Je représente un “état” :
7. J’ai des comportements :
8. On me trouve dans des objets :
9. Je réside sur le tas :
62 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

10. Je sers à créer des instances d’objets :


11. Mon état peut changer :
12. Je déclare des méthodes :
13. Je peux changer lors de l’exécution :
Note : classes et objets ont tous deux un état et un comportement. Ils sont définis dans la classe
mais on dit aussi que c’est l’objet qui les “possède”.

3.14.3 Compilateur 2
Voici trois programmes, indiquez si ces programmes compilent ; si la réponse est oui dites ce
qu’ils font, sinon comment les corrigez vous ?
1. public class XCopie{
public static void main(String [] args)
{
int orig = 42;
XCopie x = new XCopie();
int y = x.go(orig);
System.out.println(orig + " " + y);
}
int go(int arg)
{
arg = arg * 2;
return arg;
}
}
2. class Horloge{
private String heure;

void setHeure(String t){


heure = t;
}
void getHeure(){
return heure;
}
}

public class TesteHorloge{


public static void main(String [] args)
{
Horloge c = new Horloge();

c.setHeure("1245");
String h = c.getHeure();
System.out.println("heure: " + h);
}
}
3. Ce programme compile, mais que fait-il ?
class Resultat{
public static void main(String []args){
Resultat r = new Resultat();
r.go();
3.14. EXERCICES DE COURS 63

void go(){
int y=7;
for(int x=1; x < 8 ; x++){
y++;
if(x > 4){
++y;
System.out.print(y+" ");
}
if(y > 14)
{
System.out.println(" x = " + x);
break;
}
}
}
}

Réponses :
1. La classe compile et s’exécute, le résultat est 42 84
2. Le fichier ne compile pas, il manque le type de retour String pour la méthode getHeure.
3. java Resultat
13 15 x = 6

3.14.4 Qui suis-je ?


Associer à chaque ligne ci-dessous un ou plusieurs mots parmi : variable d’instance, argu-
ment, retour, méthode get, méthode set, encapsulation, public, private, passage par
valeur, méthode.
1. Une classe peut en avoir un nombre quelconque :
2. Une méthode ne peut en avoir qu’un :
3. Peut être converti implicitement :
4. Je préfère mes variables d’instances private :
5. Signifie réellement “faire une copie” :
6. Seules les méthodes set devraient les modifier :
7. Une méthode peut en avoir plusieurs :
8. Je retourne quelque chose par définition :
9. Ne m’utilisez pas avec des variables d’instance :
10. Je peux avoir plusieurs arguments :
11. Par définition, j’accepte un seul argument :
12. Ils aident à créer l’encapsulation :
13. Je vole toujours en solo :

3.14.5 Compilateur 3
Voici deux programmes, indiquez si ces programmes compilent ; si la réponse est oui dites ce
qu’ils font, sinon comment les corrigez vous ? (Pour des raisons de simplicité, l’encapsulation des
données a été omise dans cet exercice, nous rappelons cependant que cet aspect est primordial en
programmation objet)
64 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

1. class Livres{
private String titre;
private String auteur;

public String getTitre()


{
return titre;
}

public void setTitre(String titre)


{
this.titre=titre;
}

public String getAuteur()


{
return auteur;
}

public void setAuteur(String auteur)


{
this.auteur=auteur;
}
}

class TestLivres{
public static void main(String [] args)
{
Livres [] mesLivres = new Livres[3];
int x=0;
mesLivres[0].setTitre("Panique à Java");
mesLivres[1].setTitre("Java et moi");
mesLivres[2].setTitre("Cuisinez en Java");
mesLivres[0].setAuteur("Monique");
mesLivres[1].setAuteur("Michel");
mesLivres[2].setAuteur("Sophie");

while(x<3)
{
System.out.print(mesLivres[x].getTitre());
System.out.print("par");
System.out.print(mesLivres[x].getAuteur());
x=x+1;
}
}
}

2. class Hobbits{
String nom;

public static void main(String [] args)


{
Hobbits [] h = new Hobbits[3];
3.14. EXERCICES DE COURS 65

int z = -1;

while(z < 2)
{
z=z+1;
h[z]= new Hobbits();
h[z].nom="bilbo";
if(z==1){
h[z].nom="frodon";
}
if( z == 2)
{
h[z].nom="sam";
}
System.out.print(h[z].nom + " est un ");
System.out.println("bon nom de Hobbit ");
}
}
}

Réponses :
1. Il manque la création des objets livres, juste après int x=0; il faut rajouter
mesLivres[0] = new Livres();
mesLivres[1] = new Livres();
mesLivres[2] = new Livres();
2. Cette classe compile et fonctionne, nous rappelons que les indices de tableaux commencent
à zéro.
66 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION

3.14.6 Mots croisés

1 2 3

4 5

6 7

8 9

10 11 12

13 14 15

16

17

18

19 20 21 22

23

24 25

26

HORIZONTALEMENT VERTICALEMENT
1 Mettre en oeuvre (-) 2 Type d’incrémentation (-)
4 Teste d’abord (-) 3 Elle fait le travail (-)
5 32 bits (-) 4 Spécifie l’action dans un for (-)
7 Réponse d’une méthode (-) 5 Fixe la valeur de départ (-)
8 Avant le vrai code (-) 6 Méthode qui modifie (-)
11 Changement de type (-) 9 Contraire d’incrément (-)
12 Executrice (-) 10 Répétition (-)
13 Fait partie d’un tableau (-) 12 Package pour les E/S (-)
14 Peut être locale (-) 15 Faits pour être transmis (-)
16 Package contenant les classes essentielles (-) 18 Méthode qui accède (-)
17 A une méthode parseInt (-) 20 Classe qui calcule (-)
19 Pour générer des nombres au hasard (-) 22 Plus petit qu’un short (-)
21 Impossible à convertir (-) 23 Type de boucle (-)
24 Certaines réalités le sont (-) 24 Editeur préféré des puristes (-)
25 Plus petit qu’un int (-)
26 Interface de développement d’applications
(-)
3.14. EXERCICES DE COURS 67

3.14.7 Mots croisés


Certains mots sont introduits dans la section 8.3.2 sur les ArrayList.
1 2 3 4

6 7

8 9

10 11 12 13

14

15

16

17

18

19

20 21 22

23

HORIZONTALEMENT VERTICALEMENT
1 Ils n’ont pas de comportement (-) 2 Implémente un comportement (-)
6 Tous les objets en dérivent (-) 3 Retourne une position (-)
7 Premier indice (-) 4 Retourne un nombre d’éléments (-)
10 Ajoute un objet (-) 5 Grand décimal (-)
11 Loup y es-tu ? (-) 8 Groupe de classes (-)
14 Mutateur (-) 9 Pour savoir si une liste est vide (-)
16 Croı̂t dynamiquement (-) 12 Sorte (-)
18 Homologue de 4 (vertical) (-) 13 Position d’un élément (-)
20 Inconstante (-) 15 Pas réel (-)
22 Sinon (-) 16 Acronyme de la bibliothèque (-)
23 Unité adressable (-) 17 Commun aux courses et aux tableaux (-)
19 Récupère une valeur (-)
21 Article (rien à voir avec Java) (-)
68 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION
Chapitre 4

Les concepts d’Héritage et de


Polymorphisme

4.1 Présentation et définition


L’héritage permet de spécifier une classe générale donnée en sous-classe.

Premier exemple. Voici une première hiérarchie de classe.

Medecin Super classe


travailleALHopital
traiterPatient()

MedecinDeFamille Chirurgien Sous classes

faitDesVisites traiterPatient()
donnerConseil() faireUneIncision()

En Java, cette hiérarchie s’écrira comme ceci :


public class Medecin
{
boolean travailleALHopital;
void traiterPatient()
{
//traiter le patient
}
}

public class MedecinDeFamille extends Medecin


{
boolean faitDesVisites;

void donnerConseil()
{
//donner un simple conseil
}

69
70 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME

public class Chirurgien extends Medecin


{
void traiterPatient()
{
//traitement d’un chirurgien
}

void faireUneIncision()
{
//action spécifique du chirurgien
}
}

Autre exemple : retour à la classe Point.

public class Point


{
private int x,y;

public Point(int x,int y)


{
this.x = x;
this.y = y;
}

public Point()
{
this(0,0);
}

public void deplace(int dx,int dy)


{
x += dx;
y += dy;
}

public void modifie(int x,int y)


{
this.x = x;
this.y = y;
}

public void affiche()


{
System.out.println("Je suis en "+x+" "+y);
}
}

Imaginons que nous ayons besoin de manipuler des points colorés de manière à :
— garder les même fonctionnalités de Point,
— ajouter le traitement d’une couleur,
— redéfinir l’affichage d’un point avec sa couleur.
4.1. PRÉSENTATION ET DÉFINITION 71

public class PointCouleur extends Point


{
private byte couleur;

public void colorie(byte couleur)


{
this.couleur = couleur;
}

public void modifie(int x,int y,byte couleur)


{
super.modifie(x,y);
this.couleur = couleur;
}

public void afficheCouleur()


{
super.affiche();
System.out.println(" de couleur" + couleur);
}

PointCouleur est un Point, Point est la classe de base ou superclasse, PointCouleur est la sous-
classe ou classe dérivée. Un objet de type PointCouleur peut alors faire appel aux méthodes
publiques de PointCouleur et aux méthodes publiques de Point.
Un objet d’une classe dérivée peut accéder directement aux membres public de sa classe de
base, mais pas aux membres privés.

A retenir :
— Une classe dérivée n’accède pas aux membres privés (champs et méthodes) de sa su-
perclasse. Par exemple, les points x et y ne sont pas accessibles directement de la classe
PointCouleur.
— Une classe dérivée a accès aux membres publiques de sa superclasse. Pour accéder à une
méthode public de la superclasse on utilise le mot clé super.
— Un objet d’une classe dérivée accède aux membres publics de sa classe de base exactement
comme s’ils étaient définis directement dans la classe dérivée elle-même.
Exemple d’utilisation des 2 classes précédentes.

PointCouleur pc1 = new PointCouleur();


pc1.modifie(3,5);
pc1.colorie((byte) 3);
pc1.affiche();
pc1.afficheCouleur();
PointCouleur pc2 = new PointCouleur();
pc2.modifie(5,8,(byte)2);
pc2.afficheCouleur();
pc2.deplace(1,-3);
pc2.afficheCouleur();

Ces lignes de code provoquent le résultat suivant :

Je suis en 3 5
Je suis en 3 5
de couleur 3
72 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME

Je suis en 5 8
de couleur 2
Je suis en 6 5
de couleur 2

4.2 Construction et initialisation des objets dérivés

Quand il n’y a pas de constructeur, un pseudo-constructeur par défaut est appelé, nous nous
intéressons maintenant au constructeur de la classe dérivée.

En Java, le constructeur de la classe dérivée doit prendre en charge l’intégralité de la construc-


tion de l’objet. Pour l’initialiser certains champs private de la classe de base, on peut soit utiliser
des méthodes de modifications publiques, soit appeler le constructeur de la super-classe.
Attention. Si un constructeur d’une classe dérivée appelle un constructeur d’une classe de base,
il doit obligatoirement s’agir de la première instruction du constructeur (même principe que pour
l’utilisation du this). La référence à un constructeur est désignée par super.

public class Point


{
private int x,y;

public Point(int x,int y)


{
this.x = x;
this.y = y;
}
...
}

public class PointCouleur extends Point


{
public PointCouleur(int x,int y,byte couleur)
{
super(x,y); //appel du constructeur de la classe Point
this.couleur = couleur;
}
...
}

Attention.
— Les mots clés this et super ne peuvent pas être utilisés en même temps.
— Lorsqu’une classe dérive d’une classe qui dérive elle aussi d’une autre classe, l’appel par
super ne concerne que le constructeur de la classe de base de niveau immédiatement
supérieur.
4.2. CONSTRUCTION ET INITIALISATION DES OBJETS DÉRIVÉS 73

super()

Remarques importantes sur la définition d’un constructeur.


— Si la super-classe ne possède pas de constructeur, il est possible d’appeler le constructeur
par défaut à partir de la classe dérivée via super();. Cet appel peut paraı̂tre superflu,
mais ne nuit pas. Ceci est pratique lorsque l’on construit une classe dérivée sans connaı̂tre
les détails de la classe de base : on s’assure que les différents éléments de la super-classe
seront correctement initialisés. Ceci justifie également le principe de toujours mettre un
constructeur par défaut sans argument dans une classe.
— Si la classe dérivée ne possède pas de constructeur, le constructeur par défaut de la classe
sera appelée et par conséquent le constructeur par défaut de la super-classe. Si ce construc-
teur n’existe pas (s’il y a que des constructeurs avec arguments qui sont définis), il y a une
erreur de compilation. Par exemple ;
class A
{
public A(int n)
{ ... }
...
}

class B extends A
{
// pas de constructeur
}

Cet exemple provoque une erreur car il n’y a pas de constructeur sans argument dans A.
La construction d’un objet B dérivé d’un objet A entraı̂ne 6 étapes :

1. Allocation mémoire pour un objet de type B (y compris les champs définis dans la super-
classe A).

2. Initialisation par défaut de tous les champs de B (aussi bien ceux hérités de A que ceux
définis dans B) aux valeurs nulles classiques.

3. Initialisation explicite des champs hérités de A.

4. Exécution du corps du constructeur de A.

5. Initialisation explicite des champs propres à B.

6. Exécution du constructeur de B.

Notez également qu’il est possible d’avoir plusieurs dérivations successives :


74 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME

B C

D E F G

4.3 Redéfinition, surdéfinition et héritage

4.3.1 Redéfinition

La redéfinition consiste à re-écrire une méthode définit dans la super-classe et à changer son
comportement. Le nombre et le type des arguments ainsi que la valeur de retour doivent être
exactement les mêmes.

public class Point


{
private int x,y;
...
public void affiche()
{
System.out.println("Je suis un point de coordonnées "+x+" "+y);
}
}

public class PointCouleur


{
private byte couleur;
...
public void affiche()
{
super.affiche();
System.out.println(" de couleur "+couleur);
}
}

Lorsque l’on utilise la méthode affiche d’un objet de type PointCouleur, la méthode définie
dans la classe PointCouleur est appliquée, sinon pour un objet de type Point uniquement c’est
la méthode définie dans la classe Point.

Imaginons que nous ayons une hiérarchie de classe avec plusieurs dérivations successives. Dans
la classe la plus “élevée” une méthode f et définie et cette méthode est redéfinie dans certaines de
ses sous-classes.
4.4. LE POLYMORPHISME 75

A définition de f

B redéfinition de f
C

redéfinition de f
D E F

Voici la liste des méthodes f qui seront appelées en fonction du type de l’objet considéré :
— pour A, la méthodes f de A,
— pour B, la méthodes f de A,
— pour C, la méthodes f de C,
— pour D, la méthodes f de D,
— pour E, la méthodes f de A,
— pour F, la méthodes f de C.
Attention. Les droits d’accès des méthodes redéfinies ne doivent pas être diminués : une méthode
publique dans la super-classe ne peut pas être redéfinie privée dans la classe dérivée, l’inverse est
cependant possible.

4.3.2 La surchage
La surcharge ou surdéfinition consiste à modifier le prototype d’une méthode existante, en
changeant le nombre d’arguments et/ou le type des arguments. Nous avons déjà vu cette notion
auparavant, dans le cadre de l’héritage, la recherche d’une méthode acceptable se fait en “remon-
tant” les dérivations successives.

4.3.3 En résumé
— Si une méthode possède la même signature dans une classe dérivée que dans une classe
parente :
— les types des valeurs de retour doivent être exactement les mêmes,
— les droits d’accès de la méthode de la classe dérivée ne doivent pas être moins élevés
que dans la classe parente,
— la clause throws de la méthode dérivée ne doit pas mentionner des exceptions non
indiquées dans la clause throws de la méthode de la classe parente. (Nous verrons ceci
plus tard).
Si ces 3 conditions sont réunies, nous avons une redéfinition sinon il y a une erreur.
— Si la signature de la méthode (nombre/type des arguments) n’est pas la même on a une
surcharge (ou surdéfinition). On rappelle que le type de la valeur de retour n’est pas pris
en compte dans le cas d’une surcharge.
Notez qu’il est possible de dupliquer des champs lors d’un processus d’héritage, dans ce cas seul
le champ de la classe dérivée n’est visible de “l’extérieur”. Ceci est à éviter.

4.4 Le polymorphisme
Le polymorphisme permet de manipuler des objets sans connaı̂tre tout à fait leur type. C’est
un principe extrêmement puissant en programmation orientée objet, qui complète l’héritage.

Exemple de base :

public class Point


{
76 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME

private int x,y;

public Point(int x,int y){ ... }

public Point(){ ... }

public void affiche(){ ... }

...
}

public class PointCouleur


{
private byte couleur;

public PointCouleur(int x,int y,byte couleur){ ... }

public PointCouleur(){ ... }

public void affiche(){ ... }

...
}

Tout élément de type PointCouleur est également de type Point, il est possible d’utiliser cette
caractéristique commune.

Objet de type Point

Point p; 3 x
(Point) p 5 y
p = new Point(3,5);

...
4 x
p = new PointCouleur(4,8,(byte) 2); (Point) p 8 y
2 couleur

Objet de type PointCouleur

Autre exemple.

Point p = new Point(3,5);


p.affiche(); //methode affiche de la classe Point

p = new PointCouleur(4,8,(byte)2);
p.affiche(); //méthode affiche de la classe PointCouleur

Le choix de la méthode à appliquer se fait automatiquement en fonction du type effectif de


l’objet et non pas en fonction du type de la variable qui référence l’objet. Ce choix porte le nom
de liaison dynamique ou ligature dynamique.

Le polymorphisme se traduit par :


— la compatibilité par affectation entre un type de classe et un type ascendant,
— la liaison dynamique des méthodes : comportement adapté à chaque objet.
4.4. LE POLYMORPHISME 77

4.4.1 Tableaux hétérogènes d’objets


Exemple :
Point [] tabPts = new Point[4];

tabPts[0] = new Point(0,2);


tabPts[1] = new PointCouleur(1,5,(byte) 2);
tabPts[2] = new PointCouleur(2,3,(byte) 4);
tabPts[3] = new Point(1,2);

for(int i=0 ; i<tabPts.length ; i++)


{
tabPts.affiche();
}
Ce bout de code provoque le résultat suivant :
Je suis un point de coordonnées 0 2
Je suis un point de coordonnées 1 5
de couleur 2
Je suis un point de coordonnées 2 3
de couleur 4
Je suis un point de coordonnées 1 2
Cette situation s’adapte également à une hiérarchie de classe plus complexe.

B C

D E F G

Autre exemple : cette fois on redéfinit pas de méthode affiche dans la classe PointCouleur
public class Point
{
private int x,y;

public Point(int x,int y)


{
this.x = x;
this.y = y;
}

public void affiche()


{
identifie();
System.out.println(" de coordonnées "+x+" "+y);
}
78 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME

public void identifie()


{
System.out.print("Je suis un point");
}
...
}

public class PointCouleur extends Point


{
private byte couleur;

public PointCouleur(int x,int y,byte couleur)


{
super(x,y);
this.couleur = couleur;
}

public void identifie()


{
System.out.print("Je suis un point couleur de couleur "+couleur+", ");
}
}

Si on reprend le code utilisé sur les tableaux on obtient le résultat suivant :

Je suis un point de coordonnées 0 2


Je suis un point couleur de couleur 2, de coordonnées 1 5
Je suis un point couleur de couleur 4, de coordonnées 2 3
Je suis un point de coordonnées 1 2

4.4.2 Règles du polymorphisme en Java


Compatibilité. Il existe une conversion implicite d’une référence à un objet de classe T à un
objet d’une classe parente de T.

Liaison dynamique. Lors d’un appel d’une méthode a.f() où a est supposé être de type d’une
classe T, le choix de f est déterminé :
— A la compilation : on détermine dans la classe T ou ses ascendants la signature de la
meilleure méthode f() convenant à l’appel, ce qui défini du même coup le type de la valeur
de retour.
— A l’exécution : on cherche la méthode f de syntaxe et de type de retour voulu à partir de la
classe correspondante au type effectif de l’objet référencé par a (de type T ou ascendant).
Si cette classe ne comporte pas de méthode appropriée on remonte le plus possible jusqu’à
ce qu’on en trouve une.

4.5 La super-classe Object


En Java, toute classe définie hérite de la classe Object. Une variable de type Object peut être
utilisée pour référencer un objet de type quelconque.

Exemple

Point p = new Point(1,3);


Object o;
4.5. LA SUPER-CLASSE OBJECT 79

o = p;
((Point)o).affiche();
Point p1 = (Point) o;
p1.affiche();

Attention. L’instruction o.affiche(); provoque une erreur car il n’y a pas de méthode affiche
définie dans la classe Object. Il faut alors faire une conversion (cast) explicite comme le montre
l’exemple ci-dessus.

La classe Object possède quelques méthodes, dont notamment :


— la méthode public String toString() qui renvoie, par défaut, une chaı̂ne de caractères
contenant le nom de la classe et l’adresse mémoire de l’objet (adresse précédée de @).
Point a = new Point(1,2);
System.out.println(a.toString()); //affiche Point@fc1aedf
System.out.println(a); //équivalent
La méthode toString définie par défaut le chaı̂ne à afficher lorsque l’on veut afficher un
objet. Plutôt que de définir une méthode d’affichage on redéfinie la méthode toString dans
la classe souhaitée puis on affiche directement l’objet.
public class Point
{
private int x,y;
public Point(...){...}
...
public String toString()
{
System.out.println("Je suis un point de coordonnées "+x+" "+y);
}
}

...

Point p = new Point(0,2);


...
System.out.println(p);
— La méthode public boolean equals(Object o) se content de comparer les adresses mémoire
des objets référencés.
Object o1 = new Point(1,2);
Object o2 = new Point(1,2);
o1.equals(o2); //renvoie false comme réponse
o1.equals(o1); //renvoie true comme réponse
Il est également possible de rédéfinir/surcharger la méthode equals dans ses propres classes :
public class Point
{
private int x,y;

public Point(...){...}

...

public boolean equals(Point p)


{
return ((p.getX()==x) && (p.getY()==y));
}
}
80 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME

4.6 Accès par membres protégés : protected


En plus des droits d’accès public et private et du droit par défaut lié à la notion de paquetage,
il existe un autre droit d’accès : protected.
Un membre protected est accessible à des classes du même package ainsi qu’à leur classe
dérivées. En pratique, ce droit d’accès est assez peu utilisé.

4.7 Méthodes et classes finales


Une méthode déclarée final ne peut pas être redéfinie dans une classe dérivée.

Une classe déclarée final ne peut plus être dérivée.

4.8 Les classes abstraites


Une classe abstraite est une classe qui ne permet pas d’instancier des objets, elle ne peut servir
que de classe de base pour une dérivation.

Exemple
public abstract class A
{
public void f()
{
... // méthode complètement sécifiée dans A
}

//méthode abstraite pas définie dans A


public abstract void g(int n);
}
Une classe abstraite possède au moins une méthode abstraite, utilisation du mot clé abstract.
Il est possible de créer une référence de type A (A a;), mais on ne peut pas créer d’objet de type
A (A a = new A(); provoque une erreur).
En revanche, on crée une classe B qui dérive de A en définissant g.
public class B extends A
{
public void g(int n)
{
...
}
}
Ensuite on peut créer des objets de type B (B b = new B();).
Une classe dérivée d’une classe abstraite n’est pas obligée de redéfinir toutes les méthodes
abstraites de sa classe de base. Dans ce cas c’est une classe abstraite elle aussi, il faut alors penser
à utiliser le mot clé abstract dans sa définition.
Une méthode abstraite est toujours publique.

Intérêt.
— Permet de spécifier toutes les fonctionnalités que l’on souhaite disposer dans les classes
dérivées.
— Permet d’exploiter le polymorphisme en étant sûr que certaines méthodes existes.
4.8. LES CLASSES ABSTRAITES 81

Exemple.
abstract class Affichable
{
abstract public void affiche();
}

class Entier extends Affichable


{
private int valeur;

public Entier(int n)
{ valeur = n; }

public void affiche()


{
System.out.println("Je suis un entier de valeur "+valeur);
}
}

class Flottant extends Affichable


{
private float valeur;

public Flottant(float x)
{ valeur = x; }

public void affiche()


{
System.out.println("Je suis un flottant de valeur "+valeur);
}
}

public class Test


{
public static void main(String [] args)
{
Affichable [] tab;
int i; /*compteur*/

tab = new Affichable[3];


tab[0] = new Entier(25);
tab[1] = new Flottant(1.25f);
tab[2] = new Entier(42);

for(i=0 ; i<3 ; i++)


tab[i].affiche();
}
}
Si on exécute le code contenu dans la méthode main, on obtient le résultat suivant :

Je suis un entier de valeur 25


Je suis un flottant de valeur 1.25
Je suis un entier de valeur 42
82 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME

4.9 Les interfaces


Une interface est une notion correspondant à une classe abstraite où aucune méthode n’est
implémentée.

4.9.1 Définition et mise en œuvre


public interface I
{
void f(int n); //public et abstract sont facultatifs
void g();
}

public class A implements I


{
//A doit redéfinir f et g
}

— Une même classe peut implémenter plusieurs interfaces.

public interface I2
{
int h();
}

class B implements I,I2


{
//redéfinition de f et g de I et h de I2
}

— Les interfaces peuvent contenir des constantes de type static final. Ces constantes seront
donc accessibles en dehors d’une classe implémentant l’interface.
— Les interfaces peuvent se dériver, mais les classes dérivées obtenues sont aussi des interfaces.

interface I1
{
static final int MAXI = 20;
void f(int n);
}

interface I2 extends I1
{
void g();
}

class A implements I1
{
//redéfinit f, on a accès à MAXI directement
//par exemple if(i < MAXI) ...
}

class B extends A implements I2


{
//redéfinition de g
}
4.10. CONNAÎTRE LA CLASSE ET LES TYPES D’UN OBJET 83

On peut ainsi écrire les choses suivante d’où on veut :

I1 i = new A();
I1 i2 = new B();
I2 b = new B();
System.out.println("Constante "+I1.MAXI);

4.9.2 Intérêt des interfaces


— Une classe peut implémenter plusieurs interfaces, alors qu’une classe ne peut dériver que
d’une seule classe (éventuelle abstraite).
— La notion d’interface se superpose à la notion de dérivation et ne s’y substitue pas.
— On peut utiliser des variables de type d’interfaces.
— Les interfaces peuvent se dériver.
Les interfaces permettent notamment de fournir une solution au problème de l’héritage mul-
tiple.

Graveur

graver()

GraveurCD GraveurDVD

graver() graver()

Combo

Problème de l’héritage multiple : quelle méthode graver s’exécute sur le combo ?

En Java il n’y a pas d’héritage multiple : on ne peut hériter que d’une seule classe. On contourne
alors le problème en utilisant des interfaces. Dans l’exemple du dessus, une solution pourrait être
de créer une classe Combo qui hérite de Graveur et qui implémente l’interface GraveurCD possédant
une méthode graverCD et l’interface graverDVD possédant une méthode graverDVD.

4.10 Connaı̂tre la classe et les types d’un objet


4.10.1 L’opérateur instanceof
C’est un opérateur booléen qui renvoie true lorsque qu’un objet peut être converti en type défini
par une classe. Cette classe peut correspondre à une classe (abstraite ou non) située au dessus de
la classe de la référence de l’objet dans la hiérarchie d’héritage ou une interface implémentée par
l’une des classes mères ou la classe propre de l’objet.
84 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME

Exemple d’utilisation

if(objetA instanceof classeB)


{
//renvoie true si objetA peut-etre converti dans
//le type classeB
}

Un autre exemple, dans le cas de redéfinition de equals

public boolean equals(Object o) {


if (o instanceof MyClass) {
MyClass mc = (MyClass) o;
if (mc == null) // ne peut pas etre nul ici
return false;
else
return ... // il faudrait comparer les champs de mc
}
return false;
}

4.10.2 La class Class


Les instances de cette classe (qui n’est pas sous-classable, c’est-à-dire dont une spécification
est final) sont utilisées dans une application en cours d’exécution dans une JVM pour représenter
les différentes classes ou interfaces utilisées par l’application. Il en va de même des tableaux et des
types primitifs du langage. Cette classe n’a pas de constructeurs : les instances sont construites
automatiquement par la JVM lors du chargement des classes.
Si on se réfère par exemple à la classe Object la méthode getClass permet d’accéder à la
classe (référence sur Class) associée et à partir d’une telle référence des informations relatives à
cette classe peuvent être obtenues par l’intermédiaire des méthodes de cette classe. Parmi celles-ci
citons :
— Class[ ] getClasses( ) : renvoie un tableau contenant toutes les classes publiques et
interfaces ;
— String getName( ) : renvoie sous forme de chaı̂ne le nom de l’entité (classe, interface,
tableau ou type primitif) correspondante ;
— Class getSuperclass( ) : renvoie une référence sur la sur-classe ;
— boolean isInstance(Object obj) : renvoie true si la référence est compatible avec la
classe courante, ce qui est similaire à l’utilisation de l’opérateur instanceof.

Voici un petit exemple d’utilisation :

class AAAA { int a; }

class BBBB { }

class CCCC extends AAAA{ }

class Class1{
public static void main(String[ ] arg){
AAAA a = new AAAA( );
CCCC c = new CCCC( );
int[ ] t = new int[10];
Object[ ] tabObj = new Object[5];
BBBB[ ] tabB = new BBBB[33];
4.11. EXERCICES DE COURS 85

System.out.println("classe de a : " + a.getClass( ).getName( ));


System.out.println("classe de c : " + c.getClass( ).getName( ));
System.out.println("classe de t : " + t.getClass( ).getName( ));
System.out.println("classe de tabObj : " + tabObj.getClass( ).getName( ));
System.out.println("classe de tabB : " + tabB.getClass( ).getName( ));
}
}

Voici le résultat

classe de a : AAAA
classe de c : CCCC
classe de t : [I
classe de tabObj : [Ljava.lang.Object;
classe de tabB : [LBBBB;

D’autres utilisations de cette classe sont possibles, notamment pour la création et l’utilisation
de classes dynamiques. Ceci sort du cadre de ce cours, mais pour plus d’informations vous pouvez
consulter la documentation de Sun et jeter un coup d’oeil à la dernière partie du dernier chapitre.

4.11 Exercices de Cours

4.11.1 Classes abstraites - classes concrètes

L’idée de cet exercice est de trouver une application concrète à toute définition abstraite.
Plusieurs classes sont listées dans la colonne du milieu. L’objectif est d’imaginer des applications
dans lesquelles la classe listée pourrait être abstraite et des applications où elle pourrait être
concrète. Quelques exemples sont fournis pour aider à démarrer. Par exemple, la classe arbre
serait abstraite dans un programme pépinière où les différences entre un chêne et un peuplier sont
importantes. Mais dans un programme de simulation de golf, la classe arbre peut être concrète
(par exemple une sous-classe d’obstacle), parce qu’il n’y a aucun besoin de différencier les arbres.
86 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME

Concrète Classe Abstraite


simulation de parcours de golf Arbre application de pépinière

Maison application d’architecte

application de photo satellite Ville

JoueurDeFootball application d’entraı̂nement

Chaise

Client

Commande

Livre

Magasin

Fournisseur

ClubDeGolf

Carburateur

Four

4.11.2 Compilateur
Voici un bout de programme Java incomplet :

public class MonstreTest{


public static void main(String [] args)
{
Monstre [] ma = new Monstre[3];
ma[0] = new Vampire();
ma[1] = new Dragon();
ma[2] = new Monstre();
for(int x = 0; x < 3; x++){
ma[x].fairePeur(x);
}
}
}

class Monstre{
// -- méthode A --
}

class Vampire extends Monstre{


// -- méthode B --
}

class Dragon extends Monstre{


4.11. EXERCICES DE COURS 87

boolean fairePeur(int degre){


System.out.println("Souffler du feu");
return true;
}
}
Si l’on suppose que le programme produit le résultat ci-dessous :
>java MonstreTest
mordre ?
souffler du feu
arrrgh
Quelles paires de méthodes A et B peuvent être utilisées dans les classes Monstre et Dragon
(respectivement) pour produire le résultat ci-dessus ?
1. A: boolean fairePeur(int d){
System.out.println("arrrgh");
return true;
}
B: boolean fairePeur(int x){
System.out.println("mordre ?");
return false;
}
2. A: boolean fairePeur(int d){
System.out.println("arrrgh");
return true;
}
B: int fairePeur(int f){
System.out.println("mordre ?");
return 1;
}
3. A: boolean fairePeur(int d){
System.out.println("arrrgh");
return false;
}
B: boolean effrayer(int x){
System.out.println("mordre ?");
return true;
}
4. A: boolean fairePeur(int z){
System.out.println("arrrgh");
return true;
}
B: boolean fairePeur(byte b){
System.out.println("mordre ?");
return true;
}
Réponse :
1. Cet ensemble fonctionne, false est une valeur de retour acceptable.
2. Cet ensemble ne compile pas. La méthode fairePeur() de Vampire n’est ni une redéfinition
légale, ni une surcharge légale de la méthode fairePeur() de Monstre. Une redéfinition
de méthode ne peut pas modifier les arguments ni le type de retour, et ne changer QUE le
type ne suffit pas à rendre une surcharge valide.
88 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME

3. Cet ensemble compile mais ne produit pas le bon résultat parce que la méthode fairePeur()
de la classe Monstre n’est pas redéfinie dans la classe Vampire.
4. Cet ensemble compile mais ne produit pas le bon résultat parce que la méthode fairePeur()
de Vampire accepte un byte alors qu’il faut un int.
Les ensembles 3 et 4 produisent en fait le résultat suivant :

4.11.3 Qui suis-je ?


Associer à chaque ligne ci-dessous un ou plusieurs mots parmi : superclasse, sous-classe,
tableau, ArrayList (cf section 8.3.2), objet, tableau polymorphe, argument polymorphe,
interface, méthode abstraite, classe abstraite.
— Je suis incapable de grandir :
— Je dis quoi faire, pas comment le faire :
— J’accepte le mélange de chiens et de chats :
— J’autorise les références hétérogènes :
— Je peux dire si je suis vide :
— Ma vie est une existence plus spécialisée :
— Mon contenu est toujours homogène :
— Je peux rester floue sur certains détails :
— Je peux recevoir des choses que je n’ai jamais vues :
— Les autres me regardent avec respect :
— Je profite du travail d’une autre :
— Faites ce que je dis, pas ce que je fais :
— Je peux avoir un aspect différent pour différentes personnes :
— J’ai oublié d’où vous veniez :
— Rien de nouveau :

4.11.4 Vrai ou Faux ?


Pour répondre à certaines affirmations, il peut être utile de consulter les sections 8.1,8.2
1. Pour utiliser la classe Math il faut d’abord créer une instance.
2. On peut marquer un constructeur avec le mot clé static.
3. Les variables statiques n’ont pas accès à l’état des variables d’instance de l’objet this.
4. C’est une bonne pratique d’appeler une méthode statique avec une variable de référence.

5. Les variables statiques peuvent servir à compter les instances d’une classe.
6. Les constructeurs sont appelés avant que des variables statiques ne soient initialisées.
7. MAX SIZE serait un bon nom pour une variable statique finale.
8. Un bloc initialisateur statique s’exécute avant un constructeur de la classe.
9. Si une classe est finale, toutes ses méthodes doivent être finales.
10. Une méthode finale ne peut être redéfinie que si la classe est étendue.
11. Il n’y a pas de classe enveloppe pour les booléens.
12. On utilise une classe enveloppe pour traiter une valeur primitive comme un objet.
13. Les méthodes parseXXX retournent toujours une chaı̂ne de caractères.
Chapitre 5

La gestion d’exceptions

5.1 Présentation
La gestion d’exceptions permet :
— de détecter une anomalie et de la traiter indépendamment de sa détection,
— de séparer la gestion des anomalies du reste du code.
Une anomalie peut être due, par exemple, à des données incorrectes, à un fin de fichier
prématurée, à un événement non prévu par le programmeur.

Exemple. On dispose d’une classe Point pour laquelle on ne veut pas gérer des données négatives.
Nous lançons une exception lorsque les coordonnées sont négatives par l’instruction throw. Pour
commencer nous créons un objet donc le type sert à identifier l’exception concernée. Une classe
d’exception se définit en héritant de la classe Exception déjà définie dans l’API java.

public class ErreurCoordonnees extends Exception


{ }

Ensuite, pour “lancer” une exception nous utiliserons :

throw new ErreurCoordonnees();

Avant cela il faut indiquer le type d’exceptions qui peuvent être rencontrées dans les méthodes
de la classe Point. On ajoute alors throws ErreurCoordonnees dans la déclaration des méthodes
qui peuvent rencontrer ces exceptions. Voici un exemple :

public class Point


{
private int x,y;

public Point(int x,iny y) throws ErreurCoordonnees


{
if(x<0 || y<0)
throw new ErreurCoordonnes();

this.x = x;
this.y = y;
}

public void affiche()


{
System.out.println("Je suis un point de coordonnées "+x+" "+y);

89
90 CHAPITRE 5. LA GESTION D’EXCEPTIONS

}
}
Ensuite pour récupérer une exception lancée par une méthode, on utilise les instructions try
et catch :
public class TestExcept
{
public static void main(String args[])
{
try{ //Instructions qui peuvent voir une exception se déclencher
Point a = new Point(1,4);
a.affiche();
a = new Point(-3,5);
a.affiche();
}
catch(ErreurCoordonnees e)
{ //gestion de l’exception
System.err.println("Erreur de construction");
System.exit(-1);
}
}
}
Résultat.
Je suis un point de coordonnées 1 4
Erreur de construction
Attention : le bloc catch doit suivre immédiatement le bloc try.

5.2 Gestion de plusieurs exceptions et transmission d’infor-


mations
Exemple.

class ErreurCoord extends Exception


{
public int abs,ord;

public ErreurCoord(int abs,int ord)


{
this.abs = abs;
this.ord = ord;
}

public int getAbs()


{
return abs;
}

public int getOrd()


{
return ord;
5.2. GESTION DE PLUSIEURS EXCEPTIONS ET TRANSMISSION D’INFORMATIONS 91

}
}

class ErreurCoord extends Exception


{
private int depx,depy;

public ErreurDepl(int depx,int depy)


{
this.depx = depx;
this.depy = depy;
}

public int getDepx()


{
return depx;
}

public int getDepy()


{
return depy;
}
}

public class Point


{
private int x,y;

public Point(int x,iny y) throws ErreurCoordonnees


{
if(x<0 || y<0)
throw new ErreurCoordonnes(x,y);

this.x = x;
this.y = y;
}

public void deplace(int dx,int dy) throws ErreurDepl


{
if((x+dx<0) || (y+dy<0))
throw new ErreurDepl(dx,dy);

x+=dx;
y+=dy;
}

public void affiche()


{
System.out.println("Je suis un point de coordonnées "+x+" "+y);
}
}
92 CHAPITRE 5. LA GESTION D’EXCEPTIONS

public class TestExcept


{
public static void main(String args[])
{
try{ //Instructions qui peuvent voir une exception se déclencher
Point a = new Point(1,4);
a.affiche();
a.deplace(-3,5);
a = new Point(-3,5);
a.affiche();
}
catch(ErreurCoord e)
{ //gestion de l’exception
System.err.println("Erreur de construction"+e.getAbs()+" "+e.getOrd());
System.exit(-1);
}
catch(ErreurDepl e)
{
System.err.println("Erreur de deplacement "+e.getDepx()+" "+e.getDepy());
System.exit(-1);
}
//l’execution se poursuit ici s’il n’y a pas d’erreurs
System.out.println("Fin du programme");
}
}
Résultat.
Je suis un point de coordonnées 1 4
Erreur de deplacement -3 5

Transmission de messages On peut transmettre un message au gestionnaire d’exception en


utilisant un constructeur sans argument et la méthode getMessage héritée de la classe Exception.
Par exemple, avec la classe Point :
public class ErreurCoord
{
public ErreurCoord(String msg)
{
super(msg);
}
}

public class Point


{
...
public Point(int x,int y)
{
if(x<0 || y<0)
throw new ErreurCoord("Erreur dans le constructeur de Point avec
"+x+" "+y);

this.x=x;
this.y=y;
}
5.3. DÉRIVATION ET REDÉCLENCHEMENT D’EXCEPTIONS 93

...
}

public class TextExcept3


{
public static void main(String args[])
{
try{
...
}
catch (ErreurCoord e)
{
System.out.println(e.getMessage());
System.exit(-1);
}
...
}
}

5.3 Dérivation et redéclenchement d’exceptions


Il est possible de dériver des classes d’exceptions.
class ErreurPoint extends Exception { ... }
class ErreurCoord extends Exception { ... }
class ErreurDepl extends Exception { ... }
Lorsque l’on souhaite récupérer les exceptions il faut le faire en ordre inverse par rapport à
l’ordre défini par la hiérarchie.
try{
...
}
catch(ErreurCoord e)
{
...
}
catch(ErreurDepl e)
{
...
}
catch(ErreurPoint e)
{
...
}

Redéclenchement d’exceptions. Il est possible de déclencher une exception dans le traitement


d’une autre exception.
try{
...
}
catch(MonException e)
{
throw new Except();
94 CHAPITRE 5. LA GESTION D’EXCEPTIONS

}
Il est possible de redéclencher une exception pour la transmettre à un niveau englobant.
try{
...
}
catch(MonException e)
{
...
throw e;
}

Exemple complet.
class ErreurCoord extends Exception { }
class ErreurBidon extends Exception { }

public class Point


{
private int x,y;

public Point(int x,int y) throws ErreurCoor


{
if(x<0 || y<0)
throw new ErreurCoord();

this.x = x;
this.y = y;
}

public void f() throws ErreurCoord,ErreurBidon


{
try{
Point p = new Point(-3,2);
}
catch(ErreurCoord e)
{
System.err.println("ErreurCoord dans le catch de f");
throw new ErreurBidon();
}
}
}

public class Test


{
public static void main(String [] args)
{
try{
Point a = new Point(1,3);
a.f();
}
catch(ErreurCoord e)
{
System.err.println("ErreurCoord dans le catch de main");
5.4. LE BLOC FINALLY 95

}
catch(ErreurBidon e)
{
System.err.println("ErreurBidon dans le catch de main");
}
System.err.println("Après le bloc try-catch de main");
}
}

Résultat.

ErreurCoord dans le catch de f


ErreurBidon dans le catch de main
Après le bloc try-catch de main

5.4 Le bloc finally


Il est possible, en Java, d’introduire à la suite d’un bloc try, un bloc particulier d’instructions
qui seront toujours exécutées :
— soit après la fin normale du bloc try, si aucune exception n’a été déclenchée,
— soit après le gestionnaire d’exception (à condition que ce dernier n’est pas provoqué l’arrêt
de l’exécution).
Ce bloc est introduit par le mot clé finally et doit obligatoirement être placé après le dernier
gestionnaire.

try{
...
}
catch(Ex e)
{
...
}
finally{
...
}

Ici le même résultat aurait pu être obtenu en supprimant tout simplement le mot clé finally et
en mettant les instructions de ce bloc à la suite du gestionnaire.
Ce n’est pas la même chose dans ce cas :

void f() throws MonException


{
try{
...
}
finally{
...
}
}

Si une exception se produit dans f, on exécutera d’abord les instructions du bloc finally avant
de se brancher sur le gestionnaire d’exceptions.

De manière générale, le bloc finally peut s’avérer intéressant dans le cadre de l’acquisition
de ressources, c’est-à-dire tout ce qui nécessite une action pour la bonne bonne poursuite des
96 CHAPITRE 5. LA GESTION D’EXCEPTIONS

opérations (fermer un fichier, créer un objet, enlever un verrou, ...).

Autre cas.
try{
...
if(...)
break;
...
}
finally{
...
}
//suite
...
Si la commande break est exécutée, on exécute d’abord le bloc finally avant de passer à la suite.
Autre exemple possible.
try{
...
return 0;
}
finally{
...
return -1;
}
Ici la méthode semble devoir exécuter une instruction return 0;, mais il faut quand même exécuter
le bloc finally contenant à son tour return -1;. Dans ce cas c’est la dernière valeur qui sera
renvoyée (-1).

5.5 Les exceptions standard


Java fournit de nombreuses classe prédéfinies dérivées de la classe Exception qui sont utilisées
par certaines méthodes standard. Par exemple : IOException et ses dérivées sont utilisées par les
méthodes d’entrée/sortie.
D’autres exceptions sont utilisées par la JVM lors de situation anormales comme un indice de
tableau hors limites, une taille de tableau négative, une opération de lecture à la fin d’un fichier,
...
Il existe 2 catégories d’exception :
— les exceptions explicites (ou sous contrôle) correspondant à celles que nous venons d’étudier.
Elles doivent être traitées dans une méthode ou bien être mentionnées par la clause throws.
— les exceptions implicites (ou hors contrôle) n’ont pas à être mentionnées dans une clause
throw et on n’est pas obliger de les traiter (mais il est quand même possible de le faire).

Exemple de traitement.
public class TestExcept
{
public static void main(String [] args)
{
try
{ //on ne fait pas de vérification sur les arguments pour alléger le code
int t[];
5.6. EXERCICES DE COURS 97

int n,i;
n = Integer.parseInt(args[0]);
System.out.print("taille voulue "+n);
t = new int[n];
i = Integer.parseInt(args[1]);
System.out.print(" indice : "+i);
t[i] = 12;
System.out.println(" *** fin normale");
}
catch(NegativeArraySizeException e)
{
System.out.println("Exception de taille de tableau négative "+e.getMessage());
}
catch(ArrayIndexOutOfBoundsException e)
{
System.out.println("Exception indice de tableau "+e.getMessage());
}
}
}

Il existe beaucoup d’exceptions et les traiter toutes serait fastidieux, on traite celles qui sont
les plus pertinentes pour le problème à traiter.

5.6 Exercices de Cours


5.6.1 Vrai ou Faux ?
1. Un bloc try doit être suivi d’un bloc catch et d’un bloc finally :
2. Si on écrit une méthode susceptible de déclencher une exception vérifiée par le compilateur,
on doit envelopper le code dans un bloc try/catch :
3. Les blocs catch peuvent être polymorphes : .
4. Seules les exceptions vérifiées par le compilateur peuvent être interceptées :
5. Si on définit un bloc try/catch, le bloc finally correspondant est optionnel :
6. Si on définit un bloc try, on peut l’apparier avec un bloc catch ou un bloc finally, ou les
deux :
7. Si on écrit une méthode qui déclare qu’elle peut lancer une exception vérifiée par le compi-
lateur, on doit envelopper le code dans un bloc try/catch :
8. La méthode main dot gérer toutes les exceptions non gérées qui lui parviennent :
9. Un seul bloc try peut avoir plusieurs blocs catch différents :
10. Une méthode ne peut lancer qu’un seul type d’exception :
11. Un bloc finally peut s’exécuter sans qu’une exception soit lancée :
12. Un bloc finally peut exister sans bloc try :
13. Un bloc try peut exister seul, sans catch ni finally :
14. Lorsqu’on gère une exception on dit parfois qu’on l’esquive :
15. L’ordre des blocs catch n’a jamais d’importance :
16. Une méthode ayant un bloc try et un bloc finally peut éventuellement déclarer l’exception :

17. Les exceptions susceptibles de survenir à l’exécution doivent être gérées ou déclarées :
98 CHAPITRE 5. LA GESTION D’EXCEPTIONS

5.6.2 Mots croisés


1 2 3

4 5 6 7

8 9 10 11

12

13 14 15 16

17

18

19

20

21 22

23 24

HORIZONTALEMENT VERTICALEMENT
4 Donne une valeur (-) 1 A beaucoup de méthodes statiques (-)
6 Intercepte (-) 2 Déclare une exception (-)
8 Arbre généalogique (-) 3 Otées de la pile (-)
12 On en hérite (-) 5 Patron d’objet (-)
14 Recette de code (-) 6 Programmer (-)
18 Programme (-) 7 Avant le catch (-)
19 Le plus petit primitif (-) 9 Faire du nouveau (-)
20 Constante (-) 10 Transformerions en bytecode (-)
21 Lance (-) 11 Objet (-)
22 Non abstraite (-) 13 Pas un comportement (-)
23 Instances de classes (-) 15 Posent des conditions (-)
24 Gestion ou ... telle est la loi (-) 16 Erreur ou problème (-)
17 A des décimales (-)
Chapitre 6

Les chaı̂nes de caractères en Java

6.1 Le type String


6.1.1 Les chaı̂nes de caractères sont des objets
Voici plusieurs manières de créer des chaı̂nes :

ch1

String ch1 = new String();


String ch2 = new String("hello"); ch2 hello
String ch3 = new String(ch2);
String ch4 = "hello"; ch3 hello

ch4 hello
La dernière instruction est équivalente à la deuxième.
Les objets de type String ne sont pas modifiables

6.1.2 Affichage d’une chaı̂ne


Pour afficher une chaı̂ne sur la sortie standard on peut utiliser : System.out.println(ch2);
Pour afficher une erreur, on peut utiliser : System.err.println("Attention Erreur");

6.1.3 Longueur d’une chaı̂ne


String ch = "bonjour";
int l = ch.length();
Attention : pour les chaı̂nes on utilise une méthode pour obtenir la taille, d’où l’usage des
parenthèses (dans le cas de tableaux il s’agissait d’un champ !).

6.1.4 accès aux caractères


String ch = "bonjour";
char c1=ch.charAt(0); //caractère à la position 0 : le ’b’
char c2=ch.charAt(2); //caractère à la position 2 : le ’n’
Note : on peut intégrer des caractères spéciaux
String salutation="\tBonjour\n\t\tà tous";

99
100 CHAPITRE 6. LES CHAÎNES DE CARACTÈRES EN JAVA

6.1.5 Concaténation : l’opérateur +


String ch1 = "le langage"; ch1 le langage ch2 java
String ch2 = " java";
String ch3 = ch1 + ch2; ch3 le langage java
Lorsque l’opérateur + est utilisé avec au moins une chaı̂ne, les autres opérandes sont convertis
automatiquement en chaı̂ne.

int n=24;
int y=2005;
String d=new String("Nous le somme le " + n + " octobre " + y);

L’opérateur += est aussi utilisable :

String ch="bonjour ";


ch +=" monsieur";

Un nouvel objet contenant la chaı̂ne bonjour monsieur est créé et l’ancienne chaı̂ne bonjour de-
vient candidate au ramasse-miettes.

Autre exemple : à la fin on affiche : chiffres= 0123456789

String ch="chiffres= ";


for(int i=0;i<10;i++)
ch+=i;
System.out.println(ch);

6.1.6 Recherche dans une chaı̂ne : la méthode indexOf


Elle cherche à partir du début (ou la fin) d’une chaı̂ne ou d’une position donnée :
— soit la première position d’un caractère,
— soit la première occurrence d’une chaı̂ne.
Elle renvoie la position si une correspondance a été trouvée, -1 sinon.

String mot="anticonstitutionnellement"; //mot de 25 lettres


int n;
n=mot.indexOf(’t’); //renvoie la position 2
n=mot.lastIndexOf(’t’); //renvoie la position 24 (dernière occurrence)
n=mot.lastindexOf("ti"); //renvoie la position 12
n=mot.indexOf("ti"); //renvoie la position 2
n=mot.indexOf(’x’); //renvoie la position -1

n=mot.indexOf("ti",6); // recherche à partir de la position 6, renvoie 8


n=mot.lastIndexOf(’t’,9); //recherche à partir de la position 9, renvoie 24

6.1.7 La comparaison de chaı̂nes


Les chaı̂nes étant des objets, les opérateurs == et ! = testent les adresses des objets et non le
contenu. On ne peut pas les utiliser directement pour comparer des chaı̂nes.

Tester l’égalité de deux chaı̂nes


ch1.equals(ch2) renvoie true si le contenu de ch1 est le même que le contenu de ch2 et f alse
sinon.
6.1. LE TYPE STRING 101

Comparer deux chaı̂nes


ch1.compareTo(ch2) renvoie
— un entier négatif si ch1 est située avant ch2
— un entier nul (0) si les 2 chaı̂nes sont égales
— un entier positif si ch1 est située après ch2
L’ordre utilisé est l’ordre classique sur les caractères défini par la table ascii (correspondant à
l’ordre alphabétique pour les lettres).

ch1 ch2 ch1.compareTo(ch2)


bonjour monsieur <0
bonj monsieur <0
prix12 prix10 >0
Element élement <0
Element element <0
element élément >0
bonjour bonjour 0
monsieur bonjour >0

6.1.8 Modification d’un caractère


String ch="bonjour";
String ch1=ch.replace(’o’,’a’);

Attention un nouvel objet est créé, référencé par ch1, on aurait pu écrire ch=ch.replace(’o’,’a’);

6.1.9 Extraction de sous-chaı̂nes


String ch="bonjour";
String ch1=ch.substring(3); // ch1 contient "jour"
String ch2=ch.substring(2,4); // ch2 contient "njo"

Notez ici une exception sur le nom de la méthode.

6.1.10 Passage en majuscules/minuscules


String ch1=ch.toLowerCase(); // passage en minuscule
String ch2=ch.toUpperCase(); // passage en majuscule

6.1.11 Conversion d’une chaı̂ne vers un type primitif


Vous pouvez utiliser les méthodes explicites suivantes :
— Byte.parseByte(ch);
— Short.parseShort(ch);
— Integer.parseInt(ch);
— Long.parseLong(ch);
— Float.parseFloat(ch);
— Double.parseDouble(ch);
S’il y a un caractère qui pose problème lors de la conversion une exception est levée (BuildException).

6.1.12 Conversion d’un type primitif vers une chaı̂ne


On peut soit utiliser l’opérateur +, soit la méthode String.value()
102 CHAPITRE 6. LES CHAÎNES DE CARACTÈRES EN JAVA

float f=2.5f;
String ch0=""+f; // première possibilité
String ch1=String.value(f); //deuxième possibilité

6.1.13 Chaı̂nes et tableaux de caractères


— Convertir un tableau de caractères en chaı̂ne
char mot[]={’b’,’o’,’n’,’j’,’o’,’u’,’r’};
String ch=new String(mot);
— Convertir une chaı̂ne en tableau de caractères
String ch="bonjour";
char mot[]=ch.toCharArray();

6.1.14 Tableau de chaı̂nes


Le tableau args de la méthode main, contenant les arguments du programme, est un tableau
de chaı̂ne.

public static void main(String [] args)


{
System.out.println("Voici la liste des arguments");
for(int i=0; i < args.length; i++)
System.out.println("Argument "+String.value(i+1)+" de longueur
"+args[i].length()+" : " + args[i]);
}

Notez la différence entre args.length qui la longueur du tableau et args[i].length() qui est
longueur de la chaı̂ne à la position i du tableau.

6.2 La classe StringBuffer


La classe StringBuffer fait partie du package java.lang et contrairement à la classe String,
elle utilise un buffer de taille variable pour mémoriser une chaı̂ne de caractères modifiables. Cette
classe f inal maintient elle-même l’allocation d’espace supplémentaire pour mémoriser l’ajout de
caractères. Elle permet donc de manipuler des chaı̂nes modifiable. Voici la liste des méthodes de
cette classe :
— public StringBuffer() : construit une chaı̂ne vide
— public StringBuffer(int length) : construit une chaı̂ne de longueur length
— public StringBuffer(String str) : construit une chaı̂ne de type StringBuffer à partir
d’une chaı̂ne de caractères classique
— int length() : renvoie la taille de la chaı̂ne (comme dans String)
— void setCharAt(int index, char c) : modifie le caractère à la position index en le rem-
plaçant par c
— StringBuffer append(String str) : ajoute la chaı̂ne str en fin (possibilité de mettre un
tableau de caractères ou un objet au lieu d’une String)
— StringBuffer insert(int offset, String str) : insère une chaı̂ne à l’indice of f set
— String toString() convertit la chaı̂ne au type String
Pour plus d’informations, consulter la documentation de la classe StringBuffer.

6.3 La classe StringTokenizer


La classe StringTokenizer fait partie du package java.util (il faut importer le package si
vous voulez l’utiliser). Elle permet de décomposer une chaı̂ne de caractères en une suite de ”mots”
6.3. LA CLASSE STRINGTOKENIZER 103

séparés par des ”délimiteurs”.

Voici les constructeurs :


— StringTokenizer(String str, String delim, boolean returnDelims) : str est la chaı̂ne
à analyser, delim est une chaı̂ne contenant les délimiteurs reconnus, returnDelims indique
si les délimiteurs doivent être renvoyés comme parties de la chaı̂ne.
— StringTokenizer(String str) : crée un objet StringTokenizer, str est la chaı̂ne à ana-
lyser ; les délimiteurs sont les caractères espace, tabulation, retour chariot et changement
de ligne.
— StringTokenizer(String str, String delim) : crée un objet StringTokenizer, str est
la chaı̂ne à analyser, delim est une chaı̂ne contenant les délimiteurs reconnus. Par défaut,
les délimiteurs ne sont pas renvoyés comme éléments de la chaı̂ne.
La liste des méthodes :
— int countTokens() : calcul le nombre de fois que la méthode nextToken() peut être
appelée avant la génération d’une exception.
— boolean hasMoreElements() : retourne la même valeur que la méthode hasMoreTokens().
— boolean hasMoreTokens() : vérifie s’il n’y a plus de jetons disponibles à partir de l’objet
StringTokenizer.
— Object nextElement() : retourne la même valeur que la méthode nextToken().
— String nextToken() : retourne le prochain jeton à partir de l’objet StringTokenizer.
— String nextToken(String delim) : retourne le prochain jeton par rapport au délimiteur
spécifié.
Exemple :
StringTokenizer st = new StringTokenizer("C’est une ligne");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}

Ce bout de code provoque l’affichage de :


C’est
une
ligne

Autre exemple :
StringTokenizer st = new StringTokenizer("/home/toto/TP-JAVA","/-",false);
System.out.println("Il y a "+st.countTokens()+" éléments dans la cha^
ıne");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
Ce bout de code provoque l’affichage de :
home
toto
TP
JAVA
104 CHAPITRE 6. LES CHAÎNES DE CARACTÈRES EN JAVA
Chapitre 7

Les entrées/sorties

7.1 Les fichiers, la classe File


L’objet File constitue une représentation abstraite d’un chemin vers un fichier ou un répertoire.
Le séparateur de chemin dépend de la plateforme. Sous Unix la valeur de ce séparateur est ’/’
alors que sous Windows sa valeur est égale à ’\’.

7.1.1 Les champs


— static String pathSeparator : Ce champ représente le caractère de séparation par défaut
dépendant du système sous-jacent, sous la forme d’une chaı̂ne de caractères pour des raisons
de commodités.
— static char pathSeparatorChar : Ce champ représente le caractère de séparation de
chemin par défaut dépendant du système sous-jacent.
— static String separator : Ce champ représente le caractère de séparation par défaut
dépendant du système sous-jacent, sous la forme d’une chaı̂ne de caractères pour des raisons
de commodités.
— static char separatorChar : Ce champ représente le caractère de séparation par défaut
dépendant du système sous-jacent.

7.1.2 Les constructeurs


— File(File parent, String child) crée un nouvel objet File à partir d’un autre chemin
abstrait désignant le parent et d’une chaı̂ne de caractères indiquant un chemin enfant.
— File(String pathname) crée un nouvel objet File à partir d’un chemin donné sous la
forme d’une chaı̂ne de caractères.
— File(String parent, String child) crée un nouvel objet File à partir de deux chaı̂nes
de caractères désignant respectivement un chemin parent et un autre enfant.
— File(URI uri) crée un nouvel objet File en convertissant l’URI fourni en chemin abstrait.

7.1.3 Les méthodes


— boolean canRead() teste si l’application peut lire le fichier désigné par l’objet File.
— boolean canWrite() teste si l’application peut modifier le fichier désigné par le chemin
abstrait.
— int compareTo(File pathname) compare lexicographiquement deux objets File.
— int compareTo(Object o) compare l’objet File par rapport à un autre objet.
— boolean createNewFile() crée atomiquement un nouveau fichier vide désigné par le che-
min abstrait si et seulement si un fichier de même nom n’existe pas encore.

105
106 CHAPITRE 7. LES ENTRÉES/SORTIES

— static File createTempFile(String prefix, String suffix) crée un nouvel objet


File vide dans le répertoire temporaire par défaut en utilisant le préfixe et le suffixe donnés
pour générer son nom.
— static File createTempFile(String prefix, String suffix, File directory) crée
un nouvel objet File vide dans le répertoire spécifié en utilisant le préfixe et le suffixe donnés
pour générer son nom.
— boolean delete() supprime le fichier ou le répertoire désigné par l’objet File.
— void deleteOnExit() demande que le fichier ou le répertoire désigné par le chemin abstrait
soit supprimé lorsque la Machine Virtuelle Java s’arrête.
— boolean equals(Object obj) teste l’égalité de l’objet File par rapport à un autre objet.
— boolean exists() teste si le fichier désigné par le chemin abstrait existe.
— File getAbsoluteFile() retourne la forme absolue du chemin abstrait.
— String getAbsolutePath() retourne le chemin absolu sous la forme d’une chaı̂ne de ca-
ractères de l’objet File.
— String getName() retourne le nom du fichier ou du répertoire désigné par le chemin abs-
trait.
— String getParent() retourne le chemin parent, sous la forme d’une chaı̂ne de caractères,
de l’objet File, ou null si ce dernier n’a pas de parent.
— File getParentFile() retourne le chemin abstrait parent de l’objet File, ou null si ce
dernier n’a pas de parent.
— String getPath() convertit l’objet File vers un chemin sous forme de chaı̂ne de caractères.
int hashCode() cacule un hash code pour l’objet File.
— boolean isAbsolute() teste si le chemin abstrait est absolu.
— boolean isDirectory() teste si le fichier désigné par le chemin abstrait est un répertoire.
— boolean isFile() teste si le fichier désigné par le chemin abstrait est un fichier normal.
— boolean isHidden() teste si le fichier désigné par le chemin abstrait est un fichier caché.
— long lastModified() retourne le temps de la dernière modification du fichier donné par
le chemin abstrait.
— long length() Renvoie la longueur du fichier désigné par le chemin asbtrait.
— String[] list() retourne un tableau de chaı̂nes de caractères indiquant les fichiers et
répertoires dans le répertoire spécifié par le chemin abstrait.
— boolean mkdir() crée le répertoire désigné par le chemin abstrait.
— boolean mkdirs() crée le répertoire et éventuellement ses répertoires parents désignés par
le chemin abstrait.
— boolean renameTo(File dest) renomme le fichier défini par le chemin abstrait.
— boolean setLastModified(long time) fixe le temps de la dernière modification du fichier
ou du répertoire désigné par le chemin abstrait.
— boolean setReadOnly() marque le fichier ou le répertoire nommé par le chemin abstrait
de telle façon que des opérations en lecture seule sont autorisées.

7.2 Les flux


L’écriture et la lecture de fichiers impliquent l’utilisation de flux (streams). Ces derniers
représentent des canaux de transmission de données à partir d’une source ou vers une destination.
Dans ces canaux, deux types d’informations peuvent transiter de l’application vers un fichier
ou inversement. Il s’agı̂t de flux de données binaires ou de caractères. Cette distinction permet de
traiter différemment les fichiers textes dont l’extension peut être .txt, .ini, .log, .xml, etc., et les
fichiers binaires comme les images, vidéos, sons et autres...
Les flux sont également capables de faire transiter des informations provenant d’une entrée
standard (System.in) telle que le clavier, ou allant vers une sortie standard (System.out) comme
l’écran.
Ainsi, on distingue quatre familles de flux appelés également flots :
— Les flots en lecture ou d’entrée,
7.3. LECTURE/ÉCRITURE 107

— Les flots en écriture ou de sortie,


— Les flots binaires,
— Les flots de caractères.
Les flots d’entrée et de sortie peuvent être utilisés pour n’importe quel périphérique, à l’image
d’un écran, d’une imprimante, d’un fichier disque ou du réseau (sortie du programme) et d’un
clavier, d’une souris, d’un ordinateur distant ou d’un fichier disque (entrée du programme).
Il est possible de combiner les flux ci-dessus, de cette façon :
— Les flots binaires en lecture,
— Les flots binaires en écriture,
— Les flots de caractères en lecture,
— Les flots de caractères en écriture.
Les flux sont unidirectionnels, et possèdent donc seulement une entrée et une sortie. Ils ne
peuvent envoyer des informations que dans un seul et unique sens.
Les classes appropriées pour l’utilisation des flux se trouvent dans le paquetage java.io.
On distingue quatre classes principales dont toutes les autres héritent des représentations et
comportements propre à chaque famille.
— java.io.InputStream (flot binaire en lecture),
— java.io.OutputStream (flot binaire en écriture),
— java.io.Reader (flot de caractères en lecture),
— java.io.Writer (flot de caractères en écriture).

7.3 Lecture/écriture
Voici un exemple de classe effectuant une lecture/écriture d’un fichier. Dans un premier temps,
on lit le contenu d’un fichier nommé "poeme.txt" et on range chacune des lignes dans une structure
de type StringBuffer (cf le chapitre précédent pour ce type). Dans un second temps, on écrit
dans un fichier nommé "poeme copie.txt", on commence par y écrire Ma chaine puis un saut de
ligne, ensuite on écrit le contenu stocké dans un StringBuffer.

import java.io.*;
class GestionFichier {
public static void litEtEcrit(String args[]) {
StringBuffer contenu = new StringBuffer();
try{
//exemple de lecture
File cheminAbstraitEntree = new File("poeme.txt");
FileReader fluxLectureTexte = new FileReader(cheminAbstraitEntree);
BufferedReader tamponLecture = new BufferedReader(fluxLectureTexte);
String ligne;

while((ligne = tamponLecture.readLine()) != null){


System.out.print (ligne);
contenu.append(ligne);
contenu.append("\r\n");
}

tamponLecture.close();
fluxLectureTexte.close();
}
catch(IOException e)
{
System.err.println("Erreur de lecture");
}
108 CHAPITRE 7. LES ENTRÉES/SORTIES

try{
//exemple d’écriture
File cheminAbstraitSortie = new File("poeme_copie.txt");
FileWriter fluxEcritureTexte = new FileWriter(cheminAbstraitSortie);
BufferedWriter tamponEcriture = new BufferedWriter(fluxEcritureTexte);
tamponEcriture.write("Ma chaine\n");
tamponEcriture.flush();

tamponEcriture.write(contenu.toString());
tamponEcriture.flush();

tamponEcriture.close();
fluxEcritureTexte.close();
}
catch(IOException e)
{
System.err.println("Erreur d’écriture");
}
}
}

On commence par créer une variable de type File. Ensuite, dans le cas d’une lecture de fichier,
on crée une flux de type FileReader et ce flux est mis dans un tampon de type BufferedReader.
La lecture dans le tampon se fait à l’aide de la méthode readLine(). Pour l’écriture, on crée
un flux de type FileWriter, que l’on met dans un tampon BufferedWriter et l’écriture se fait
à l’aide de la méthode write. La méthode flush sert à vider le contenu du tampon. A chaque
fois, les différents flux doivent être d’abord ouverts (instructions new), puis fermés (instructions
close à la fin.

Note : pour limiter les accès au disque dur, java conserve les caractères devant être écrits dans
un fichier dans un tampon (buffer en anglais), puis écrit le contenu du tampon dans le fichier
lorsque le tampon est plein. Si l’on veut forcer l’écriture du contenu du tampon dans le fichier on
peut utiliser la commande flush. Il faut toujours appliquer flush au moins une fois juste avant
la fermeture du fichier.
Chapitre 8

Quelques éléments de l’API

8.1 La classe Math


La classe Math possède 2 constantes Math.PI (le nombre pi) et Math.E (exponentielle e1 ). Elle
possède également plusieurs méthodes statiques, en voici quelques-unes :
— Math.random() : retourne un nombre compris entre 0.0 et 1.0 (exclu).
— Math.abs(nombreEnArgument) : retourne la valeur absolue du nombre fourni en argument,
le type de retour correspond au type de l’argument.
— Math.round(decimalEnArgument) : arrondi à l’entier le plus proche (renvoie un int pour
un float et un long pour un double).
— Math.min(nombre1,nombre2) : renvoie le plus petit des 2 arguments.
— Math.max(nombre1,nombre2) : renvoie le plus grand des 2 arguments.
— Math.sin(nombreDouble) : renvoie le sinus du nombre, le type de retour est double. Les
fonctions cos, tan, acos, asin, atan sont également disponibles.
— Math.pow(nombre1,nombre2) : renvoie nombre1nombre2 , retour de type double.
— Math.log(nombreDouble) : renvoie le logarithme népérien d’un nombre, retour de type
double.
— Math.sqrt(nombreDouble) : renvoie la racine carré d’un nombre, retour de type double.
Il en existe quelques autres, à vous d’aller les consulter dans l’API.

8.2 Les classes enveloppes pour les valeurs primitives


Il est parfois nécessaire de traiter des valeurs primitives comme des objets, par exemple pour
les stocker dans des vecteurs (ArrayList, Vector, . . .), ensembles ou tables de correspondance
(HashMap). En effet, on ne peut pas ajouter un entier, par exemple, directement dans un ArrayList
puisque celui-ci ne peut contenir que des objets.
Il existe une classe enveloppe (un wrapper) pour chaque type primitif. Elles trouvent dans le
package java.lang, vous n’avez donc pas besoin de les importer. Elles sont faciles à reconnaı̂tre
parce que chacune d’elles est nommée d’après le type primitif qu’elle enveloppe, mais la première
lettre est une majuscule pour respecter la convention de nommage des classes. Voici les 8 classes
enveloppe qui existent, notez que la classe Integer correspond aux int, que la classe Character
aux char, et que les autres classes ont le même nom que leur type associé.
— Boolean
— Character
— Byte
— Short
— Integer
— Long
— Float

109
110 CHAPITRE 8. QUELQUES ÉLÉMENTS DE L’API

— Double
Voici maintenant comment faire pour envelopper une valeur et revenir au type primitif. On trans-
met la valeur au constructeur de la classe enveloppe.
int i = 145;
Integer valeurI = new Integer(i);
.
.
.
int valeurOriginale = valeurI.intValue();
Notez que toutes les classes fonctionnent de la même manière, Boolean a une méthode booleanValue(),
Character a une méthode characterValue(), etc.

Ces classes ont également des méthodes statiques très utiles comme Integer.parseInt().
De manière générale, les méthodes de transformation parseXXX() acceptent une valeur de type
String et retournent une valeur de type primitif.
String s = "2";
int x = Integer.parseInt(s);

double d = Double.parseDouble("420.24");

//Attention petite différence pour les booleens


//qui n’acceptent que des valeurs true/false
boolean b = new Boolean("true").booleanValue();
Notez que lorsqu’une chaı̂ne de caractère ne peut être convertie, une exception NumberFormatException
peut se produire. Elle se produit à l’exécution, vous n’êtes pas obligé de la gérer ou de la déclarer.
Nous pouvons également faire l’inverse : transformer un nombre en chaı̂ne de caractères. Il
existe 2 méthodes principales :
double d = 42.5;

String doubleString1 = "" + d;


String doubleString2 = Double.toString(d);

8.3 Les collections


8.3.1 Définition
Les principales structures de données des classes utilitaires (java.util) sont regroupées sous
une même interface Collection. Cette interface est implémenté par des ensembles, vecteurs dyna-
miques, tables associatives que nous allons voir dans cette partie. Les éléments de type collection
possèdent des méthodes pour ajouter, supprimer, modifier des éléments de type divers. Elles
peuvent être parcourues à l’aide d’itérateur (autrefois ceux-ci portaient le nom d’énumeration).
Nous verrons une illustration des différentes possibilités des collections lors des descriptions des
classes présentées ci-dessous implémentant l’interface Collection (nous ne verrons cependant pas
la classe LinkedList mais ses fonctionnalités sont proches de celles des autres classes).
Notez que lorsque que l’ordre des éléments est important la méthode compareTo (déjà vue lors
de la partie sur la classe Object) est utilisée.

8.3.2 ArrayList
Il s’agit d’une sorte de tableau d’objets de taille variable, fonctionnant comme une sorte de
liste. Il peut être vu comme un vecteur.
8.3. LES COLLECTIONS 111

Construction

ArrayList v = new ArrayList(); //vecteur dynamique vide

Ajout d’un élément

Object elem;
...
v.add(elem);

Taille de la liste On utilise la méthode size() qui renvoie le nombre d’éléments contenus dans
la liste.

Suppression d’un élément

Object o = v.remove(3); //supprime le 3eme élément que l’on obtient dans o


//l’element renvoyé est de type objet
//les éléments suivants sont décalés vers la gauche

v.removeRange(3,8); //supprime les éléments de rang 3 à 8

Accès aux éléments

ArrayList v = new ArrayList();


...
for(int i=0;i<v.size();i++)
{
System.out.println(v.get(i)+" ");
}
Syste.out.println();

Modifier un élément à l’indice i

ArrayList v = new ArrayList();


Object o;
...
Object o1 = v.set(i,o);

La méthode set renvoie la valeur de l’objet à l’indice i avant modification.

Parcours à l’aide d’un itérateur

ListIterator it = v.listIterator();
while(it.hasNext())
{
Object o = it.next();
...
}

Autes méthodes
— La méthode boolean contains(Object o) renvoie true si l’objet est dans la liste et false
sinon.
— boolean isEmpty() renvoie true si la liste est vide et false sinon.
— int indexOf(Object o) renvoie l’indice de l’objet o dans la liste ou -1 s’il n’est pas présent
112 CHAPITRE 8. QUELQUES ÉLÉMENTS DE L’API

L’ancienne classe Vector


Dans ses versions antérieures, on y trouvait une classe Vector permettant comme ArrayList
de manipuler des vecteurs dynamiques. Cette classe est synchronisée, c’est-à-dire que 2 processus
concurrents (threads) peuvent accéder au même vecteur.
Dans la version 5 de Java, la classe Vector a été fortement remaniée et offre de nouvelles
possibilités (à la fois en facilité d’écriture et en fonctionnalités). A vous de découvrir cette nouvelle
classe Vector.

8.3.3 Les ensembles


Deux classes implémentent la notion d’ensemble : HashSet et TreeSet. Un ensemble est une
collection non ordonnée d’éléments, un élément ne pouvant apparaı̂tre qu’au plus une fois. Nous
nous focaliserons plutôt ici sur la notion de HashSet.

Construction et parcours

HashSet e1 = new HashSet(); // ensemble vide


Hashset e2 = new HashSet(c); // ensemble contenant tous les elements d’une collection c

Un parcours d’un ensemble se fait à l’aide d’un itérateur.

HashSet e;
...
Iterator it = e.iterator();
while(it.hasNext())
{
Object o = it.next();
....
}

Ajout d’un élémént

HashSet e;
Object elem;
...
boolean existe = e.add(elem);
if(existe) Sytem.out.println(elem+" existe deja");
else Sytem.out.println(elem+" a ete ajoute");

Suppression

HashSet e;
Object elem;
...
boolean existe = e.remove(elem);
if(existe) Sytem.out.println(elem+" a ete supprime");
else Sytem.out.println(elem+" n’existe pas");

Autre possibilité.

HashSet e;
...
Iterator it = e.iterator();
it.next(); it.next();
it.remove();
8.3. LES COLLECTIONS 113

Opérations ensemblistes
— e1.addAll(e2) place dans e1 tous les éléments de e2.
— e1.retain(e2) garde dans e1 tout ce qui appartient à e2.
— e1.removeAll(e2) supprime de e1 tout ce qui appartient à e2.

Notion de Hachage Un ensemble HashSet est implémenté par une table de hachage, c’est-à-
dire que ses éléments sont stockés selon une position donnée. Cette position est définie selon un code
calculé par la méthode int hashCode() utilisant la valeur effective des objets. Les classes String
et File par exemple implémentent déjà cette méthode. Par contre les autres classes utilisent par
défaut une méthode dérivée de Object qui se content d’utiliser comme valeur la simple adresse
des objets (dans ces conditions 2 objets de même valeur auront toujours des codes de hachage
différents). Si l’on souhaite définir une égalité des éléments basés sur leur valeur effective, il faut
redéfinir la méthode hashCode dans la classe correspondante.
Voici une exemple avec la classe Point. La redéfinition de la méthode equals sert à définir
l’égalité entre les objets de manière à n’avoir qu’un seul élément dans un ensemble (pour rappel
dans un HashSet les éléments ne peuvent apparaitre qu’une seule fois).

import java.util.*;

class Point
{
private int x,y;

public Point(int x,int y)


{
this.x = x;
this.y = y;
}

public int hashCode()


{
return x+y;
}

public boolean equals(Object pp)


{
Point p = (Point) pp;
return ((this.x=p.getX()) && (this.y==p.getY()));
}

public String toString()


{
return "["+x+" "+"] ";
}

public int getX() { return x; }


public int getY() { return y; }
}

public class EnsPt


{
public static void main(String [] args)
{
Point p1 = new Point(1,3);
114 CHAPITRE 8. QUELQUES ÉLÉMENTS DE L’API

Point p1 = new Point(2,2);


Point p1 = new Point(4,5);
Point p1 = new Point(1,8);
Point [] p = {p1, p2, p1, p3, p4, p3};
HastSet ens = new HashSet();

for(int i=0; i<p.length; i++)


{
System.out.print("le point "+p[i]);
boolean ajoute = ens.add(p[i]);

if(ajoute) System.out.println(" a ete ajoute");


else System.out.println(" est deja present");

System.out.print("ensemble : ");
affiche(ens);
}
}

public static void affiche(HashSet ens)


{
Iterator iter = ens.iterator();
while(iter.hashNext())
{
Point p = (Point) iter.next();
System.out.print(p);
}
System.out.println();
}
}

Résultat.

le point [1 3] a ete ajoute


ensemble = [1 3]
le point [2 2] a ete ajoute
ensemble = [2 2] [1 3]
le point [1 3] est deja present
ensemble = [2 2] [1 3]
le point [4 5] a ete ajoute
ensemble = [4 5] [2 2] [1 3]
le point [1 8] a ete ajoute
ensemble = [1 8] [4 5] [2 2] [1 3]
le point [4 5] est deja present
ensemble = [1 8] [4 5] [2 2] [1 3]

Remarque la fonction de hachage est dictée par la simplicité, des points de coordonnées différentes
peuvent avoir la valeur de hashCode. Il faudrait trouver une fonction qui renvoie une valeur unique
pour chaque point.

8.3.4 Les algorithmes


Recherche de maximum ou minimum
import java.util.*;
8.3. LES COLLECTIONS 115

/*la methode compareTo est definie dans l’interface Comparable


class Point implements Comparable
{
private int x,y;

public Point(int x,int y)


{
this.x = x; this.y = y;
}

/*ordre simple defini sur la valeur de x*/


public int compareTo(Object pp)
{
(Point) p = (Point) pp;
if(this.x < p.getX()) return -1;
else if(this.x==p.getX()) return 0;
else return 1;
}

public String toString()


{
return "["+x+" "+y+"]";
}

public int getX() {return x;}


public int getY() {return y;}
}

public class MaxMin


{
public static void main(String [] args)
{
Point p1 = new Point(1,3);
Point p2 = new Point(2,2);
Point p3 = new Point(4,5);
Point p4 = new Point(1,8);
ArrayList l = new ArrayList();
l.add(p1); l.add(p2); l.add(p3); l.add(p4);

/*Max selon compareTo de Point*/


Point pMax1 = (Point)Collections.max(l); //max suivant compareTo
System.out.println("Max suivant compareTo "+pMax1);
}
}

Résultat.

Max suivant compareTo = [4 5]

Algorithme de tris et mélanges


Exemple complet. L’ordre utilisé est celui définit par la méthode compareTo ;

import java.util.*;
116 CHAPITRE 8. QUELQUES ÉLÉMENTS DE L’API

public class Tri


{
public static void main(String [] args)
{
int nb[] = {4, 9, 2, 3, 8, 1, 3, 5};
ArrayList t = new ArralyList();
for(int i = 0; i<nb.length; i++)
t.add(new Integer(nb[i]));
System.out.println("t initial = "+t);
Collections.sort(t); //tri des élements de t
System.out.println("t trie = "+t);
Collections.shuffle(t); //melange des élements de t
System.out.println("t melange = "+t);
Collections.sort(t,Collections.reverseOrder()); //tri des élements de t en ordre inverse
System.out.println("t trie inverse= "+t);
}
}

Résultat.

t initial = [4, 9, 2, 3, 8, 1, 3, 5]
t trie = [1, 2, 3, 3, 4, 5, 8, 9]
t melage = [9, 2, 1, 4, 3, 5, 3, 8]
t trie inverse= [9, 8, 5, 4, 3, 3, 3, 1]

8.3.5 Les tables associatives (HashMap)


Une table associative (ou de hachage) permet de conserver une information association deux
parties nommées clé et valeur. Elle est principalement destinée à retrouver la valeur associée à une
clé donnée. Les exemples les plus caractéristiques de telles tables sont :
— le dictionnaire : à un mot (clé) on associe une valeur qui est sa définition,
— l’annuaire usuel : à un nom (clé) on associe une valeur comportant le numéro de téléphone
et éventuellement une adresse,
— l’annuaire inversé : à un numéro de téléphone (qui devient la clé) on associe une valeur
comportant le nom et éventuellement une adresse.
Ces tables sont très utilisées lorsque l’on souhaite manipuler des données provenant de bases
de données : on peut associer à un enregistrement un ensemble de valeurs.

Ajout d’information

HashMap m = new HashMap(); //table vide


...
m.put("p",new Integer(3)); //ajoute à la table m un nouvel element
//associant la clé "p" (String) à la valeur 3 (Integer)

Si la clé fournie à put existe déjà, la valeur associée remplacera l’ancienne (une clé donnée ne
pouvant figurer qu’une seule fois dans la table). put fourni en retour soit l’ancienne valeur si la
clé existait déjà soit null.
Les clés et les valeurs doivent obligatoirement être des objets. Il n’est pas nécessaire que les
clés soient de même type, pas plus que les éléments, ce sera cependant souvent le cas pour des
raisons évidentes de facilité d’exploitation de la table.
8.3. LES COLLECTIONS 117

Recherche d’information

Object o = m.get("x");
if(o==null) System.out.println("Aucune valeur associée à la clé");

if(m.containsKey("p"))
Integer n = (Integer) m.get("p");

Suppression d’information

Object cle = "x";


Object val = m.remove(cle); //supprime cle+valeur de cle x
if(val!=null)
System.out.println("On a supprime l’element de cle "+cle+" et de valeur"+val);
else System.out.println("la clé "+cle+" n’existe pas");

Parcours d’une table : la notion de vue

HashMap m;
....
Set entrees = m.entrySet(); //entrees est un ensemble de paires
Iterator iter = entrees.iterator();
while(iter.hasNext())
{
Map.Entry entree = (Map.Entry) iter.next(); //paire courante
Object cle = entree.getKey(); //cle de la paire courante
Object valeur = entree.getValue(); //valeur de la paire courante
...
}

L’ensemble renvoyé par entrySet n’est pas une copie de la table, c’est une vue.

Autre vues
— L’ensemble des clés :
HashMap m;
...
Set cles = m.keySet();
— La collection des valeurs :
Collection valeurs = m.values();

Exemple complet

import java.util.*;

public class Map1


{
public static void main(String [] args)
{
HashMap m = new HashMap();
m.put("c","10"); m.put("f","20"); m.put("k","30");
m.put("x","40"); m.put("p","50"); m.put("g","60");
System.out.println("map initial "+m);

//retrouver la valeur associée à la clé "f"


String ch = (String) m.get("f");
118 CHAPITRE 8. QUELQUES ÉLÉMENTS DE L’API

System.out.println("Valeur associée à f "+ch);

//ensemble des valeurs


Collection valeurs =m.values();
System.out.println("Liste des valeurs initiales : "+valeurs);
valeurs.remove("30");//on supprime la valeur "30" par la vue associee
Sytem.out.println("liste des valeurs apres suppression : "+valeurs);

//ensemble des cles


Set cles = m.keySet();
System.out.println("liste des cles initiales : "+cles);
cles.remove("p"); //on supprime la cle "p" par la vue associee
System.out.println("liste des cles apres suppression : "+cles);

//modification de la valeur associee a la cle x


Set entrees = m.entrySet();
Iterator Iter = entrees.iterator();
while(iter.hasNext())
{
Map.Entry entree = (Map.Entry) iter.next();
String valeur = (String) entree.getValue();
if(valeur.equals("20"))
{
System.out.println("Valeur 20 trouvee en cle "+(String)entree.getKey());
iter.remove();
break;
}
}
System.out.println("map apres supression element 20 ");

//on supprime l’element de cle "f"


m.remove("f");
System.out.println("map apres suppression f: "+m);
System.out.println("liste des cles apres suppression f : "+cles);
System.out.println("liste des valeurs apres suppression de f : "+valeurs);
}
}
Résultat.
map initial : {c=10, x=40, p=50, k=30, g=60, f=20}
valeur associee a f : 20
liste des valeurs initiales : {10, 40, 50, 30, 60, 20}
liste des valeurs apres suppression : {10, 40, 50, 60, 20}
liste des cles initiales : [c, x, p, g, f]
liste des cles apres suppression : [c, x, g, f]
valeur associee a x avant modif : 40
map apres modif de x {c=10, x=25, g=60, f=20}
liste des valeurs apres modif de x : [10, 25, 60, 20]
valeur 20 trouvee en cle f
map apres supression element 20 : {c=10, x=25, g=60}
map apres suppression f : {c=10, x=25, g=60}
liste des cles apres suppression f : [c, x, g]
liste des valeurs apres suppresion de f : [10, 25, 60]
Note. Vous pourrez rencontrer la classe Hashtable qui est l’ancêtre de la classe HashMap.
8.4. DIVERS 119

8.4 Divers
8.4.1 Eléments disponibles à partir de Java 1.5
Il existe plusieurs instructions et structures qui ont été ajoutées à partir de la version 1.5. Nous
indiquons deux principales ici, à vous de découvrir les autres si vous souhaitez aller plus loin.

Boucle for améliorée


Une nouvelle boucle for a été ajoutée au language dans le but d’alléger l’écriture de deux
sortes de boucles fréquemment utilisées : le parcours d’un tableau et le parcours d’une collection.

— Si un tableau contient des éléments d’un certain type TypeElement (qui peut-être un type
primitif, un tableau ou une classe), la boucle
for(int i=0;i<tableau.length;i++)
{
lancerMethode(tableau[i]);
}
peut être écrite plus simplement :
for(TypeElement elt : tableau)
lancerMethode(elt);
— si liste est une structure de donnée capable d’être parcourue par un itérateur (c’est-à-dire
un objet de type Collection) et contenant toujours des élements de type TypeElement, la
boucle
for(Iterateur it=liste.iterator() ; it.hasNext() ; )
lancerMethode( (TypeElement) it.next());
peut être écrite plus simplement :
for(TypeElement elt: liste)
lancerMethode(elt);

8.4.2 Généricité
Si on souhaite définir une collection (vector, arraylist, ...) contenant un ensemble d’objets de
sorte que tous ces objets soient de type TypeObjet, on peut définir des collections ne contenant que
ce type d’objet en déclarant : List<TypeElement>. Ceci permet de vérifier que l’ajout d’éléments
à la liste sont bien de type TypeElement et que lors d’accès à des éléments de la liste on a la
certitude qu’ils sont bien de type TypeElement.
Un petit exemple.

Vector<TypeElement> v=new Vector<TypeElement>(); //déclaration


...
//on peut ajouter uniquement des objets de TypeElement
//ici monobjet doit etre obligatoirement de ce type
v.add(monobjet);
...
//on peut récupérer directement des objets TypeElement
for(int i=0;i<v.size();i++)
{
TypeElement e=v.get(i);
lancerMethode(e);
}

8.4.3 Réflection et Manipulation des types


Note : cette section sort du cadre de ce cours et est là uniquement à titre d’information. Le
120 CHAPITRE 8. QUELQUES ÉLÉMENTS DE L’API

contenu de cette section est tiré du polycopié Java d’Henri Garreta.

La classe java.lang.Class et les classes du paquet java.lang.reflect (aux noms évocateurs,


comme Field, Method, Constructor, Array, etc.) offrent la possibilité de pratiquer une cer-
taine introspection : un objet peut inspecter une classe, éventuellement la sienne propre, accéder
à la liste de ses membres, appeler une méthode dont le nom n’est connu qu’à l’exécution, etc. Une
instance de la classe java.lang.Class représente un type (c’est-à-dire un type primitif ou une
classe). Supposons que nous ayons une variable déclarée ainsi : Class uneClasse;
Nous avons plusieurs manières de lui donner une valeur :
— une classe-enveloppe d’un type primitif (Integer, Double, etc.) possède une constante de
classe TYPE qui représente le type primitif en question ; de plus, l’expression type.class
a le même effet. Ainsi, les deux expressions suivantes affectent le type int à la variable
uneClasse :
uneClasse = Integer.TYPE;
uneClasse = int.class;

— si uneClasse est l’identificateur d’une classe, alors l’expression uneClasse.class a pour


valeur la classe en question. L’exemple suivant affecte le type java.math.BigInteger à la
variable uneClasse :
uneClasse = java.math.BigInteger.class;
Notez la différence entre TYPE et class pour les classes-enveloppes des types primitifs :
Integer.TYPE est le type int, tandis que Integer.class est le type java.lang.Integer.

— on peut aussi demander le chargement de la classe, à partir de son nom complètement


spécifié. C’est un procédé plus onéreux que les précédents, ou la plupart du travail de char-
gement (dont à la détection d’éventuelles erreurs dans les noms des classes) était fait durant
la compilation alors que, dans le cas présent, il sera fait pendant l’excution. L’exemple sui-
vant affecte encore le type java.math.BigInteger à la variable uneClasse :
uneClasse = Class.forName("java.math.BigInteger");

— enfin, le moyen le plus naturel est de demander à un objet quelle est sa classe. L’exemple
suivant affecte encore le type java.math.BigInteger à la variable uneClasse :
Object unObjet = new
BigInteger("92233720368547758079223372036854775807");
...
uneClasse = unObjet.getClass();
Pour survoler cette question assez pointue, voici un exemple qui illustre quelques unes des
possibilités de e la classe Class et du paquet reflect. La méthode demoReflexion ci-dessous,
purement démonstrative, prend deux objets quelconques a et b et appelle successivement toutes
les méthodes qui peuvent être appelées sur a avec b pour argument, c’est-à-dire les méthodes
d’instance de la classe de a qui ont un unique argument de type la classe de b :
8.5. LES AUTRES ÉLÉMENTS DE L’API 121

import java.lang.reflect.Method;
import java.math.BigInteger;

public class DemoReflexion {


static void demoReflexion(Object a, Object b) {
try {
Class classe = a.getClass();
Method[] methodes = classe.getMethods();
for (int i = 0; i < methodes.length; i++) {
Method methode = methodes[i];
Class[] params = methode.getParameterTypes();
if (params.length == 1 && params[0] == b.getClass()) {
Object r = methode.invoke(a, new Object[] { b });
System.out.println( a + "." + methode.getName() + "(" + b + ") = " + r);
}
}
}
catch (Exception exc) {
System.out.println("Probl‘me : " + exc);
}
}

public static void main(String[] args) {


demoReflexion(new BigInteger("1001"), new BigInteger("1003"));
}
}

Le programme précédent affiche la sortie suivante :


1001.compareTo(1003) = -1
1001.min(1003) = 1001
1001.add(1003) = 2004
...
1001.gcd(1003) = 1
1001.mod(1003) = 1001
1001.modInverse(1003) = 501

8.5 Les autres éléments de l’API


A vous de consulter régulièrement l’API pour trouver les classes dont vous pouvez avoir besoin.
Sachez, par exemple, qu’il existe des classes pour traiter des dates, du son, . . .