Vous êtes sur la page 1sur 91

Support de cours

PROGRAMMATION ORIENTEE OBJET


EN JAVA
MOHAMED HAMMOUDA- OCA
SAMEH HBAIEB TURK
Tables des Matières

CHAPITRE 1 : INTRODUCTION A LA PROGRAMMATION ORIENTEE OBJET ..................................... 2

I. LA COMPLEXITE DU PROCESSUS DE DEVELOPPEMENT ........................................................ 2

II. LES APPROCHES DE PROGRAMMATION.............................................................................. 2

II.1. L’approche de programmation Procédurale (Fonctionnelle) .............................................. 2

II.2. L’approche de programmation Orientée Objet .................................................................. 3

III. LES PREMIERS CONCEPTS ORIENTES OBJETS ....................................................................... 5

III.1. Les Classes ........................................................................................................................... 5

III.1.1. Notion de classe .......................................................................................................... 5

III.1.2. Définition d’une classe ................................................................................................ 6

III.2. Les Objets ............................................................................................................................ 6

III.2.1. Notion d’Objet ............................................................................................................. 6

III.2.2. Définition d’un Objet ................................................................................................... 6

CHAPITRE 2 : LES BASE DE LA PROGRAMMATION ORIENTEE OBJET EN JAVA ................................ 8

I. LA STRUCTURE D’UNE CLASSE JAVA ................................................................................... 8

I.1. Déclaration du package ....................................................................................................... 9

I.2. Importation d’un package ................................................................................................... 9

I.3. Déclaration d’une classe.................................................................................................... 10

I.4. Définition d’une classe ...................................................................................................... 11

I.4.1. Attributs..................................................................................................................... 12

I.4.2. Méthodes .................................................................................................................. 12

I.4.3. Constructeurs ............................................................................................................ 12

I.4.4. Modificateur d’Accès ................................................................................................. 12

II. LA STRUCTURE D’UN FICHIER DE CODE SOURCE JAVA ....................................................... 13

II.1. Définition d’un fichier code source .java ........................................................................... 13

II.2. La classe exécutable java ................................................................................................... 13

CHAPITRE 3 : CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA .......................... 16


I. CYCLE DE VIE DES OBJETS JAVA ........................................................................................ 16

I.1. La naissance d’un objet en java ......................................................................................... 16

I.2. Accessibilité des objets...................................................................................................... 17

I.3. Le processus ramasse miette (gabraj collector) ................................................................ 17

II. LES METHODES DE CLASSE ............................................................................................... 18

II.1. Création des méthods avec des arguments et type de retour.......................................... 18

II.1.1. Type de retour d’une méthode ................................................................................. 18

II.1.2. LES PARAMETRES D’UNE METHODE ......................................................................... 19

II.2. La surcharge des méthodes ............................................................................................... 19

III. LES CONSTRUCTEURS....................................................................................................... 20

III.1. Les constructeurs définis par l’utilisateur ........................................................................ 21

III.2. La surcharge des constructeurs ......................................................................................... 22

III.3. Le constructeur par défaut ................................................................................................ 23

IV. L’ENCAPSULATION........................................................................................................... 23

IV.1. Modificateur d’accès : public ............................................................................................ 23

IV.2. Modificateur d’accès : protected ...................................................................................... 23

IV.3. Modificateur d’accès : default ........................................................................................... 24

IV.4. Modificateur d’accès : private ........................................................................................... 25

IV.5. Accès aux champs d’un objet ............................................................................................ 25

IV.6. Les accesseurs et les mutateurs ........................................................................................ 26

IV.7. Le besoin d’encapsulation ................................................................................................. 27

Chapitre 4 : LES ELEMENTS DU LANGAGE JAVA .......................................................................... 29

I. LES COMMENTAIRES........................................................................................................ 29

II. LES TYPES PRIMITIFS ........................................................................................................ 29

II.1. Entier et flottant ................................................................................................................ 29

II.2. Littéral de type numérique................................................................................................ 30

II.3. Caractère ........................................................................................................................... 30

II.4. Booléen.............................................................................................................................. 31
II.5. Compatibilité ascendante des types de variables en Java ................................................ 31

II.6. Les opérateurs ................................................................................................................... 31

II.7. Instruction conditionnelle ................................................................................................. 32

II.7.1. Instruction if .............................................................................................................. 33

II.7.2. Instruction if-else....................................................................................................... 33

II.7.3. Instruction switch ...................................................................................................... 34

II.8. Instructions répétitives...................................................................................................... 35

II.8.1. Instruction while ........................................................................................................ 36

II.8.2. Instruction do-while .................................................................................................. 36

II.8.3. Instruction for ............................................................................................................ 37

II.8.4. Instructions break et continue .................................................................................. 38

II.9. Portée des identificateurs ................................................................................................. 39

III. LES CHAINES DE CARACTERES : LA CLASSE STRING ............................................................ 40

III.1. Création d’une chaine de caractère .................................................................................. 40

III.2. Quelque opérations sur les chaines de caractères............................................................ 42

III.2.1. Informations sur la chaîne ......................................................................................... 42

III.2.2. Comparer deux chaînes ............................................................................................. 43

III.2.3. Extraire une sous-chaine ........................................................................................... 43

III.2.4. Modification d’une Chaine ........................................................................................ 43

III.3. La classe StringBuffer ........................................................................................................ 44

III.3.1. Concaténation ........................................................................................................... 46

III.3.2. Suppression ............................................................................................................... 46

III.3.3. Modification .............................................................................................................. 46

IV. LES TABLEAUX ................................................................................................................. 47

IV.1. Déclaration d’un tableau ................................................................................................... 48

IV.2. Allocation d’un tableau ..................................................................................................... 48

IV.3. Initialisation d’un tableau .................................................................................................. 49

IV.4. Les tableaux dynamiques .................................................................................................. 49


IV.4.1. La classe Vector ......................................................................................................... 50

IV.4.2. La classe Arraylist ...................................................................................................... 50

CHAPITRE 5 : L’HERITAGE DANS JAVA........................................................................................ 53

I. LE CONCEPT D’HERITAGE DES CLASSES ............................................................................. 53

I.1. Les avantages de l’héritage ............................................................................................... 55

I.1.1. Une définition plus réduite des classes dérivées ...................................................... 55

I.1.2. Une facilité de modification des propriétés et des comportements communs ....... 55

I.1.3. Utilisation d’un code testé de la classe de base ........................................................ 55

I.1.4. Concentration sur le comportement des classes dérivées ....................................... 55

I.1.5. Structuration et groupement logiques des caractéristiques et des comportements 55

I.1.6. Extensibilité ............................................................................................................... 55

I.2. L’accès aux membres hérités ............................................................................................ 56

I.3. Les membres hérités qui sont accessibles......................................................................... 56

I.4. Les membres hérités qui Ne sont pas accessibles............................................................. 57

II. LES CLASSES ABSTRAITES ................................................................................................. 57

III. LES INTERFACES .............................................................................................................. 58

IV. VARIABLES DE REFERENCE ET OBJETS REFERENCES DE TYPES DIFFERENTS ......................... 60

IV.1. Accés à un objet d’une classe dérivée de même type que sa variable de référence ...... 60

IV.2. Accés à un objet d’une classe dérivée à travers une variable de référence de type
superclasse .................................................................................................................................... 61

IV.3. Accès à un objet d’une classe dérivée à travers une variable de référence de type
interface ........................................................................................................................................ 62

IV.4. Pourquoi référencer des objets par des variables de références de types différents ...... 62

V. LE TRANSTYPAGE (CASTING) ............................................................................................ 63

V.1. Transtypage d’une variable de référence vers un autre type ........................................... 63

V.2. Le besoin de transtypage .................................................................................................. 64

VI. UTILISATION DE THIS ET SUPER POU R ACCEDER A DES OBJETS ET DES CONSTRUCTEURS ... 65

VI.1. La référence d'objet : this.................................................................................................. 65

VI.2. La référence d'objet: super ............................................................................................... 65


VII. LE POLYMORPHISME ....................................................................................................... 66

VIII. MECANISME DE RESOLUTION D’ACCES AUX METHODE ET VARIABLES ............................... 69

CHAPITRE 6 : LES EXCEPTIONS DANS JAVA................................................................................. 72

I. LES EXCEPTIONS DANS JAVA ............................................................................................ 72

I.1. Un avant-gout des exceptionS........................................................................................... 72

I.2. Pourquoi faut-il traiter les exceptions séparément .......................................................... 73

II. LA GESTION DES EXCEPTIONS........................................................................................... 74

II.1. Les différentes catégories des exceptions......................................................................... 75

II.1.1. Les exceptions vérifiées (Checked Exception) : ......................................................... 75

II.1.2. Les exceptions non vérifiées...................................................................................... 76

II.1.3. Les erreurs ................................................................................................................. 76

II.2. Lever une exception .......................................................................................................... 76

II.3. le gestionnaire des exceptions : les blocs try-catch-finally ............................................... 78

II.4. Gérer plusieurs exceptions ................................................................................................ 79

II.5. Les exceptions prédéfinies de java .................................................................................... 81

II.5.1. NullPointerException : ............................................................................................... 81

II.5.2. ArrayIndexOutOfBoundsException ........................................................................... 82

II.5.3. ClassCastException : .................................................................................................. 82

II.5.4. NumberFormatException : ........................................................................................ 83


PRESENTATION GENERALE

PRESENTATION GENERALE

Objectif Général
Ce cours est destiné aux étudiants du troisième niveau licence Informatique et leur permet de
découvrir les fondements de la Programmation Orientée Objet (POO). Le célèbre langage Java est
utilisé pour illustrer les concepts théoriques.
Ce cours est inspiré principalement des livres suivants :
• Edward Finegan ; Robert Liguori. OCA JAVA SE 7 Programmer I : Study guide 1.
• Mala Gupta. OCA JAVA SE7 Programmer I : Certification Guide 2 .
• Bruce Eckel. Thinking in JAVA 3.
Objectifs Spécifiques
A la fin de ce cours, vous serez capables de :
• comprendre les avantages de l’approche POO par rapport à l’approche de programmation
procédurale (classique) ;
• comprendre les deux concepts de base de la POO : les classes et les objets ;
• implémenter des classes java : déclaration des attributs, définition des méthodes, définition
de constructeur ;
• maîtriser les concepts d’encapsulation et de surcharge ;

• importer des classes de différentes packages dans une même application JAVA ;
• comprendre et mettre en œuvre le concept d’héritage ;
• factoriser le comportement commun des classes à travers des classes abstraites et des
interfaces ;
• utiliser le concept de polymorphisme pour des objets hétérogènes ; et
• comprendre l’intérêt de la gestion des exceptions et définir des classes d’exception
personnalisées.
Prérequis
Pour pouvoir suivre ce cours, vous devriez maîtriser les notions suivantes :
• Les types de base (Réel, entier, booléen, caractère).
• Les opérateurs sur les types de base : opérateurs arithmétiques, opérateurs de comparaison
et opérateurs logiques.
• Les types complexes (chaînes de caractères et tableaux).
• Les structures de contrôle (structures conditionnelles et structures itératives).
• La structure d’un programme principal.
• Les sous-programmes (fonctions et procédures).
• Les modes de passage des paramètres (passage par valeur, passage par référence)
PRESENTATION GENERALE

Méthodes d’Évaluation
L'évaluation des connaissances et des compétences acquises repose sur deux types de contrôle : le
contrôle d'attention et le contrôle des compétences.
 Le contrôle d'attention : il s'agit d'un QCM (questionnaire à choix multiples) auquel vous
devriez répondre individuellement, durant une séance de cours, et dont les questions portent
sur des points abordés au niveau de cette séance. Ce type de contrôle teste directement votre
degré d’acquisition des connaissances. Il permet d'évaluer "en temps réel" votre capacité
d'attention pour vous inciter à être plus attentifs afin que vous bénéficiiez au maximum des
séances de cours.
 Le contrôle des compétences : Il s’agit de planifier et réaliser des évaluations adaptées aux
objectifs spécifiques de ce cours à travers :
• Des séries d’exercices : qui sont distribuées à la fin des séances de cours. Vous êtes incités à
réaliser des exercices d’application individuellement qui seront corrigés et expliqués durant
des séances dédiées. Ce type de contrôle vous offre la possibilité d’appliquer les notions
théoriques découvertes durant les séances de cours et, par conséquent, de vous auto-
évaluer.
• Un examen de contrôle et un examen de synthèse : qui durent entre 60’ et 90’. Ils
permettent une évaluation sommative afin de s’assurer que les objectifs visés par ce cours
sont bien atteints.
Equipe pédagogique
L’équipe pédagogique du cours « Programmation Orientée Objet » est formée de trois enseignants :

Mohamed Hammouda Cours POO + Atelier POO med_hammouda@yahoo.fr

Sameh Hbaieb Turki Cours POO + Atelier POO turkisameh@yahoo.fr

Amina Hechkel Cours POO + Atelier POO amina.hechkel@hotmail.fr

Volume Horaire
• Les cours auront lieu chaque semaine à raison de 3h par semaine durant 15 semaines, soit
45h/semestre.
• Les séances d’ateliers auront lieu chaque semaine à raison de 3h par semaine durant 15
semaines, soit 45h/semestre
PRESENTATION GENERALE

Répartition des séances de cours POO

Semaine Chapitre

1 Chapitre 1 : Introduction à la programmation Orientée Objet

2 Chapitre 2 : Les bases de JAVA

3 Correction du TD 1 : Les bases de JAVA

4 et 5 Chapitre 3 : Cycle de vie, méthodes et encapsulation des objets java

5 Correction du TD 2 : Cycle de vie, méthodes et encapsulation des


objets java

6 et 7 Chapitre 4 : Les Eléments simples du langage Java

7 Correction du TD 3 : Les Eléments simples du langage Java

8 Examen de Contrôle

9 et 10 Chapitre 5 : Travailler avec l’Héritage

11 Correction du TD 4 : Travailler avec l’Héritage

12 et 13 Chapitre 6 : Les exceptions

13 Correction du TD 5 : Les exceptions

14 Révision : correction des examens des années précédentes


INTRODUCTION A LA PROGRAMMATION ORIENTEE OBJET

Chapitre I
INTRODUCTION A LA PROGRAMMATION ORIENTEE OBJET

Introduction
Dans le monde réel, nous sommes entourés par des objets de toutes tailles, de toutes origines, de
toutes valeurs. La majorité des objets qui nous entourent sont clairement identifiables. Chaque
objet possède des caractéristiques et offre un ou plusieurs services en se basant sur ses propres
ressources. Les objets qui possèdent des caractéristiques communes peuvent être groupés en
famille.
Le monde informatique s’est inspiré largement du concept d’objet. En effet, en informatique, un
objet est un conteneur symbolique qui possède une existence propre, incorpore des informations,
possède des méthodes, est en relation avec d’autres objets via l’échange de massages et peut être
manipulé dans un programme. C'est le concept pivot de la programmation orientée objet.
Chaque objet appartient à une classe. Cette dernière regroupe dans une même entité toutes les
caractéristiques et tous les comportements communs à un ensemble d’objets.
Dans ce chapitre, vous allez découvrir les différentes approches de programmation informatique,
ainsi que les avantages des unes par rapport aux autres. Les principaux points abordés sont les
suivants :
 Comprendre la différence entre la programmation procédurale et la programmation orientée
objet.
 Enumérer les avantages de la programmation orientée objet.
 Comprendre le concept de classe et d’objet.

Mohamed HAMMOUDA 1
INTRODUCTION A LA PROGRAMMATION ORIENTEE OBJET

CHAPITRE 1 : INTRODUCTION A LA
PROGRAMMATION ORIENTEE OBJET
I. LA COMPLEXITE DU PROCESSUS DE DEVELOPPEMENT
Souvent, dans le domaine de développement logiciel, on se trouve face à des problèmes à résoudre
extrêmement complexes, telles que les applications aéronautiques et les applications robotiques.
Par ailleurs, assurer le bon fonctionnement de ces applications logicielles est une tâche très difficile
à cerner. Cette complexité augmente de plus en plus avec l’intégration des exigences non
fonctionnelles (et surtout implicites), telles que la facilité d’emploi, l’efficacité et la performance.
Le rôle principal des équipes de développement logiciel est de produire des logiciels de qualité et
simples à manipuler par les utilisateurs finaux. Pour se faire, les développeurs se trouvent souvent
dans l’obligation d’écrire de codes supplémentaires afin d’assister les utilisateurs et leur permettre
de manipuler les logiciels de manière confortable et souple.
Par exemple il a fallu plus que 60 millions de lignes de code pour offrir aux internautes un site aussi
riche que le Facebook.
Une réalité est claire dans le monde de développement : plus le code est complexe, plus il est moins
extensible, moins réutilisable, et plus difficile à maintenir. Nous pouvons même affirmer que
demander à un développeur d’ajouter de nouvelles fonctionnalités à un code complexe c’est
comme demander à un entrepreneur d’ajouter une nouvelle assise à un immeuble de 100 étages.

Figure 1 - Plus un système est complexe, plus il est susceptible d'effondrement

II. LES APPROCHES DE PROGRAMMATION


II.1. L’approche de programmation Procédurale (Fonctionnelle)
La programmation procédurale privilège les traitements sur les données. Sa première
préoccupation consiste en la conception des algorithmes qui résolvent les problèmes rencontrés,
puis s’intéresse, en second lieu, à la manière dont les données sont stockées. Elle se base
essentiellement sur le principe de décomposition des programmes en sous-programmes, des sous-
programmes en fonctions, des fonctions en sous-fonctions jusqu’à arriver à des fonctions
atomiques. Certes, cette approche est cohérente, intuitive et simple à mettre en œuvre mais
l’interdépendance des fonctions et la séparation des données et des traitements limitent son
utilisation par les équipes de programmation logicielle. Cette approche trouve son intérêt surtout
avec les petits projets dont le nombre de fonctions est limité.
Les limites de la programmation procédurale ont été clairement cernées à la fin des années 80 avec

Mohamed HAMMOUDA 2
INTRODUCTION A LA PROGRAMMATION ORIENTEE OBJET

l’évolution des exigences des utilisateurs. Les développeurs se trouvaient dans l’obligation
d’adapter les données aux nouvelles exigences. La proposition d’une nouvelle approche de
programmation à cette époque était indispensable pour détourner les limites de la programmation
procédurale.

Appel de
Traitement Manipulation
des données

Données
Appel de
Traitement 1

Figure 2 – Problème d’interdépendance des fonctions dans l’approche de programmation


procédurale

II.2. L’approche de programmation Orientée Objet


L’adoption d’une nouvelle approche de programmation consiste à changer la manière de concevoir
les choses. C’est le cas de la programmation orientée objet qui se préoccupe des objets composant
le système plutôt de ce que fait le système.
Par ailleurs, un système informatique conçu selon une approche orientée objet peut être perçu
comme un ensemble d’objets en interaction. Chaque objet est défini par un ensemble de
caractéristiques et offre un ensemble de services. Depuis son apparition, cette approche a
révolutionné le monde informatique et continue à le faire.

Donnée O

O
Traitement

Figure 3 – Interactions entre un ensemble d’objets dans un système informatique


La POO est centrée plutôt sur les données. L’approche de programmation orientée objet commence
par identifier l’ensemble des objets qui constituent l’application à développer. Dans ce cadre, un
objet est tout entité ayant une existence propre et qui est utilisée ou manipulée par l’application à
informatiser. Ensuite, les développeurs définissent les traitements à réaliser par l’application et

Mohamed HAMMOUDA 3
INTRODUCTION A LA PROGRAMMATION ORIENTEE OBJET

associent chaque traitement à l’objet correspondant. De ce fait, un objet peut être vu comme une
entité regroupant un ensemble de données et de méthodes (l’équivalent d’une fonction en C). En
fin, les objets communiquent ensemble par échange de messages afin d’assurer le bon
fonctionnement du système.
L’exemple suivant permet de mettre en évidence ce nouveau paradigme de programmation.
Supposons un scénario dans lequel nous souhaitons programmer des robots qui conduisent des
voitures en appliquant l’approche de POO présentée dans le paragraphe précédent. Les étapes à
suivre sont les suivantes :
1. Identifier les objets de l’application :
 Deux robots.
 Deux voitures
2. Recenser les caractéristiques des objets identifiés :
Chaque rebot est caractérisé par :
 un code,
 un nom,
 un poids,
 une version
 une date de fabrication.
Chaque voiture est caractérisée par :
 un numéro d’immatriculation,
 une marque,
 un modèle,
 une couleur,
 une vitesse max,
3. Identifier ce que peut faire les objets :
Un robot est capable de :
 Tourner le volant à droite

 Tourner le volant à gauche

 Appuyer sur l’embrayage

 Appuyer sur le frein

 Appuyer sur l’accélérateur

 Tourner la clé

Mohamed HAMMOUDA 4
INTRODUCTION A LA PROGRAMMATION ORIENTEE OBJET

Une voiture est capable de :


 Démarrer
 Ralentir
 Accélérer
 Avancer
 Reculer
 Tourner à gauche
 Tourner à droite
4. Echange des messages entre les objets afin de faire fonctionner le système :

Démarrer ❷ Tourner la clé ❶

Appuyer sur……
l’accélérateur ❸

Système de contrôle

Avancer ❹

Figure 4 - Echange de messages entre les différents objets composant le système


Dans ce système, nous avons utilisé 4 objets (2 robots et 2 véhicules) de types différents.
Que pensez-vous si ce nombre était 100 robots et autant de voitures ?
Pour surmonter cette difficulté, la programmation orientée objet propose un nouveau concept
permettant de regrouper l’ensemble des objets possédant les mêmes caractéristiques et offrant les
mêmes comportements dans une même entité, appelée classe.
Une classe est donc un ensemble d'objets concrets partageant certaines propriétés. Souvent, nous
assimilons une classe à un type abstrait, une moule ou même un plan qu’on doit suivre afin de créer
des objets qui présenteront les mêmes caractéristiques.

III. LES PREMIERS CONCEPTS ORIENTES OBJETS


III.1. Les Classes
III.1.1. Notion de classe
Dans le monde réel, nous pouvons énumérer des centaines de milliards d’objets. Ces objets sont
souvent des mélanges trop complexes dont il est difficile de comprendre du premier coup. Pour
réduire cette complexité, l'être humain a appris à classer les éléments qui se ressemblent et à
distinguer les caractéristiques de plus haut niveau d'abstraction et se débarrasser des détails
inutiles.

Mohamed HAMMOUDA 5
INTRODUCTION A LA PROGRAMMATION ORIENTEE OBJET

III.1.2. Définition d’une classe


La classe décrit le domaine de définition d'un ensemble d'objets. Chaque objet doit appartenir à
une classe. Les caractéristiques sont contenues dans la classe et les particularités sont contenues
dans les objets. Les objets informatiques sont construits à partir de la classe, par un processus
appelé instanciation. De ce fait, tout objet est une instance d’une classe. Une classe est l'abstraction
d'un ensemble d'objet qui possède une structure identique (liste des attributs) et un même
comportement (liste des méthodes)
ROBOT Classe
Code
Modèle
Attributs / Caractéristiques
Couleur
Poids
Avancer Méthodes / comportement
Sauter

ObjetROBOT1 ObjetROBOT2 ObjetROBOT3


Code : MAH1 Code : MAH2 Code : TN21
Modèle : X225X7 Modèle : X225X7 Modèle : Z212x7
Couleur : bleu Couleur : rouge Couleur : bleu
Poids : 150 Poids : 150 Poids : 170
Avancer(int,int) Avancer(int,int) Avancer(int,int)
Sauter (int) Sauter (int) Sauter (int)

Figure 5 : Des objets robots comme des instances de la classe Robot

III.2. Les Objets


III.2.1. Notion d’Objet
Le monde dans lequel nous vivons est constitué d'objets matériels de toutes sortes. Par extension, il
est possible de définir d'autres objets, sans masse, comme les comptes en banque, les polices
d'assurance. Ces objets correspondent plutôt à des concepts plutôt qu'à des entités physiques.
III.2.2. Définition d’un Objet
Un objet est une définition de caractéristiques propres à un élément particulier. Il s'agit d'un
élément concret qui se voit attribué des valeurs aux attributs de sa classe. L’attribution des valeurs
aux attributs permet de définir l’état de l’objet. L'état d'un objet, à un instant donné, correspond à
une sélection de valeurs parmi toutes les valeurs possibles des différents attributs de la classe qui
l’a instanciée. D’un autre côté, chaque objet est capable de réaliser toutes les méthodes
(comportement) définies dans sa classe. Les opérations sont déclenchées suite à une simulation
externe, représenté sous la forme d'un message invoqué par un autre objet.

ObjetROBOT1
Code : MAH1 Valeur de l’attribut Code de type Sting
Modèle : X225X7
Couleur : bleu
Poids : 150
Le robot peut avancer de x mètres à
Avancer(int,int)
une vitesse de y mètres /minutes
Sauter (int)

Figure 6 : L’attribution des valeurs aux attributs définit l’état de l’objet ROBOT1.

Mohamed HAMMOUDA 6
LES BASES DE LA PROGRAMMATION ORIENTEE OBJET EN JAVA

Chapitre 2
LES BASE DE LA PROGRAMMATION ORIENTEE OBJET EN JAVA

Introduction
Imaginez que vous avez mis en place une nouvelle boite informatique qui fonctionne avec plusieurs
développeurs. Pour assurer un déroulement fluide et efficace, vous définissez une structure pour
votre boite et un ensemble de départements ayant des responsabilités séparées. Ces départements
vont interagir les uns avec les autres afin d’assurer le bon fonctionnement de la boite informatique.
En outre, selon les exigences de confidentialité, les données de votre boite seront disponibles
seulement à quelques développeurs en leur attribuant des privilèges spéciaux. Ceci est un exemple
de la façon dont les boites de développement travaillent avec une structure bien définie et un
ensemble de règles pour obtenir de meilleurs résultats.
De même, Java a organisé son workflow. La structure et les composants de la boite peuvent être
comparés avec la structure et les composants d’une classe Java, et les départements de la boite
peuvent être comparés avec les packages Java. Restreindre l'accès à toutes les données de
l'organisation peut être comparée à des modificateurs d'accès de Java.
A travers ce chapitre, vous allez être capable de répondre à des questions sur la structure d’une
classe Java, les packages, l'importation des classes, l'application des modificateurs d’accès, la
définition des méthodes, les constructeurs pour instancier des objets et les destructeurs pour
libérer des ressources.
Étant donné ces informations, ce chapitre traitera les éléments suivants:
• Comprendre la structure et les composants d’une classe java
• Comprendre les applications Java exécutables

• Comprendre les packages Java


• Importer des packages Java dans votre code

Mohamed HAMMOUDA 7
LES BASES DE LA PROGRAMMATION ORIENTEE OBJET EN JAVA

CHAPITRE 2 : LES BASE DE LA


PROGRAMMATION ORIENTEE OBJET EN JAVA
I. LA STRUCTURE D’UNE CLASSE JAVA
Comme vous pouvez le voir dans la figure 7, une personne physique peut être représentée par une
classe java Personne. Cette classe devrait résider dans un fichier de code source Java
(Personne.java). En se basant sur ce fichier de code source, le compilateur Java (javac.exe) génère
du bytecode et le stocke dans un fichier Personne.class. Ce fichier peut être par la suite exécuté par
la JVM (Java Virtual Machine).

Réside dans
Personne.java Entrée
Personne.class
Class Personne {
String nom ; Class Personne { .........
String getNom() { .........
..................... .........
Return nom;
..................... .........
} Compilateur

} ..................... Java
.........
..................... .........
..................... .........
..................... .........
..................... .........
.........
}

Personne Classe Personne Fichier Code Source Java ByteCode Java

Figure 7 – Relation entre la classe Persone, le fichier source Personne.java, et le fichier byteCode
Personne.class
Une classe Java contient plusieurs composants. La Figure 8 définit la structure générale d'une classe
Java, ainsi que les composants qui la constituent. Voici une liste des composants d'une classe java
qui seront détaillés dans ce chapitre :
 L'instruction package
 La déclaration d'importation
 Les Commentaires
 La déclaration de la classe
 Les attributs ou variables
 Les méthodes
 Les constructeurs

Mohamed HAMMOUDA 8
LES BASES DE LA PROGRAMMATION ORIENTEE OBJET EN JAVA

Déclaration du Package
Instructions Import
Commentaires
Déclaration de la classe {
Attributs
Constructeurs
Méthodes
Classes Internes
Interfaces Internes Ces composants feront
Enum l’objet d’un autre cours
}

Figure 8 – Structure générale d’une classe Java

I.1. Déclaration du package


Toutes les classes Java font partie d'un package. Une classe Java peut être définie explicitement
dans un package nommé; sinon elle appartient à un package par défaut qui ne possède pas de
nom. Une instruction package est utilisé pour définir explicitement à quel package appartient une
classe. La déclaration d’un package doit être la première instruction dans le fichier auquel
appartient la classe :
La déclaration d’un package doit être la
package poo; première instruction d’une classe java

class cours { Le reste du code de la classe


}

Figure 9 – Déclaration d’un package

I.2. Importation d’un package


D’après ce que nous avons présenté jusqu’à maintenant, nous pouvons dire qu’un package java
joue le rôle d’un paquet utilisé pour ranger et organiser des classes ou bien d’autres packages. Il est
tout à fait évident que deux classes se trouvant dans le même package sont généralement visibles
l’une par l’autre.
La première question qui se pose : est-ce que ces deux classes seront aussi visibles l’une par
l’autre si elles se trouvent dans deux packages différents ?
Pour répondre à cette question, nous allons se référer à la figure suivante qui permet d’illustrer les
deux scénarios qui ont été évoqués.

Package p1 Package p1 Package p2

Class C1{}
Class C1{} Class C2{}
Class C2{}

Scénario 1 Scénario 2

Figure 10 – Visibilité des classes se trouvant dans deux packages différents

Mohamed HAMMOUDA 9
LES BASES DE LA PROGRAMMATION ORIENTEE OBJET EN JAVA

• Pour le scénario 1, nous avons déjà dit que les deux classes sont généralement visibles l’une par
l’autre. Nous avons bien dit généralement parce qu’il existe un seul cas pour lequel une classe
n’est pas visible par les autres classes du même package. Ce cas sera détaillé par la suite.
• Pour le scénario 2, il est clair que les frontières de chaque package empêchent les classes de
communiquer ensemble.
Une deuxième question s’impose : Pourquoi doit-on utiliser des packages pour organiser
des classes si ces classes ne peuvent pas communiquer ensemble ?
En réalité, ceci est possible. Java vous propose deux techniques permettant à des classes d’un
package de se référer à des classes se trouvant dans un autre package.
• Solution 1 : Pour utiliser une classe d'un autre package, vous devez utiliser son nom complet. Par
exemple, en se référant à la figure 10 et plus précisément au scénario 2, si la classe C2 du
package p2 souhaite utiliser la classe C1 du package p1 alors dans la classe C2 vous devriez
ajouter l’instruction p1.C1. Cela peut être fastidieux et peut rendre votre code difficile à lire
surtout si votre classe se trouve dans des package imbriqués, d’où la deuxième solution proposé
par java.
• Solution 2 : Vous pouvez utiliser l'instruction import pour utiliser le nom d’une classe dans votre
code. Notez que la déclaration d'importation suit directement la déclaration du package mais
précède celle de la classe.

package p2; package p2;


class C2 { Nom complet de la import p1.* ; L’instruction import suit
directement la déclaration
p1.C1 obj1 ;
classe C1 : nom du ou class C2 { d’un package
}
des packages suivi du
nom de la classe
C1 obj1 ; La classe C1 peut être utilisée dans la
} classe C2 sans erreur de compilation

Figure 11 – Importation des packages comme solution pour accéder à des classes externes
(d’autres packages)

I.3. Déclaration d’une classe


La déclaration d’une classe marque le début d'une classe. La déclaration la plus simple se compose
d’un mot clé class suivi par le nom de la classe ainsi que son code :
class Personne {/*code*/}
En réalité, la déclaration d’une classe est loin d’être aussi simple. Une déclaration complexe d’une
classe peut contenir les éléments suivants :
 modificateur d’accès
 modificateurs
 nom de la classe
 nom de la classe de base (à voir dans un autre chapitre dans ce cours)
 les interfaces que la classe implémente (à voir dans un autre chapitre de ce cours).
 le tout délimité par des accolades.

Mohamed HAMMOUDA 10
LES BASES DE LA PROGRAMMATION ORIENTEE OBJET EN JAVA

Ne vous inquiétez pas, si vous ne comprenez pas quelques éléments de cette liste, ils seront
détaillés dans la suite du cours.
Observons maintenant les composants d'une déclaration de classe en utilisant un exemple:
public final class Coureur extends Personne implements Athlète {……………}
Les composants de la classe Coureur peuvent être représentés graphiquement, comme le montre la
figure 12.
Le tableau 1 résume les composants optionnels et les composants obligatoires d’une classe java.
Tableau 1 – Composants obligatoires et composants optionnels d’une classe java

Composants Obligatoires Composants Optionnels

Mot clé class Modificateur d’accès, tel que public

Nom de la classe Modificateurs, tel que final

Corps de la classe délimité par {} Mot clé extends lié au nom de la classe de base

Mot clé implements lié au nom des interfaces

public final Class Coureur extends Personne Implements Athlete {}

Modificateur Modificateur Mot clé Nom de Mot clé Clasee Mot clé Nom
d’accès la classe extends de base implements interface
Accolade

Optionnel Optionnel Obligatoire Obligatoire Optionnel Optionnel Optionnel Optionnel Obligatoire

Figure 12 – Structure complète d’une classe java

I.4. Définition d’une classe


Une classe est un concept utilisé pour décrire les propriétés et le comportement d'un objet. Les
propriétés d'un objet sont mises en œuvre en utilisant des variables ou bien des attributs, et le
comportement est mis en œuvre en utilisant des méthodes.
Dans le chapitre 1, nous avons vu qu’une classe peut jouer le rôle d’une moule pour construire des
objets ayant les mêmes propriétés que la classe et que les valeurs de ces propriétés définissent
l’état des objets. Rappelez-vous l’exemple du robot.
Nous allons donc voir, à travers un exemple, comment java crée ses classes et les utilise par la suite

Mohamed HAMMOUDA 11
LES BASES DE LA PROGRAMMATION ORIENTEE OBJET EN JAVA

pour instancier ses objets. Nous allons définir une classe simple pour représenter un Téléphone.

class Telephone {
String modele;
Attributs
String compagnie;
double poids;
Telephone(String modele) {
this.modele = modele; Constructeur
}
void emettreAppel(String numero) {
// code
}
void recevoirAppel() { Méthodes
// code
}
}

Figure 13 – Définition de la classe java Telephone

I.4.1. Attributs
Dans l’exemple de la figure 13, les attributs modele, compagnie et poids sont utilisés pour stocker
l'état d'un objet Telephone (également appelé une instance). Ces attributs sont appelés variables
d’instances ou bien attributs d'instance. Chaque objet a sa propre copie des variables d'instance. Si
vous modifiez la valeur d'une variable d'instance d'un objet, la valeur de la même variable
d'instance nommée ne va pas changer pour un autre objet.
Par opposition aux variables d’instance, java permet de définir des variables de classe dont la valeur
est partagée par toutes les instances (objets) de la classe. Le concept de variable de classe sera
détaillé plus loin dans ce chapitre

I.4.2. Méthodes
Nous reprenons le même exemple pour identifier les méthodes. Ainsi, emettreAppel et
recevoirApel sont des méthodes d'instance qui sont généralement utilisées pour manipuler les
variables d'instance. La première méthode admet comme paramètre un string et comme type de
retour un void. Alors que la deuxième est sans paramètre et admet aussi comme type de retour un
void. Une méthode de classe (appelée aussi méthode statique) est utilisée pour manipuler des
variables de classes ou statiques.
I.4.3. Constructeurs
La class Telephone de la figure 13 admet une méthode un peu particulière. Elle porte le même nom
que celui de la classe et n’admet aucun type de retour, même pas un void. Une telle méthode en
java est appelée constructeur. Nous verrons plus loin dans ce cours que ce type de méthode
particulier est utilisé pour instancier (créer) des objets.

I.4.4. Modificateur d’Accès


Nous ne pouvons pas parler d’une définition d’une classe sans parler des modificateurs d’accès. Les
modificateurs d'accès déterminent la visibilité des classes, des méthodes et des variables par
rapport aux autres membres et aux autres classes du même package ou des autres packages. Pour

Mohamed HAMMOUDA 12
LES BASES DE LA PROGRAMMATION ORIENTEE OBJET EN JAVA

une classe java, il existe seulement deux types de modificateurs d’accès. Le premier c’est public qui
rend une classe visible en dehors de son package. Le deuxième c’est celui par défaut (lorsque le mot
clé public est omis dans la définition de la classe). Dans ce cas elle ne sera visible qu’à l’intérieur de
son package ou bien dans son fichier.

II. LA STRUCTURE D’UN FICHIER DE CODE SOURCE JAVA


II.1. Définition d’un fichier code source .java
Un fichier de code source Java est utilisé pour définir les classes et les interfaces. Tout le code Java
doit être défini dans un ou plusieurs fichiers de code source (fichiers texte dont le nom se termine
par « .java »). Un fichier source peut contenir au minimum une classe. Si vous définissez une classe
publique, son nom doit correspondre avec le nom du fichier de code source Java auquel elle
appartient. En plus, un fichier de code source ne peut pas définir plus qu’une classe publique. Si
vous essayez de le faire, votre code ne compilera pas. Nous allons, à travers un ensemble
d’exemples illustrés par la figure 14, expliquer ces principes.
Fichier F1.java

Un fichier source contenant la définition


package p1 ; d’une seule classe. Cette classe sera visible
class C1{ } seulement à l’intérieur du package p1 par les
autres classes du package

Fichier F1.java

package p1 ; Cette classe génère une erreur de


public class C1{ } compilation car une classe publique doit
avoir le même nom que le fichier source
dans laquelle est défini

Fichier F1.java
Bien que le nom de la classe public est le
même que le nom du fichier source, ce code
package p1 ; génère une erreur de compilation, car dans
public class F1{ } un fichier source il ne peut exister qu’une
public class C1{ } seule classe public et une ou plusieurs classes
par défaut.

Figure 14 – Le principe de définition des fichiers de code source java

II.2. La classe exécutable java


Une classe Java exécutable est une classe qui, lorsqu'elle est remise à la JVM, commence son
exécution à un point particulier qui n’est autre que sa méthode principale (fonction main).
Typiquement, une application est constituée d'un nombre de classes qui sont définies dans
plusieurs fichiers de code source Java. De tous ces fichiers, le programmeur doit désigner une des
classes comme une classe exécutable.
La figure 15 illustre d’une manière simplifiée comment la JVM cible la classe exécutable afin de
commencer l’exécution du programme. La classe exécutable est la classe du projet qui contient la
méthode principale main.

Mohamed HAMMOUDA 13
LES BASES DE LA PROGRAMMATION ORIENTEE OBJET EN JAVA

Package p2 Package p2
class C4 {
Class C1 {} Class C3 {} int x ;
public static void
main(String[] a) {
Package p3 Package p4
}
JVM //RESTE DU CODE
Class C2 {} Class C4 { main} }

Figure 15 – la JVM cible la classe exécutable afin de commencer l’exécution du programme.

La méthode principale main

La première exigence dans la création d'une application Java exécutable est de créer une classe
comportant une méthode dont la signature est la suivante :

public static void main(String args[]) { }

Les règles suivantes doivent être vérifiées pour la méthode main :

• elle doit être marquée comme public


• elle doit être marquée comme static
• le nom de la méthode doit être main
• le type de retour de la méthode doit être void.

• La méthode doit accepter un tableau de String comme argument


La Figure 16 met en évidence les règles que doit satisfaire la méthode principale main.

La méthode ne doit pas Le nom de la méthode


retourner une valeur. doit être main.
La méthode doit accepter un
tableau de String. Le nom du
paramètre de la méthode peut être
Le modificateur n’importe quel nom d’identifiant
d’accès doit être Public class HelloExam {
publique Public static void main ( String args [ ] ) {
System.out.printLn( ‘hello exam ‘ ) ;
}
}

Le modificateur doit être statique

Figure 16 – Composants de la méthode principale (main)

Mohamed HAMMOUDA 14
CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA

Chapitre 3
Cycle de vie, méthodes et encapsulation des objets java

Introduction
Si nous jetons un coup d'œil autour de nous, nous pouvons se rendre compte qu’il y a pas mal
d’objets bien encapsulés. L’être humain est un exemple d’objets bien encapsulés. En fait, chacun de
nous dispose d’un certain nombre de caractéristiques de valeurs différentes permettant de nous
distinguer les uns des autres. De plus, chacun de nous peut, à sa manière, effectuer des actions.
Nous pouvons marcher, manger, parler, etc. Mais la manière avec laquelle on effectue ces actions
est encapsulée à l’intérieur de nous-même. Personne ne peut accéder et voir comment notre
système interne fonctionne.
Un autre exemple type est les services bancaires qui appliquent un ensemble de processus bien
définis pour nous permettre d'assurer notre argent et les objets de valeur (dans le coffre-fort de la
banque). La Banque peut exiger des informations (des paramètres) de notre part pour exécuter
certains de ses processus, tel que le dépôt de l'argent dans notre compte. Mais la banque ne peut
pas nous renseigner sur les résultats d'autres processus. Par exemple, elle peut nous informer sur le
solde du compte après une transaction, mais elle ne peut pas nous informer sur ses plans de
recrutement pour les nouveaux employés.
En Java, vous pouvez comparer une banque à une classe bien encapsulée, et les processus bancaires
à des méthodes Java. Dans cette analogie, votre argent et objets de valeur sont comme des champs
d'objet en Java. Vous pouvez également comparer les entrées d'un processus de banque aux
paramètres des méthodes, et le résultat d’un processus de la banque à la valeur de retour d'une
méthode Java. Enfin, vous pouvez comparer l'ensemble des mesures que la banque exécute
lorsqu’il y a une ouverture d’un nouveau compte bancaire à un constructeur en Java assurant la
création d’un nouvel objet.
Ce chapitre vous aidera à obtenir des réponses correctes sur les points suivants :

• Définition du cycle de vie d'un objet


• Création de méthodes avec des arguments (primitifs et objets) et des valeurs de retour
• Utilisation du principe de surcharge pour la définition méthodes et de constructeurs
• Lecture et l'écriture dans les champs des objets
• Invocation des méthodes des objets
• Application du principe d'encapsulation sur une classe

Mohamed HAMMOUDA 15
CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA

CHAPITRE 3 : CYCLE DE VIE, METHODES ET


ENCAPSULATION DES OBJETS JAVA
I. CYCLE DE VIE DES OBJETS JAVA
I.1. La naissance d’un objet en java
Dans java, un objet est créé dans la mémoire centrale à chaque fois que vous utilisez le mot
réservé new. Pour pouvoir accéder à cet objet, vous devez le référencer dans votre programme
par une variable de référence. La création d’un objet est appelée instanciation d’un objet.
L’instanciation d’un objet peut se faire en java en une ou deux étapes (voir figure 17).

❶ NomClasse variableReference = new NomClasse() ;


❷ NomClasse variableReference ;
variableReference= new NomClasse() ;
Figure 17 – Instanciation d’un objet à travers l’opérateur new
Par contre, vous devez en aucun cas instancier un objet sans l’avoir référencé car, dans ce cas, il
sera inaccessible. Nous allons, à travers un ensemble d’exemples illustrés par la figure 18, expliciter
les problèmes de référencement.

class Chien{ Mémoire (Programme) Mémoire (Données)


void cycleVieObjet(){
Chien objChien = new Chien() ; objChien
}
}

class Chien{ Les deux exemples assurent la


void cycleVieObjet(){ création et l’accès à un objet chien
Chien objChien ;
objChien = new Chien() ; Objet chien concret instancié
} par new
}

class Chien{ objChien


void cycleVieObjet(){
Chien objChien ;
new Chien() ; Objet Chien inaccessible
}
}
Objet chien instancié par new

Class Chien{ Variable de référence objChien


void cycleVieObjet(){ objChien pointant vers null vue que nous
Chien objChien ; n’avons pas instancié un objet Chien
} null
}

Figure 18 – Distinction entre objet et variable de référence

Mohamed HAMMOUDA 16
CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA

I.2. Accessibilité des objets


Nous avons vu dans la section précédente qu’un objet créé doit être impérativement référencé par
une variable de référence afin qu’il soit accessible.
En revanche, un objet accessible, c’est à dire référencé par une variable de référence, peut devenir
inaccessible dans deux cas :
• Cas 1 : si sa variable de référence pointe sur un nouveau objet concret, et qu’il n’est pas
référencé par une autre variable de référence.
• Cas 2 : en dehors de sa zone de définition

class Chien{
void cycleVieObjet(){ Dans cet exemple nous avons créé deux
Chien objChien = new Chien(); objets Chien. Seulement le deuxième est
objChien = new Chien() ; accessible. Le premier devient inaccessible
}
}

public void maMethode() {


int resultat = 88;
if (resultat > 78) {
Exam monExam1 = new Exam();
monExam1.setNom("Android"); L’objet référencé par monExamen1 n’est
}❶ accessible que dans le bloc if ❶
else { L’objet référencé par monExamen2 n’est
Exam monExam2 = new Exam(); accessible que dans le bloc else ❷
monExam2.setNom("MySQL");
}❷
}

Figure 19 – Accessibilité d’un objet java

I.3. Le processus ramasse miette (gabraj collector)


Dans les sections précédentes, nous avons vu que chaque objet concret est créé par la commande
new suivi du nom de la classe. Nous avons précisé aussi qu’un objet n’est autre qu’une zone
mémoire allouée pour stocker toutes ses données et qu’il doit être référencé pour qu’il soit
accessible. Bien, considérant maintenant l’exemple du code suivant, et je vous laisse deviner le
comportement de la JVM lorsque elle commence l’exécution de la classe Personne.

package p1 ;
class Personne {
String nom ;
public static void main(String[] args){
Boucle while infinie assurant la création
while (true){
des objets de types Personne
Personne p = new Personne();
}
}
}

Figure 20 – Création d’un nombre illimité d’objets

Mohamed HAMMOUDA 17
CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA

Vous l’avez sans doute deviné, c’est une erreur de type stack overflow qui indique généralement un
problème de saturation au niveau de la mémoire.
Examinons le code et essayons de voir pour quelle raison cette erreur est générée. Le programme
principal est composé d’une boucle while (le chapitre suivant mettra l’accent sur les éléments du
langage java) à l’intérieur de laquelle nous avons défini une instruction assurant la création d’un
objet personne. Supposons maintenant que cette boucle a été exécutée 10 fois. Nous aurons donc
10 objets créés mais seulement un seul qui est référencé par la variable de référence p. Imaginez
maintenant que ce nombre d’itération a atteint le un milliard et que la taille qu’occupe un objet
Personne dans la mémoire est de 10 octets. Vous comprenez maintenant qu’elle est l’origine de
cette erreur.
La question qui se pose est alors : Comment libérer la mémoire des objets qui ne sont pas
référencés ?
En fait, java dispose d’un thread (un programme qui s’exécute en arrière-plan) assurant la
suppression de tous les objets non référencés de la mémoire centrale. Ce thread appelé ramasse
miette (garbage collector) parcourt périodiquement la mémoire centrale afin de pointer les objets
non référencés et les supprimer.

II. LES METHODES DE CLASSE


II.1. Création des méthods avec des arguments et type de retour

class Telephone{ class Telephone{ class Telephone{


String modele; String modele; String modele;
void setModele(String val){ String afficheval() { String modèleFormat(String format ) {
modele= val; Return modele; return format+modele + format
} } }
} } }

Méthode qui modifie une Méthode retourne le modèle délimité


Méthode retourne la valeur
variable d’instance ayant un par un format passé en paramètre
d’une variable d’instance
seul paramètre d’entrée
Figure 21 – Les méthodes sont utilisées pour définir le comportement d'un objet
Les méthodes sont utilisées pour définir le comportement d'un objet. Chaque méthode assure un
traitement spécifique. En java, les méthodes sont classées en deux grandes catégories :

• les méthodes avec ou sans type de retour

• les méthodes avec ou sans paramètres

II.1.1. Type de retour d’une méthode


Une méthode peut retourner au maximum une valeur. La valeur retournée doit être compatible
avec le type de retour de la méthode. La compatibilité des types sera discutée en partie dans le
chapitre suivant. Dans le cas où la méthode ne retourne aucune valeur, le type de retour de la
méthode doit être void.

void sum(double x, double y){System.outprintln("la somme est " +(x+y));}


double sum ( double x, double y){ return x+y ;}

Mohamed HAMMOUDA 18
CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA

Une méthode renvoie une valeur en utilisant le mot-clé return suivi par le nom d’une variable ou
une expression dont la valeur est passée à la méthode appelante.

double sum(double x, double y){double z ; z=x+y ; return z ;}


double sum ( double x, double y){ return x+y ;}

Si la valeur de retour d’une méthode est affectée à une variable alors cette dernière doit avoir le
même type de retour de la méthode ou bien un type compatible.

double sum(double x, double y){double z ; z=x+y ; return z ;}


public static void main(String [] a){ double b=sum(4.0,2.0);
int c = sum (2.0,5.3);}

Une déclaration de retour doit être la dernière instruction dans une méthode. Une instruction
placée après l’instruction return n’est pas accessible et génère une erreur de compilation.

double sum(double x, double y){double z ; z=x+y ; return z ; x+=1 ;}

Vous avez surement remarqué que le code marqué en rouge est un code erroné.
II.1.2. LES PARAMETRES D’UNE METHODE
Une méthode peut avoir plusieurs paramètres. Chaque paramètre est défini par un type suivi par le
nom du paramètre. Au moment de l’invocation d’une méthode, les arguments passés doivent
avoir les mêmes types que leurs paramètres correspondants ou bien de types compatibles.

void sum(double x, double y){return x+y ;}


double prod (double x, float y){ return sum(x,y) * 4 ;}

II.2. La surcharge des méthodes


La surcharge de méthode est un mécanisme qui permet de définir, dans une même classe, plusieurs
méthodes ayant le même nom mais différent au niveau de leur signature. Ceci revient donc à
définir plusieurs versions pour une méthode avec des listes de paramètres différentes. La surcharge
simplifie l'écriture de méthodes sémantiquement similaires sur des paramètres de types différents
et apporte plus de flexibilité au développement des applications.

int sum (int x, int y){}❶


sum int sum (int x, int y, int z) {}❷
double sum (double x, double y){}❸

Figure 22 – Exemple de surcharge de méthode

Mohamed HAMMOUDA 19
CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA

Si nous référons à la figure 22 présentée ci-dessus, nous remarquons que l’invocation de la


méthode sum(1,2,3) exécute le corps de la méthode ❷ car il y a une compatibilité entre les
paramètres de la méthode ❷ et les arguments passés à la méthode sum.

III. LES CONSTRUCTEURS


Les constructeurs sont des méthodes spéciales qui créent des objets de la classe dans laquelle ils
sont définis. Les constructeurs portent le même nom que celui de la classe dans laquelle ils sont
définis, et n’ont aucun type de retour. Un constructeur est invoqué en utilisant le mot clé new et
retourne une référence sur l’objet créé dans la mémoire centrale. Il existe deux types de
constructeurs : les constructeurs définis par l’utilisateur et les constructeurs par défaut qui sont
définis par le système.

III.1. Les constructeurs définis par l’utilisateur

class CoffreFort {
double poids ;
String modele ;
double prix ;
CoffreFort (){ poids : 4.2
poids=4.2 ;
} modele :null
Public static void main(String[] a){
CoffreFort f = new CoffreFort() ;
prix: 0.0
} ❶
}

class CoffreFort {
double poids ;
String modele ;
double prix ;
CoffreFort ( double poids, String m){ poids : 5.2
this.poids=poids ;
modele=m; Modele : m12z
}
Public static void main(String[] a){
prix: 0.0
CoffreFort f = new CoffreFort(5.2, ”m12z”) ;
} ❷
}

class CoffreFort {
double poids ;
String modele ;
double prix ;
CoffreFort (){ poids : 5.2
Poid=4.2 ;
} modele : m12z
Public static void main(String[] a){
CoffreFort f = new CoffreFort(5.2, ”m12z”) ;
f.prix=22.3 ;
❸ prix : 22.3
}
}

Figure 23 – Un développeur peut définir des constructeurs à sa guise

Mohamed HAMMOUDA 20
CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA

A l’intérieur d’une classe, le développeur peut définir plusieurs constructeurs afin d’assurer, une
fois invoqués, la création d’objets de cette classe. Le corps d’un constructeur précise la manière
avec laquelle le développeur souhaite créer ses objets. De ce fait, un constructeur peut présenter
un ou plusieurs paramètres qui sont utilisés pour définir l’état que peut prendre un objet après sa
création. Nous vous rappelons que l’état d’un objet est défini à travers les valeurs que peuvent
prendre ses attributs.
Ces principes seront expliquées, en figure 23, à travers un ensemble d’exemples :
Dans l’exemple ❶ de la figure 23, nous avons défini un constructeur non paramétré. Ce
constructeur, une fois invoqué dans la méthode principale main, instancie un objet et affecte sa
référence dans la mémoire centrale à la variable f. Vous avez sans doute remarqué que dans le
corps du constructeur un petit traitement a été développé permettant d’affecter la valeur 5.2 à
l’attribut poids, alors que les autres attributs ne sont pas initialisés et prennent en revanche leurs
valeurs par défaut. En effet, java affecte des valeurs par défaut aux attributs des objets si ces
derniers n’ont pas été initialisés dans le constructeur. Le tableau suivant permet de présenter les
valeurs par défaut des attributs des classes en fonction de leur type.
Tableau 2 – Valeurs par défaut des attributs de classe

Valeur par défaut des attributs d’instance

byte,shirt,int, et long 0

float, double 0.0

objet null

boolean false

char \u000

Dans l’exemple ❷, nous avons créé un constructeur paramétré recevant deux paramètres : poids
pour initialiser l’attribut poids, et m pour initialiser l’attribut modele. Par ailleurs, pour le cas du
paramètre poids, la JVM ne peut pas distinguer entre le paramètre du constructeur et l’attribut,
c’est le rôle que joue le mot clé this. Ce mot permet de rendre le code explicite et non ambigu.
En fin, dans l’exemple ❸, nous avons repris le même exemple ❷ en ajoutant une seule
instruction : f.prix=22.3. Il faut noter que la modification de l’attribut a été effectuée après
l’instanciation de l’objet f ;

III.2. La surcharge des constructeurs


Le principe de surcharge de méthode peut être appliqué également aux constructeurs. Les
constructeurs surchargés suivent les mêmes règles discutées dans la section précédente pour les
méthodes surchargées.

Mohamed HAMMOUDA 21
CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA

class Employe {
String nom;
int age;

❶Employe() {
nom = "Hammouda"; age = 25; }

❷ Employee(String nom) {
this.nom = nom;}

❸Employee(int age, String nom) {


this (nom): this.age = age;}

❹Employee(String n, int a) {
name =n; age = a;}
}

Figure 24 – Constructeurs surchargés dans une même classe java


Dans la figure 24, nous avons défini plusieurs constructeurs dans une même classe Employe. Nous
allons nous concentrer sur le constructeur ❸. Vous avez certainement remarqué l’utilisation
encore une fois du mot réservé this (nom). Dans ce cas, le this (nom) invoque un constructeur de la
classe ayant un paramètre de type String qui n’est autre que celui défini dans ❷. Dans ce cas,
l’objet est créé en deux étapes. La première assurée par le constructeur ❷, alors que la deuxième
est complétée par le constructeur ❸. Il est à noter que l’invocation d’un constructeur à l’intérieur
d’un autre constructeur doit être la première instruction dans le code de création de l’objet.

III.3. Le constructeur par défaut


Dans la section précédente, nous avons discuté comment un constructeur est utilisé pour créer un
objet.
Que se passe-t-il si un constructeur n’a pas été défini par un développeur ?
La réponse est très simple. Puisqu’un objet ne peut être instancié qu’à travers un constructeur et
devant l’absence d’un constructeur développé par l’utilisateur, java définit un constructeur par
défaut. Ce constructeur n’accepte aucun argument et attribut les valeurs par défaut aux variables
d'instance.

class CoffreFort {
double poids ;
String modele ;
double prix ;
poids : 0.0
Public static void main(String[] a){
CoffreFort f = new CoffreFort() ; modele :null

}
prix: 0.0

Figure 25 – Invocation du constructeur par défaut de java


La figure ci-dessus présentée met en évidence une classe sans constructeur qui compile
correctement en appelant le constructeur par défaut de java.

Mohamed HAMMOUDA 22
CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA

IV. L’ENCAPSULATION
Les modificateurs d'accès contrôlent l'accessibilité d'une classe y compris ses membres (méthodes
et variables) par d'autres classes. En utilisant les modificateurs d'accès appropriés, vous pouvez
limiter l'accès à votre classe et leurs membres par d'autres classes. Un tel comportement s’appelle
l’encapsulation.

IV.1. Modificateur d’accès : public


public est le modificateur d’accès le moins restrictif. Un membre public est accessible par tous les
membres y compris ceux hors de sa portée et tant que la classe parente est elle-même visible
(public).

IV.2. Modificateur d’accès : protected


Un membre protégé est seulement accessible par les membres de sa classe parente et par ceux du
paquetage parent et tant que la classe parente est elle-même accessible. Bien que ce modificateur
ne sera abordé que dans le chapitre 5, nous avons jugé intéressant de l’illustrer par la figure
suivante.

bibliotheque immeuble

package bibliotheque; extends


public class Livre {
Peut accéder à
Protected String auteur ;
Protected void modifierCouleur() ;

}
package immeuble;
import biblitheque.Livre;
Peut accéder
extendss public class LivreHistoire extends Livre {
public LivreHistoire {
package bibliotheque; String auteur =”abc”
public class Cours extends book { modifierCouleur() ;
Cannot access

public Cours { }
String auteur =”abc” }
modifierCouleur() ;
}
}
package immeuble ;
import biblitheque.Livre;
package bibliotheque; public class Classe {
public class librerie { public Classe {
public librerie { Livre l =new Livre()
Livre l=new Livre() l.auteur=”cba”;
l.auteur=”cba”; l.modifierCouleur() ;
Peut accéder à
l. modifierCouleur(); } }
} }
}

Figure 26 - Accès aux membres protected de la class Livre.

Mohamed HAMMOUDA 23
CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA

IV.3. Modificateur d’accès : default


Si aucun modificateur d'accès n'est déclaré, le membre sera accessible pour l'ensemble des classes
et sous-classes situées dans le package parent, mais en aucun cas, à l'extérieur de ce dernier. La
figure suivante permet d’illustrer ce principe.

bibliotheque immeuble

package bibliotheque; extends


public class Livre {
Ne peut pas accéder
} String auteur ;
void modifierCouleur() ;

package immeuble;
import biblitheque.Livre;
Peut accéder
extendss public class LivreHistoire extends Livre {
public LivreHistoire {
package bibliotheque; String auteur =”abc”
modifierCouleur() ;
Ne peut pas accèder

public class Cours extends book {


public Cours { }
String auteur =”abc” }
modifierCouleur() ;
}
}
package immeuble ;
import biblitheque.Livre;
package bibliotheque; public class Classe {
public class librerie { public Classe {
public librerie { Livre l =new Livre()
Livre l=new Livre() l.auteur=”cba”;
l.auteur=”cba”; l.modifierCouleur() ;
Peut accéder à
l. modifierCouleur(); } }
} }
}

Figure 27 - Accès aux membres par défaut de la class Livre

IV.4. Modificateur d’accès : private


private est le modificateur d’accès le moins restrictif. Un membre ou une classe interne privé est
uniquement accessible par sa classe parente.

IV.5. Accès aux champs d’un objet


L’accès en lecture et en écriture aux champs d’un objet (variables d’instance) peut se faire de deux
manières différentes : soit à travers l’opérateur d’accés « . » succédant le nom de la variable de
référence, soit à travers une méthode d’instance.

Mohamed HAMMOUDA 24
CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA

class Personne { public static void main (String[] args){


String nom ; Personne p = new Personne();❶
public String prenom ;
private int age; p.nom=”Fraj”;❷
public void setAge(int a) {age=a;} p.prenom=”Wahid”;❸
String getAge(){return age;}
p.age=19;❹
public String getNom(){return nom;}
public setNom (String nom){ this.nom=nom);} p.setNom(“Frad”);❺
void setPrenom(String p){prenom=p;} String vPrenom=p.prenom; ❻
private String getPrenom(){return prenom;}
} vPnom=p.getPrenom();❼
int vAge= p.getAge();❽
}

Figure 28 – Exemple d’accès aux champs de l’objet Personne


L’accès à travers l’opérateur « . » dépend strictement de la visibilité des attributs vis-à- vis de la
classe où ils sont invoqués.
En se basant sur plusieurs scénarios, nous allons essayer de discuter la manière avec laquelle les
variables d’instance sont accédées. Pour cela, nous allons considérer la class Personne définie
précédemment.
• Scénario 1 : La fonction main est implémentée dans la classe Personne. Toutes les instructions
développées dans la fonction main sont correctes et assurent un accès dans les deux sens pour
les variable d’instance de l’objet p. Par exemple, l’instruction ❷ assure une affectation de la
valeur « Fraj » au champ nom. L’instruction ❾ permet d’affecter la valeur retournée par la
méthode getAge() à une variable locale vAge. Il faut noter que lorsque nous travaillons dans la
même classe, tous les champs de la classe sont accessibles indépendamment de leur visibilité.
• Scénario 2 : La fonction main est définie dans une autre classe du même package Toutes les
instructions développées dans la fonction main sont correctes et assurent un accès dans les deux
sens pour les variable d’instance de l’objet p, à l’exception de l’instruction❹ qui présente une
erreur de compilation. Cette erreur est due à la visibilité réduite du champ nom qui est déclaré
avec le modificateur d’accès private.
• Scénario 3 : La fonction main est définie dans une classe d’un autre package : Les instructions
❷, ❹, et ❽ génèrent des erreurs de compilation pour les mêmes problèmes de visibilité.
Notez que le champ nom n’est accessible qu’à l’intérieur de sa classe puisqu’il possède comme
modificateur d’accés private, et toutes les méthodes qui permettent d’y accéder sont aussi
privées.
Remarquez que pour certains attributs, l’accès peut se faire de deux manières différentes. Pour une
meilleure sécurité de votre application et des données qu’elle manipule, il est plus prudent de
déclarer vos attributs comme des variables privées et assurer par la suite leur accès à travers des
méthodes d’accès.

IV.6. Les accesseurs et les mutateurs


Dans la section précédente, vous avez remarqué certainement l’appellation un peu particulière des
méthodes assurant l’accès aux attributs d’un objet. Ces méthodes sont appelées dans java des
accesseurs (getters) et des mutateurs (setters).

Mohamed HAMMOUDA 25
CYCLE DE VIE, METHODES ET ENCAPSULATION DES OBJETS JAVA

Un accesseur est une méthode permettant de récupérer le contenu d'une donnée membre
protégée. Un accesseur, pour accomplir sa fonction :
• doit avoir comme type de retour le type de la variable à renvoyer
• ne doit pas nécessairement posséder d'arguments
Un mutateur est une méthode permettant de modifier le contenu d'une donnée membre protégée.
Un mutateur, pour accomplir sa fonction :
• doit avoir comme paramètre la valeur à assigner à la donnée membre. Le paramètre doit
donc être de même type que la donnée membre
• ne doit pas nécessairement renvoyer de valeur (il possède généralement le type void)
Par convention, le nom du mutateur est préfixé par set et le nom de l’accesseur est préfixé par get.

IV.7. Le besoin d’encapsulation


A travers les sections précédentes, vous avez sans doute remarqué que les champs d'une classe
d'objets qui sont mal encapsulés sont accessibles à l'extérieur de la classe. Ceci permettra à des
utilisateurs d’affecter des valeurs arbitraires pour les champs de l'objet sans être contrôlés.
La question qui se pose, ceci doit-t-il être permis? Pour répondre à cette question forte
intéressante, prenons l’exemple d’une voiture. La classe voiture peut être représentée par la
figure suivante :

class voiture {
public int vitesse ;

Figure 299 – Déclaration d’un attribut d’instance avec le modificateur d’accès public
L’attribut vitesse est un attribut publique. Ceci signifie que cet attribut peut être modifié, sans
contrainte, à l’extérieur de sa classe. Que pensez-vous alors de ce bout de code ?
v.vitesse = -220 ;
Vous avez surement compris que cette instruction est aberrante puisque la vitesse d’une voiture ne
peut pas être négative. Pour résoudre un tel problème, la classe doit être parfaitement encapsulée.
Ceci revient à déclarer les attributs de la classe privés et définir des mutateurs et des accesseurs
assurant un accès contrôlé à ces attributs. Cette solution est présentée par le bout de code suivant :
class voiture {
private int vitesse ;
public void set vitesse (int v){
if (v>0&&v<250)
vitesse=v ;
}
public getVitesse(){
return vitesse ;} }

Mohamed HAMMOUDA 26
LES ELEMENTS DU LANGAGE JAVA

Chapitre 4
Les Eléments du Langage Java

Introduction
Java est un langage de programmation à usage général, évolué et orienté objet dont la syntaxe est
proche du C. Ses caractéristiques ainsi que la richesse de son écosystème et de sa communauté lui
ont permis d'être très largement utilisé pour le développement de diverses applications. Java est
largement utilisé pour le développement des applications d'entreprise et mobiles.

Ainsi, la maîtrise des éléments de base java est indispensable afin de pouvoir développer des
applications logicielles de manière correcte, ce qui constitue l’objet de ce chapitre.
Les points à discuter sont les suivants :
 Connaître les types primitifs de java
 Connaître les structure de contrôle de java
 Comprendre et manipuler les chaînes de caractères String
 Comprendre et manipuler les tableaux multidimensionnels en java

Mohamed HAMMOUDA 27
LES ELEMENTS DU LANGAGE JAVA

CHAPITRE 4 : LES ELEMENTS DU LANGAGE


JAVA
I. LES COMMENTAIRES
Comme dans tout langage, les zones de commentaires sont cruciales pour permettre la
compréhension du code. On distingue trois types de commentaires en java :

// les commentaires de fin de ligne /** Les commentaires que le logiciel

/* Les commentaires susceptibles de javadoc pourra utiliser pour générer


unedocumentation HTM*/
s’étendre sur plusieurs lignes */

II. LES TYPES PRIMITIFS


Lorsqu'on écrit des programmes, on a besoin de stocker et manipuler des données. En Java, toute
donnée appartient à un type bien précis. Il y a des données dites de type primitif et des données
de type objet. Dans ce qui suit, nous allons mettre l’accent sur les types primitifs, les types objets
seront discutés tout au long de ce cours.
Il existe huit types primitifs en Java : quatre variantes de nombres entiers, deux variantes de
nombres flottants (c'est-à-dire à virgule), un type booléen (vrai ou faux) et un type caractère.

II.1. Entier et flottant


Il y a quatre types de données entières (byte, short, int et long) et deux types de flottants
(float et double). Ces types diffèrent par l’espace mémoire nécessaire pour stocker une donnée,
et donc par les différentes valeurs qu'il est possible de représenter. Le tableau 3 reprend chacun
de ces types avec la quantité de mémoire utilisée et les valeurs minimales et maximales
représentables.
Tableau 3 – Les types primitifs de Java

Type Bits Minimum Maximum

byte 8 -128 127

short 16 -32 768 32 767

int 32 -2 147 483 648 2 147 483 647

long 64 -9 223 372 036 854 775 808 9 223 372 036 854 775 807

float 32 -3.4e+38 (7 chiffres significatifs) 3.4e+38 (7 chiffres significatifs)

double 64 -1.7e+308 (15 chiffres significatifs) 1.7e+308 (15 chiffres significatifs)

Mohamed HAMMOUDA 28
LES ELEMENTS DU LANGAGE JAVA

II.2. Littéral de type numérique


Un littéral est une valeur explicite utilisée dans le code source d'un programme. On représente les
entiers simplement en donnant leurs valeurs sous forme décimale, binaire, octale ou
hexadécimale. Ces valeurs sont considérées par Java comme des valeurs de type int. On peut
aussi avoir des littéraux de type long ; il suffit de faire suivre le nombre par la lettre l ou L.
Voici plusieurs littéraux qui représentent tous l'entier 45. Les quatre premiers sont de type int et
les quatre derniers sont de type long.
Tableau 4 – Différentes valeurs des littéraux dans java

45 // en décimal (int)

0b101101; // en binaire (int)

055 // en octal (int)

0x2d // en hexadécimal (int)

45L // en décimal (long)


0b101101L; // en binaire (long)

055L // en octal (long)

0x2dL // en hexadécimal (long)

II.3. Caractère
Les caractères sont un autre type fondamental en Java. Ils sont généralement codés sur 7 bits
(Codage ASCII). Ce type de codage permet de représenter 128 caractères représentés dans le
tableau 5.
Tableau 5 – Les différents caractères ASCII dans java

Pour des raisons de flexibilité, ASCII a été étendu sur 8 bits (iso-8859-1 ou iso-latin-1) augmentant
le nombre de caractères représentables à 256. Parmi les caractères ajoutés par rapport à ASCII, il
y a notamment les lettres accentuées non utilisées par les anglophones mais bien par d'autres
Européens de l'Ouest, comme les francophones.
Cependant, malgré cette extension, on ne peut représenter tous les caractères existants. On
pense de suite aux langues asiatiques, arabes, cyrilliques, etc. qui utilisent des symboles. C'est
pourquoi le format Unicode a été créé et a été choisi par les concepteurs de Java.

Mohamed HAMMOUDA 29
LES ELEMENTS DU LANGAGE JAVA

II.4. Booléen
Le dernier type primitif permet de représenter des données booléennes, c'est-à-dire des données
qui ne peuvent prendre que les deux valeurs vrai ou faux. On utilise par exemple le type booléen
pour indiquer si une condition particulière est vraie ou non. On peut aussi l'utiliser pour exprimer
une situation qui comporte deux états différents, comme un interrupteur enclenché ou non.
Il n'y a que deux littéraux booléens représentant simplement les valeurs : true (vrai) et false (faux)

II.5. Compatibilité ascendante des types de variables en Java


Nous venons de voir que java a défini plusieurs types primitifs de tailles différentes. A chaque fois
que vous définissez une variable, vous devez lui attribuer un type. Pour les entiers, par exemple,
nous venons de définir 4 types (long, int, short, et byte) et deux type réel (float et double). Il est
évident que l’affectation d’une variable de type float à une autre variable de même type
s’effectue correctement.
Mais la question qui se pose, que ce passe-t-il si nous affectons un float à un double et vice versa ?

La réponse est simple, en fait java permet de garantir la compatibilité ascendante des types des
variables. Cela veut dire qu’il est possible d’affecter une variable d’un type donné à une autre
variable de même type de taille différente, à condition que la taille du premier type soit inférieure
ou égale à la taille du deuxième type. Nous pouvons assimiler ceci au comportement des poupées
russes où la plus petite peut être imbriquée dans celle qui lui est directement plus grande.
Ainsi, une variable de type float peut être affectée à une variable de type double, mais l’inverse
n’est pas vrai.

long int short byte double float


Figure 30 – Emboitement des types primitifs dans java

II.6. Les opérateurs


Les opérateurs sont des symboles qui permettent d’effectuer des opérations arithmétiques et
logiques sur des variables. En java, on distingue plusieurs types d'opérateurs :
• les opérateurs de calcul
• les opérateurs d'assignation
• les opérateurs d'incrémentation
• les opérateurs de comparaison
• les opérateurs logiques
• (les opérateurs bit-à-bit)
• (les opérateurs de rotation de bit)

Mohamed HAMMOUDA 30
LES ELEMENTS DU LANGAGE JAVA

Une liste des opérateurs disponibles en Java est présentée par ordre de priorité décroissante
dans le tableau 7
Tableau 6 – Les différents opérateurs définis dans java

Syntaxe Sémantique
x=y x prend la même valeur que celle de y (affectation)
x == y vrai si x = y, faux sinon (test d’´égalité)
x != y faux si x = y, vrai sinon (test de différence)
x<y x < y (inférieur)
x>y x > y (supérieur)
x <= y x ≤ y (inférieur ou égal)
x >= y x ≥ y (supérieur ou égal)
x+y x + y (addition)
x–y x − y (soustraction)
x*y x × y (multiplication)
x/y x/y (division)
x%y x mod y (modulo)
++x pré-incrémentation de x
--x pré-décrémentation de x
x++ post-incrémentation de x
x-- post-décrémentation de x
x += y équivaut à x = x + y
x *= y équivaut à x = x ∗ y
x -= y équivaut à x = x − y
x /= y équivaut à x = x/y
x %= y équivaut à x = x mod y
x && y conjonction logique de booléens
x || y disjonction logique de booléens
!x négation logique d’un booléen
x&y conjonction logique bit à bit d’entiers
x|y conjonction logique bit à bit d’entiers
~x négation logique bit à bit d’entier
^x disjonction exclusive (xor) bit à bit d’entier
x<<n décalage à gauche des bits de x (0 à droite)
x>>n décalage à droite des bits de x (bit de signe propagé)
x>>>n décalage à droite des bits de x (0 à gauche)
(T)x transtypage x vers le type T (si possible)

II.7. Instruction conditionnelle


Les instructions conditionnelles permettent de faire des choix dans un programme. Elles
permettent d'altérer le déroulement du programme en fonction de la valeur de condition. Il y a
trois instructions conditionnelles en Java : l'instruction if, l'instruction if-else et
l'instruction switch.

Mohamed HAMMOUDA 31
LES ELEMENTS DU LANGAGE JAVA

II.7.1. Instruction if
Une instruction if consiste en le mot réservé if suivi d'une condition entre parenthèses, et enfin
d'une instruction. La figure 32 montre la syntaxe de l'instruction if.

Figure 31 – Syntaxe de l'instruction if.


En ce qui concerne l'exécution de l'instruction if, elle est illustrée par l’Organigramme présentée à
la figure 33. Son principe est simple. Tout d’abord la condition est évaluée. Si elle est vraie, un
traitement sera exécuté. Dans le cas contraire, c’est la fin de l’instruction.

Figure 32 – Organigramme de l'instruction if

if ( diviseur == 0)

System.out.println ( " Division


par 0 impossible ! ");

Si on veut exécuter plus d'une instruction en fonction d'une condition, il suffit d'utiliser un bloc de
code.

if ( points >= 10 ){
int percentage = points * 5 ;
System.out.print ("Vous avez réussi votre examen avec ") ;
System.out.print (percentage) ;
System.out.println ("%") ;}

Ce bloc de code est tout naturellement appelé bloc if de l'instruction if.

II.7.2. Instruction if-else


Parfois, on aimerait exécuter une instruction si une condition est vérifiée et une autre si la
condition n'est pas vérifiée. L’instruction if-else permet de répondre à ce besoin.
La figure 34 présente la syntaxe de l'instruction if-else. La première partie est exactement la
même que pour l'instruction if; vient ensuite la seconde partie constituée du mot
réservé else suivi d'une instruction. Lorsque cette instruction est un bloc, il est appelé bloc else.

Mohamed HAMMOUDA 32
LES ELEMENTS DU LANGAGE JAVA

Tout comme pour l'instruction if, il est recommandé de toujours en utiliser un.

Figure 33 – Syntaxe de l’instruction if-else


L'instruction if-else représente un aiguillage avec deux sorties possibles. La condition est tout
d'abord évaluée et si sa valeur vaut true, instruction1 est exécutée, sinon c'est instruction2 qui est
exécutée. Le programme continue ensuite son exécution. La figure 35 montre l’organigramme de
l'instruction if-else.

Figure 34 – Organigramme de l’instruction if-else


Si on a plusieurs instructions et que chacune de celles-ci ne peut être exécutée que si une certaine
condition est vérifiée et qu'on ne veut en exécuter qu'une seule, on va devoir utiliser plusieurs
instructions if-else imbriquées les unes dans les autres. Le programme suivant affiche une lettre
correspondante à des points sur 20 en suivant la correspondance suivante : A (18–20), B (16–18),
C (14–16), F (0–14).

char grade;
if (20 <= points && points <= 18){
grade = 'A';
else{
if (18 < points && points <= 16{
grade = 'B'; }
else {
if (16 < points && points <= 14) {
grade = 'C'; }
else{
gradede = 'F';}
}
}}

II.7.3. Instruction switch


La dernière instruction conditionnelle disponible en Java est l'instruction switch. Cette instruction
représente un aiguillage multiple : le choix est fait en fonction de la valeur d'une variable; chaque
chemin possible correspond à une valeur différente pour la variable. L'instruction switch est tout
simplement un raccourci d'écriture pour l'instruction if-else.
La Figure 36 montre la syntaxe de l'instruction switch. On commence avec le mot

Mohamed HAMMOUDA 33
LES ELEMENTS DU LANGAGE JAVA

réservé switch suivi d'une expression entre parenthèses. Vient ensuite un bloc de code qui
contient zéro, un ou plusieurs case et éventuellement un default. Chaque case est défini par le
mot réservé case suivi d'une valeur et de deux points ( : ) et d'une séquence d'instructions. Le cas
par défaut est construit avec le mot réservé default suivi de deux points et d'une séquence
d'instructions.

Figure 35 – Syntaxe de l’instruction switch


La figure 37 illustre le déroulement d'une instruction switch en supposant que tous les case se
terminent par une instruction break. L'expression est donc évaluée et ensuite, si un
des case correspond à sa valeur, les instructions du case correspondant sont exécutées. Sinon, ce
sont celles du default qui sont exécutées.

Figure 36 – Organigramme de l’instruction switch


L'expression du switch doit être de type (ou doit pouvoir être converti en) int ou char. De plus, les
valeurs utilisées dans les case doivent être des expressions constantes : c'est-à-dire soit des
littéraux, soit des constantes (définies avec final), soit des expressions dont tous les opérandes
sont des constantes.

Int erreur ;
switch (erreur){
case 1 : afficher("Argument de fonction non valide";
erreur=0; break;
case 2 : afficher("Syntaxe incorrecte");
erreur=0; break;
default : afficher("Erreur inconnue");}

II.8. Instructions répétitives


Les instructions répétitives, également appelées instructions itératives ou boucles, permettent de
répéter un certain nombre de fois une instruction ou un bloc de code. Il y a quatre types de
boucle en Java : l'instruction while, l'instruction do-while, l'instruction for et l'instruction for-
each. Cette section expose les trois premiers types; la boucle for-each sera discutée plus loin dans
ce cours.

Mohamed HAMMOUDA 34
LES ELEMENTS DU LANGAGE JAVA

II.8.1. Instruction while


L'instruction while permet de répéter une instruction ou un bloc de code tant qu'une condition
est vraie. La condition est une expression booléenne, comme pour l'instruction if-else.
La figure 38 montre une instruction while formée du mot réservé while suivi d'une condition entre
parenthèses et d'une instruction. Cette instruction est appelée corps de la boucle.

Figure 37 – Syntaxe de la boucle while


L'exécution d'une boucle while commence par l'évaluation de la condition. Si la valeur de celle-ci
est false, la boucle est terminée et le programme continue son exécution. Si la valeur de la
condition est true, le corps de la boucle est exécuté, et la condition est ensuite à nouveau évaluée
et on recommence. Le corps de la boucle est donc exécuté tant que la condition est vérifiée. La
figure 39 illustre l'exécution d'une boucle while. L'exécution d'une boucle est une succession
d'itérations; une itération correspondant à une exécution du corps de la boucle.

Figure 38 – Organigramme de la boucle while

int i = 100, j = 0, somme = 0;


while (j <= i)
{ somme += j;
j++;
}

A la sortie de la boucle, la variable somme contient la somme des 100 premiers entiers.

II.8.2. Instruction do-while


L'instruction do est similaire à l'instruction while sauf que l'ordre dans lequel les instructions sont
exécutées est différent. Le corps de la boucle est d'abord exécuté, puis la condition est évaluée. Si
sa valeur est false, le programme continue; sinon, le corps de la boucle est à nouveau exécuté,
etc... Ceci implique donc que le corps de la boucle sera exécuté au moins une fois dans tous les
cas. La figure 40 illustre la syntaxe de cette boucle. Faîtes bien attention qu'il faut un point-virgule
( ; ) après la condition.

Mohamed HAMMOUDA 35
LES ELEMENTS DU LANGAGE JAVA

Figure 39 – Syntaxe de la boucle do-while


Comme le montre la figure 41, on voit bien que le corps de la boucle est exécuté au moins une
fois. La condition est vérifiée après chaque itération et pas avant comme pour les boucles while.
Notez qu'il est toujours possible de transformer une boucle construite avec l'instruction do en une
boucle while. Néanmoins, il est parfois plus clair et lisible d'utiliser l'instruction do, notamment
pour mettre en avant le fait que le corps de la boucle sera exécuté au moins une fois.

Figure 40 – Organigramme de l’instruction do-while

int i = 100, j = 0, somme = 0 ;


do{
somme+=j;
j++;
}
while (j <= i);

II.8.3. Instruction for


Les deux boucles qu'on vient de découvrir sont intéressantes si vous ne savez pas de manière
explicite combien de fois vous voulez que le corps de la boucle soit exécuté. Si on veut exécuter
une instruction ou un bloc de code un nombre précis et connu de fois, on va préférer utiliser
l'instruction for.
Comme le montre la figure 42, l'instruction for commence par le mot réservé for suivi de trois
éléments séparés par des points-virgules, le tout mis entre parenthèses, suivi du corps de la
boucle. Les trois éléments de la boucle for sont respectivement appelés l'initialisation,
la condition et la mise à jour; elles sont toutes optionnelles.

Figure 41 – Syntaxe de de l’instruction for

Mohamed HAMMOUDA 36
LES ELEMENTS DU LANGAGE JAVA

Si vous ne spécifiez pas la partie condition, c'est comme-ci vous aviez mis true comme condition.
Voyons maintenant comment se déroule l'exécution d'une boucle for avec la figure 43. La partie
initialisation est exécutée une seule fois, au début. Ensuite, la condition est évaluée : si sa valeur
est false, la boucle s'arrête et le programme continue. Par contre, si la condition est vérifiée, le
corps de la boucle est exécuté suivi par la partie mise à jour. Enfin, la condition est à nouveau
évaluée et on boucle.

Figure 42 – Organigramme de l’instruction for


Dans la partie initialisation, on peut soit déclarer et initialiser une nouvelle variable, soit juste
affecter une valeur à une variable déjà existante. Dans la partie mise à jour, on retrouve une
instruction.

Nt somme = 0;
for (int i=0; i<=100; i++)
somme+= i;

II.8.4. Instructions break et continue


L’instruction break est utilisée pour sortir immédiatement d’un bloc d’instructions (sans traiter les
instructions restantes dans ce bloc). Dans le cas d’une boucle, on peut également utiliser
l’instruction continue avec la déférence suivante :
 break : l’exécution se poursuit après la boucle (comme si la condition d’arrêt devenait
vraie) ;
 continue : l’exécution du bloc est arrêtée mais pas celle de la boucle. Une nouvelle itération
du bloc commence si la condition d’arrêt est toujours vraie.

f or (int i = 0, j = 0 ; i < 100 ; i++) {


if (i > tab.length) {
break ;
}
if (tab[i] == null) {
continue ;
}
tab2[j] = tab[i];
j++;
}

Mohamed HAMMOUDA 37
LES ELEMENTS DU LANGAGE JAVA

II.9. Portée des identificateurs


Java est un langage structuré en blocs. Toute variable déclarée dans un bloc est visible dans ce
bloc et dans tous les blocs imbriqués dans ce bloc. En java, les blocs peuvent contenir :
 des classes,
 des méthodes,
 des instructions composées,
 des corps de boucles,
 des try...catch
Deux règles à appliquer pour la visibilité des variables :
1. Il est interdit de redéfinir une variable déjà déclarée dans une méthode soit :
- comme paramètre de la méthode,
- comme variable locale à la méthode,
- dans un bloc inclus dans la méthode.
2. Il est possible de redéfinir une variable déjà déclarée dans une classe (attribut), dans ce
cas, l’attribut de la classe ne sera plus visible dans le bloc où la variable locale a été
déclarée.
A travers la figure 44, nous allons essayer d’expliquer le principe de visibilité des variables dans
java.

Classe X
public int a,b ;
Méthode F
char c=’2’ ;
Boucle For
int a = 4, i=5 ;
Bloc If

int b=0 ;

a=4 ;

b++
Figure 43 – Visibilité des variables à travers des blocs imbriqués
- La variable "a" déclarée dans la classe X est masquée dans la méthode F dans le bloc
imbriqué for.
- La variable "b" déclarée dans la classe X est masquée dans la méthode F dans le bloc
imbriqué If.

Mohamed HAMMOUDA 38
LES ELEMENTS DU LANGAGE JAVA

- Dans le bloc If, c'est la variable b interne à ce bloc qui est utilisée car elle masque la
variable b de la classe.
- Dans le bloc If, c'est la variable a du bloc For qui est utilisée car elle masque la
variable a de la classe.
- Dans le bloc de la classe X, c’est la variable b de la classe qui est incrémenté vu que la
variable b du bloc If n’est plus visible à cet endroit.
- Dans l'instruction b++, c'est la variable b de la classe qui est utilisée car la variable b du bloc
If n’est plus visible à cet endroit.
Si nous rajoutons dans le bloc If la définition d'une nouvelle variable interne a à ce bloc, java
produit une erreur de compilation sur la variable a en indiquant que c'est une redéfinition de
variable à l'intérieur de la méthode F, car nous avions déjà défini une variable a dans le bloc
englobant For. La figure 45 permet d’illustrer ce cas.

Classe X
public int a,b ;
Méthode F
char c=’2’ ;
Boucle For
int a = 4, i=5 ;
Bloc If

int b=0, a;

a=4 ;

b++ ;

Figure 44 – une variable ne peut pas être déclarée plus qu’une fois dans des blocs imbriqués

III. LES CHAINES DE CARACTERES : LA CLASSE STRING


Les chaînes de caractères sont des concepts très importants dans les langages de programmation.
Java ne fait pas l’exception et propose une classe String assurant la création et la manipulation
des chaînes de caractères. Une chaîne de caractères dans java n’est autre qu’un objet de la classe
String. Nous avons déjà manipulé quelques chaînes de caractères dans les chapitres précédents.
Dans cette section, nous allons essayer de mettre encore plus l’accent sur cette classe très utile
dans le développement des applications java.

III.1. Création d’une chaine de caractère


La création des objets de type chaîne de caractères peut être réalisé de deux manières : soit en
utilisant l’opérateur new, soit en affectant un littéral chaîne de caractères à une variable String.
Par contre, vous allez être surpris du comportement de la JVM vis-à-vis de ces deux techniques de
création des objets String.

Mohamed HAMMOUDA 39
LES ELEMENTS DU LANGAGE JAVA

String str1 = new String("Mohamed"); Création de deux objets String avec l'opérateur new La comparaison
String str2 = new String("Mohamed"); entre la variable str1
System.out.println(str1 == str2); et str2 retourne false

String str3 = "Wahid"; Création de deux objets String par l'opérateur La comparaison
String str4 = "Wahid"; d’affectation entre la variable str3
System.out.println(str3 == str4); et str4 retourne true

Figure 45 – création d’objets string de deux manières différentes

Dans l'exemple illustré par la figure 46, les variables str1 et str2 permettent de référencer deux
objets String différents créés par l’opérateur new. Cela veut dire que chacune des variables
contient une adresse mémoire différente de l’autre, ce qui explique le résultat false de la
comparaison malgré que les deux chaînes aient la même valeur.
NB. L’opérateur (==) permet de comparer les adresses mémoires des objets en question et non
pas leurs valeurs.
Dans le deuxième cas, nous avons utilisé l’operateur d’égalité afin de référencer deux objets
String ayant une même valeur. Alors que nous nous attendons à un comportement identique au
premier cas, le résultat été complètement surprenant vu que le résultat de la comparaison est
true.
En fait, java place tout objet créé par l’opérateur d’affectation dans un endroit spécifique de la
mémoire centrale appelé pool. Mais pour des raisons de performance, java cherche d’abord s’il
existe un objet ayant la même valeur. Si c’est le cas, il récupère sa référence dans la mémoire et
l’affecte à sa variable de référence, dans le cas contraire, il se trouve dans l’obligation de le créer.
Ainsi, le deuxième cas, java vérifie s’il existe un objet String dont la valeur est « wahid », dans le
pool. Et comme il s’agit du premier objet String ayant la valeur « wahid », java le crée et affecte
sa référence à la variable str3. Pour le deuxième objet, java refait la même chose, mais cette fois-
ci détecte la présence d’un objet String ayant la même valeur. Dans ce cas, il récupère son adresse
et l’affecte à str4. Nous nous trouvons à la fin avec deux variables de référence ayant la même
adresse, ce qui explique le résultat true.

Str3 Str3
salut salut

Med Waw ! Je vais pas Med


Sh créer un autre Objet Sh
Si je pourrais trouver String dans le string
Str4 = ‘Med‘ ; un Objet String avec pool. str4 peut point
la valeur « Med » chat à ce String chat
dans pool Str4

Pool d’objets String Pool d’objets String

Figure 46 - la séquence d'actions qui s'exécute lorsque java localise un string dans le pool des
objets string

Mohamed HAMMOUDA 40
LES ELEMENTS DU LANGAGE JAVA

III.2. Quelque opérations sur les chaines de caractères


III.2.1. Informations sur la chaîne
Il est possible de connaître le nombre de caractères que contient une chaîne de caractères en
utilisant la méthode length(). La méthode isEmpty() vous permet de savoir si une chaîne est vide
ou pas (c'est-à-dire qu'elle ne comporte aucun caractère). Vous pouvez ensuite obtenir le
caractère se trouvant à une certaine position dans la chaîne en utilisant la méthode charAt(). Le
programme suivant affiche une chaîne de caractères.

String str = "Hello";


for (int i = 0; i < str.length(); i++)
{
System.out.print (str.charAt (i));
} listing js1.9 Afficher une chaine de caractères

Comme vous avez pu le constater, le premier caractère a pour indice 0. C'est exactement comme
pour les tableaux. Vous pouvez également transformer une chaîne de caractères en un tableau de
type primitif char grâce à la méthode toCharArray().
Les méthodes startsWith() et endsWith() permettent de savoir si une chaîne de caractères
commence ou se termine par une sous-chaîne de caractères. La méthode contains() permet de
savoir si une chaîne de caractères contient une autre chaîne.

String str = "Hello World !";


boolean b1 = str.startsWith ("Hell"); //
b1 vaut true
boolean b2 = str.endsWith ("World"); //
b2 vaut false
boolean b3 = str.contains ("ll"); // b3
vaut true

Une fois qu'on sait qu'une chaîne de caractères se retrouve dans une autre, on a parfois envie de
savoir à quelle position. Pour ce faire, java définit les deux méthodes indexOf() et lastIndexOf().
Les deux méthodes renvoient l'indice de la première lettre de la chaîne recherchée si elle existe,
sinon, elles renvoient -1.

String str = "Hello World !";


int p1 = str.indexOf ("Hell"); // p1 vaut 0
int p2 = str.indexOf ("World"); // p2 vaut 6
int p3 = str.indexOf ("z"); // p3 vaut -1
int p4 = str.indexOf ("o"); // p4 vaut 4
int p5 = str.indexOf ("o", 5); // p5 vaut 7
int p6 = str.lastIndexOf ("o"); // p6 vaut 7

Comme vous avez pu le constater, il y a deux versions de la méthode indexOf() (surcharge de


méthodes). La seconde version prend deux paramètres qui sont la chaîne recherchée et l'indice à
partir duquel il faut commencer à chercher.

Mohamed HAMMOUDA 41
LES ELEMENTS DU LANGAGE JAVA

III.2.2. Comparer deux chaînes


Pour savoir si deux chaînes de caractères sont identiques, on utilise la méthode equals(). Si on
veut ignorer la casse, on doit utiliser la méthode equalsIgnoreCase().
La classe String implémente l'interface Comparable<String>. Vous pouvez donc utiliser la
méthode compareTo() de cette interface pour comparer deux chaînes en utilisant l'ordre
lexicographique. La classe String propose, en plus, une méthode compareToIgnoreCase() qui
compare deux chaînes en ignorant la casse.

String str = "Hello World !";


boolean b1 = str.equals ("HELLO World !"); // b1 vaut false
boolean b1 = str.equals ("HELLO World !"); // b1 vaut false
boolean b2 = str.equalsIgnoreCase ("HELLO World !"); // b2 vaut true
int i1 = str.compareTo ("HELLO World !"); // i1 vaut 32
int i2 = str.compareToIgnoreCase ("HELLO World !"); // i2 vaut 0

La chaine "Hello World !" est plus grande que la chaine "HELLO World !" car les majuscules sont
avant les minuscules.

III.2.3. Extraire une sous-chaine

String str = "Hello World !";


String s1 = str.substring (0); // s1 vaut "Hello World !"
String s2 = str.substring (4); // s2 vaut "o World !"
String s3 = str.substring (0, 4); // s3 vaut "Hell"

On peut extraire une sous-chaîne à partir d'une chaîne de caractères. Pour ce faire, on peut
utiliser la méthode substring(). Cette méthode existe en deux versions : la première prend
deux int en paramètres qui représentent l'indice de début et l'indice de la fin de la sous-chaîne à
extraire, et la seconde version ne prend que l'indice de début et extrait jusqu’à la fin de la chaîne.

III.2.4. Modification d’une Chaine


Voyons maintenant des méthodes pour modifier une chaîne de caractères. Attention, n'oubliez
pas que la chaîne originale n'est pas modifiable. Les String sont immuables, mais un nouvel objet
String est renvoyé. Les méthodes toLowerCase() et toUpperCase() permettent de changer toutes
les lettres en minuscules et en majuscules, respectivement. La méthode trim() permet de
supprimer tous les caractères blancs (espace, tabulation, saut de ligne, retour chariot) se trouvant
au début et à la fin d’une chaîne.

String str = " Hello World ! ";


String s1 = str.toLowerCase(); // s1 vaut " hello world ! "
String s2 = str.toUpperCase(); // s2 vaut " HELLO WORLD ! "
String s3 = str.trim(); // s3 vaut "Hello World !"

On peut également remplacer toutes les sous-chaînes par une nouvelle sous-chaîne dans une
chaîne de caractères à l'aide de la méthode replace(). Cette méthode existe aussi en deux
versions : la première pour remplacer des caractères et la seconde pour remplacer des chaînes.

Mohamed HAMMOUDA 42
LES ELEMENTS DU LANGAGE JAVA

String str = "Hello World !";


String s1 = str.replace ('o', 'e'); // s1 vaut "Helle Werld !"
String s2 = str.replace ("ll", "l"); // s2 vaut "Helo World !"

La classe String propose une méthode qui permet de découper une chaîne, mais elle se base sur
les expressions régulières que l'on verra dans le chapitre suivant.
On va utiliser la classe StringTokenizer qui permet de découper une chaîne de caractères en
fonction d'une autre chaîne. Par défaut, le découpage se fera en fonction du caractère espace.
Voyons tout d'abord un exemple d'utilisation.

String str = "John Doe 1976 Oregon";


StringTokenizer tokenizer = new StringTokenizer (str);
while (tokenizer.hasMoreTokens())
{
System.out.println (tokenizer.nextToken());
}

L'exécution de ce programme affiche sur la sortie standard :


John
Doe
1976
Oregon
La chaîne a été découpée en éléments appelés token. Le découpage est réalisé en se basant sur le
caractère espace. Pour pouvoir récupérer les tokens, il faut utiliser la
méthode hasMoreTokens() qui permet de savoir s'il reste encore un token, et la
méthode nextToken() qui renvoie le token suivant.
L'un des désavantages de cette technique est que si on est uniquement intéressé par le dernier
token, on est quand même obligé de faire toute la boucle pour arriver au dernier. On verra
au chapitre suivant une technique plus avancée et beaucoup plus riche.

III.3. La classe StringBuffer


Si vous désirez modifier une chaîne de caractères ou construire une nouvelle par concaténation
de caractères, l'utilisation de la classe String peut coûter cher en termes de performance étant
donné le caractère immuable des String. La création d'objets est en fait une opération qui peut
prendre du temps.
Afin de résoudre ces problèmes, la librairie standard Java contient la classe StringBuffer qui
représente également une chaîne de caractères mais les objets de type StringBuffer sont
mutables. En conséquence, les besoins évoqués seront possibles et plus rapides. Commençons
par un exemple.
La première boucle avec des String prend en moyenne 47587891 nanosecondes, tandis que la
seconde avec StringBuffer prend en moyenne 1234793 nanosecondes. On gagne plus qu'un
facteur de 10.

Mohamed HAMMOUDA 43
LES ELEMENTS DU LANGAGE JAVA

// Construction d'un tableau d'entiers


int[] tab = new int[1000];
for (int i = 0; i < tab.length; i++)
{
tab[i] = i + 1;
}

// Avec String
String s = "";
for (int i = 0; i < tab.length; i++)
{
s += tab[i] + ", ";
}

// Avec StringBuffer
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < tab.length; i++)
{
buffer.append (tab[i]).append (", ");
}

listing js1.15 Comparaison


de String avec StringTokenizer

La classe StringBuffer propose les mêmes méthodes que la classe String. Le tableau 8, ci-dessous
présenté, établit une correspondance entre les méthodes des deux classes.
Tableau 8 – Correspondance entre les méthodes des classes String et StringBuffer

String StringBuffer
Informations sur la chaîne
length length
isEmpty pas d'équivalent
charAt charAt
startsWith et endsWith pas d'équivalent
indexOf et lastIndexOf indexOf et lastIndexOf
Comparaison de deux chaînes
equals equals
equalsIgnoreCase, compareTo etcompareToIgnoreCase pas d'équivalent
Extraction d’une sous-chaine
substring substring
Modification d’une chaine
toLowerCase, toUpperCase et trim pas d'équivalent
replace pas d'équivalent (attention, une
méthode replace existe mais elle fait
autre chose)

Mohamed HAMMOUDA 44
LES ELEMENTS DU LANGAGE JAVA

Comme c’est montré dans le tableau 8, certaines méthodes de la classe String ne se trouvent pas
dans la classe StringBuffer. Outre les méthodes que l'on vient de voir dans le tableau, la
classe StringBuffer propose d'autres méthodes très intéressantes.

III.3.1. Concaténation
Pour concaténer une donnée de type quelconque avec un objet de type StringBuffer, on ne peut
pas utiliser l'opérateur +comme pour les String, il faut utiliser la méthode append().
Avec les StringBuffer, on n'est pas obligé de concaténer à la fin de la chaîne, on peut concaténer à
n'importe quelle position grâce à la méthode insert().

StringBuffer buff = new StringBuffer();


buff.append ("Hell");
buff.append (" World ");
buff.append ('!');
buff.insert (4, 'o');
System.out.println (buff); // Affiche Hello World !

III.3.2. Suppression
On peut également supprimer des caractères ou des sous-chaînes très facilement grâce aux
méthodes delete() qui supprime une sous- chaîne dont on donne les indices de début et de fin
et deleteCharAt() qui supprime un caractère à un indice quelconque.

StringBuffer buff = new StringBuffer ("Hellllo World !!");


buff.delete (2, 4);
buff.deleteCharAt (12);
System.out.println (buff); // Affiche Hello World !

III.3.3. Modification
On a déjà vu les méthodes append() et insert() qui permettent de modifier une chaîne de
caractères. Il y a aussi les méthodes replace(), reverse() et setCharAt() qui permettent de
modifier une chaîne. La première méthode remplace une sous-chaîne par une autre. La seconde
méthode inverse une chaîne et la dernière permet de changer un caractère à une position bien
précise.

StringBuffer buff = new StringBuffer ("Hello Mum .");


buff.replace (6, 9, "World");
buff.setCharAt (12, '!');
buff.reverse();
System.out.println (buff); // Affiche ! dlroW olleH

Mohamed HAMMOUDA 45
LES ELEMENTS DU LANGAGE JAVA

IV. LES TABLEAUX


Un tableau est tout à bord un objet qui stocke une collection de valeurs. Cette collection de
valeurs peut être de type primitif ou type objet.
Un tableau primitif stocke une collection de valeurs de type primitif (Les types primitifs seront
discutés dans le chapitre suivant de ce cours).
Un tableau d'objets stocke non pas des objets concrêts mais plutôt des variables de référence qui
pointent vers des objets.

4 Frad

intTableau
8
objTableau
Imen Objets
107 Fraj String
hamza

Tableau de donnée primitive Tableau d’objets

Figure 47 – Les types de tableaux en java


En Java, vous pouvez définir des tableaux unidimensionnels et multidimensionnels. Un tableau
unidimensionnel est un objet qui fait référence à un ensemble de valeurs scalaires. Un tableau à
deux dimensions est considéré comme un tableau multidimensionnel. Il se réfère à une collection
d'objets dans laquelle chaque objet est lui-même un tableau unidimensionnel. De même, un
tableau à trois dimensions se réfère à un ensemble de tableaux à deux dimensions, etc.
Notez que les tableaux multidimensionnels peuvent ou pas contenir le même nombre d’éléments
dans chaque ligne ou colonne comme c’est illustré dans le tableau à deux dimensions de la
figure49.
La création d'un tableau doit passer par trois étapes, comme suit:
 Déclaration du tableau
 Allocation de l’espace nécessaire pour le tableau
 Initialisation des éléments du tableau

Elément Tableau

Elément Tableau

Elément
Tableau

Tableau une dimension Tableau à deux dimensions Tableau à trois dimensions

Figure 48- Un tableau peut être unidimentionnel ou multidimentionnel

Mohamed HAMMOUDA 46
LES ELEMENTS DU LANGAGE JAVA

IV.1. Déclaration d’un tableau

int intTableau1[]; Déclaration d’un tableau de valeurs de type


int[] intTableau2 primitif int

String[] strTableau1;
Déclaration d’un tableau d’objet de type String
String strTableau2[];

int[] multiTableau1[];
int multiTableau2 [][]; Déclaration d’un tableau multidimensionnel
int[][] multiTableau3 ; d’objet de type int

Figure 49 – Exemples de déclaration de tableaux

IV.2. Allocation d’un tableau


Comme son nom l'indique, l'allocation d’un tableau revient à allouer de l’espace mémoire pour
ses éléments. Lorsque vous allouez de la mémoire pour un tableau, vous devez spécifier ses
dimensions, tel que le nombre d'éléments que le tableau doit stocker. Rappelez-vous qu’un
tableau est un objet et que tout objet est créé en utilisant l’opérateur new. De même, un tableau
est créé en utilisant le mot-clé new, suivi par le type des données qu'il stocke, puis sa taille. Voici
quelques exemples illustrés par la figure 51.

Figure 50 – Allocation de l’espace mémoire aux tableaux

Notez qu’il est possible d’allouer un tableau multidimensionnel en spécifiant une seule dimension
au lieu de deux. Dans la section précédente, nous avons dit qu’un tableau bidimensionnel
(dimension =2) n’est autre qu’un tableau dans chaque élément est un tableau.

Figure 51 – Les étapes de déclaration d’un tableau en java

Mohamed HAMMOUDA 47
LES ELEMENTS DU LANGAGE JAVA

IV.3. Initialisation d’un tableau


Initialiser un tableau revient à affecter des variables de types primitifs ou bien des variables de
références aux éléments du tableau d’une manière combinée ou pas avec la déclaration.

int intTableau1[];
intTableau1= new int[2] ;
intTableau1[0]=1 ; Affectation des
intTableau1[1]=10 ; éléments à un
String[] strTableau1;
tableau après sa
strTableau1= new String[8]; ❶
strTableau1[0]= "Frad" ; déclaration
strTableau1[2]= new String("wahid");

int[][] multiTableau1 ;
multiTableau1 = new int[2][2] ;
multiTableau1 [0][0]=12; ❷
multiTableau1[0][1]=32;
int multiTableau2=new int[3][] ;
mutliTableau2[0]=new int[2];
multiTableau2[0][0]=22;
multiTableau[0][1]=25 ;
multiTableau2[1][0]=120 ; Ce code génère une
erreur
int intTableau2= new int[]{1,10} ; ❸ Déclaration et
String strTableau2= new String[] {"Fradd",new("wahid"} ; affectation
int multiTableau3= new int[][]{{1,2,3},{11,22,33}}; combinées

Figure 52 – Différentes techniques sont utilisées pour déclarer et instancier un tableau


La figure 53 permet de présenter les différentes techniques d’affectation des éléments d’un
tableaux. La partie ❶ a été consacrée pour la déclaration puis l’affectation des tableaux
unidimensionnels. La partie ❷ montre comment déclarer un premier tableau
multidimensionnel « multiTableau1 » composé d’un tableau dont chaque élément pointe sur un
autre tableau de taille 2 et c’est seulement le premier tableau pointé qui a été rempli. Le
« multiTableau2 » déclare au début un tableau de 3 éléments pouvant référencer des tableaux
dont la taille est encore inconnue. Dans une deuxième étape, nous avons affecté au premier
élément, un tableau d’entier de taille 2. Puis, nous avons affecté à ce tableau les valeurs 22 et 25.
En fin, la ligne marquée en rouge indique une erreur de compilation puisque, à ce stade, il n’existe
pas un tableau référencé par le deuxième élément du tableau. La partie ❸ décrit comment
combiner les étapes de déclaration et d’affectation d’un tableau.

IV.4. Les tableaux dynamiques


Les tableaux que nous avons créés actuellement sont des tableaux dont la taille est fixée à la
création. Cette taille, une fois fixée, ne peut plus varier, on parle de tableaux statiques. Il est
possible de créer des tableaux dont la taille peut varier au cours de l'exécution, on parle alors
de tableaux dynamiques.

Mohamed HAMMOUDA 48
LES ELEMENTS DU LANGAGE JAVA

Différentes classes sont fournies dans la librairie standard Java pour manipuler des tableaux
dynamiques, c'est-à-dire des tableaux dont la taille peut varier au cours du temps. Les classes
dont nous allons parler ici sont les classes Vector et ArrayList. Ces classes font partie du
package java.util, il ne faut donc pas oublier d'importer le package pour utiliser ces classes.

import java.util.Vector;
import java.util.ArrayList;

IV.4.1. La classe Vector


Contrairement aux tableaux, on ne déclare pas le type des données que va contenir le tableau
dynamique. Ces tableaux ne peuvent contenir que des références vers des objets. Voici, par
exemple, un tableau dynamique de type Vector qui contient des objets de type String.

Vector vec = new Vector();


vec.add ("premier élément ajouté");
vec.add ("second élément ajouté");
System.out.println (vec);

L'exécution affichera à la console : [ premier élément ajouté, second élément ajouté ]

IV.4.2. La classe Arraylist


On peut obtenir exactement la même chose avec la classe ArrayList. En fait, les deux
classes Vector et ArrayList partagent les mêmes méthodes puisque toutes les deux implémentent
l'interface LIST (la notion d’interface sera abordée plus en détail plus loin dans ce cours).
Le tableau 9, ci-dessous présenté, reprend quelques méthodes intéressantes de l'interface List.
Tableau 9 – Quelques méthodes de l’interface List

Informations sur l'état de la liste


int size(); Récupère le nombre d'éléments dans la liste
boolean isEmpty(); Permet de savoir si la liste est vide
boolean contains (Object o); Permet de savoir si la liste contient l'objet o
Object get (int i); Renvoie l'objet se trouvant à l'indice i
Agir sur les éléments de la liste
void add (Object o); Ajoute l'objet o en fin de liste
void add (int i, Object o); Ajoute l'objet o à l'indice i
void set (int i, Object o); Remplace l'objet de l'indice i par l'objet o
Object remove (int i); Retire l'objet de l'indice i et le renvoie
boolean remove (Object o); Retire l'objet o de la liste et renvoie true ou
false si l'objet n'existe pas dans la liste
void clear(); Vide complètement la liste

Mohamed HAMMOUDA 49
LES ELEMENTS DU LANGAGE JAVA

Voici un petit exemple d'utilisation des listes qui montre l'usage et l'effet de quelques-unes des
méthodes de l'interface List. Notez que l'on peut sans aucun problème
remplacer ArrayList par Vector étant donné que les deux classes implémentent l'interface List,
ceci illustre bien une grande utilité du polymorphisme.

List list = new ArrayList() ; // On peut remplace par Vector()


list.add ("un");
list.add ("deux");
list.add ("trois");
System.out.println (list);
System.out.println (list.size());
System.out.println (list.isEmpty());
list.remove ("deux");
System.out.println (list);
list.clear();
System.out.println (list);
System.out.println (list.isEmpty());

Mohamed HAMMOUDA 50
L’HERITAGE DANS JAVA

Chapitre5
L’Héritage dans Java

Introduction
Tous les êtres vivants héritent des caractéristiques et des comportements de leurs parents. La
descendance d'une mouche ressemble et se comporte comme une mouche, et celle d'un lion
ressemble et se comporte comme un lion. Mais en dépit d'être semblable à leurs parents, tous les
descendants ont , à leur tour, leurs propres caractéristiques et comportements qui les distinguent les
uns des autres.
En outre, une seule action peut avoir des significations différentes pour des êtres différents. Par
exemple, l'action «manger» a des significations différentes pour une mouche et un lion. Une mouche
mange du nectar, tandis qu'un lion mange de la viande.
Quelque chose de semblable se passe dans Java. Le concept d’héritage des caractéristiques et des
comportements des parents peuvent être comparés à des classes héritant des attributs et des
méthodes à partir d'une classe parente. Être différent et unique est similaire à la façon dont une classe
peut aussi hériter d'un parent et également définir des attributs et des méthodes supplémentaires.
Des actions simples ayant des significations différentes peuvent être comparées à du polymorphisme
en Java.
Dans ce chapitre, vous allez apprendre à mettre en œuvre l’héritage et le polymorphisme, ainsi que
l’implémentation des interfaces. Ce chapitre traite les points suivants:
• Comprendre et mettre en œuvre l'héritage
• Développer du code qui démontre l'utilisation du polymorphisme
• Différencier entre le type d'une référence et d’un objet
• Déterminer quand est-il nécessaire de faire le casting des objets
• Utiliser super et this pour accéder aux objets et constructeurs
• Manipuler des classes abstraites et des interfaces

Mohamed HAMMOUDA 51
L’HERITAGE DANS JAVA

CHAPITRE 5 : L’HERITAGE DANS JAVA

I. LE CONCEPT D’HERITAGE DES CLASSES


Imaginez les deux fonctions programmeur et gestionnaires au sein d'une organisation. Ces deux
positions ont un ensemble commun de propriétés, y compris les noms, adresse et numéro de
téléphone.
Ces fonctions ont également des propriétés différentes. Un Programmeur doit se concentrer sur
les langages de programmation qui doit les maitriser, alors qu'un Gestionnaire peut être
préoccupé par les rapports des états d’avancement des projets.
Supposons que vous êtes censé stocker les détails de tous les programmeurs et de tous les
gestionnaires travaillant dans ton service. La Figure 54 montre les propriétés et le comportement
que vous avez identifiés pour un programmeur et un gestionnaire avec leur représentation sous
forme de classes java.

Programmeur
class Programmeur {
nom string nom;
adresse string adresse;
string telephone;
telephone
float experience ;
experience String []langagesProgrammation;
langagesProgrammation void ecrireCode ( ) {} ;
ecrireCode ( ) }

Gestionnaire
Class Gestionnaire {
name string Nom;
adresse string Adresse;
string telephone;
phoneNumber
float experience
experience int tailleEquipe;
tailleEquipe void rapportProjet () [] ;
rapportProjet() }

Figure 53 – Redondance des attributs dans les deux classes Gestionnaire et Programmeur.
Avez-vous remarqué que les classes Programmeur et Gestionnaire ont des propriétés communes,
notamment, le nom, l'adresse, le numéro de téléphone et l’expérience?
Que dites-vous si nous regroupons ces propriétés communes dans une nouvelle fonction/classe
plus générique appelé employée ? Cette étape est illustrée par la figure 55.

Mohamed HAMMOUDA 52
L’HERITAGE DANS JAVA

Employe

nom
adresse
telephone
experience

Programmeur Gestionnaire

nom nom
adresse adresse
telephone telephone
experience experience
langagesProgrammation tailleEquipe
ecrireCode ( ) rapportProjet()

Figure 54 – Généralisation des classes Gestionnaire et Programmeur


On dit que les classes Programmeur et Gestionnaire héritent de la classe Employe. La classe
Employe regroupe ainsi toutes les caractéristiques communes à ces deux classes (nom, adresse,
telephone et experience). Les classes Programmeur et Gestionnaire se limitent à la définition de
leurs caractéristiques spécifiques. En java, ce lien entre les classes s’appelle Héritage et est
implémenté avec le mot clé extends, comme le montre la figure 56.

class Employe {
String name ;
Employe String adresse ;
String phoneNumber ;
nom Fload experience;
}
adresse
class Programmeur {
telephone String []langagesProgrammation;
experience void ecrireCode(){};
}
class Gestionnaire {
int tailleEquipe;
void rapportProjet () [] ;}

Programmer Manager

langagesProgrammation tailleEquipe
ecrireCode ( )
ecrireCode() rapportProjet()

Figure 55 – Extension de la classe Employe


Dans la figure 56, la classe Employé est appelée la superclasse, classe de base, ou classe mère. Les
classes Programmeur et Gestionnaire qui héritent de la classe Employé sont appelées sous-
classes, classes dérivées, classes étendues ou classes filles.

Mohamed HAMMOUDA 53
L’HERITAGE DANS JAVA

I.1. Les avantages de l’héritage


Comme vous pouvez le constater, factoriser les attributs communs dans une même classe qui est
héritée par les autres classes présente plusieurs avantages :

I.1.1. Une définition plus réduite des classes dérivées


Vous avez sans doute remarqué que les deux classes Programmeur et Gestionnaire ont été écrites
avec un code réduit. Si vous allez ajouter une autre classe qui dérive de la classe Employe, par
exemple une classe Financier, vous aurez seulement besoin de définir les attributs qui la distingue
des autres classes, tout le reste sera hérité de la classe Employe.

I.1.2. Une facilité de modification des propriétés et des comportements


communs
Que se passe-t-il si nous décidons d’ajouter un compte facebook à tous les gestionnaires et à
tous les Programmeurs ?
La solution est très simple, comme le compte facebook est une propriété qui peut être partagée
par tous les employés, que ce soit des programmeurs ou bien des gestionnaires, il suffit de placer
cet attribut dans la classe Employe. Cet attribut sera automatiquement hérité par toutes les
classes dérivées de la classe employé.

I.1.3. Utilisation d’un code testé de la classe de base


Vous n’avez pas à réinventer la roue de nouveau. Avec le mécanisme d'héritage, vous pouvez
utiliser directement du code testé et vérifié à partir de la superclasse.

I.1.4. Concentration sur le comportement des classes dérivées


Héritant d'une classe permet de vous concentrer sur les variables et les méthodes qui définissent
le comportement particulier de votre classe.

I.1.5. Structuration et groupement logiques des caractéristiques et des


comportements
Lorsque plusieurs de vos classes héritent d'une classe de base, vous créez en fait un groupe
logique. A titre d'exemple, les classes Programmeur et Gestionnaire sont toutes regroupées dans
un groupe logique qui n’est autre que la classe Employe.

I.1.6. Extensibilité
L’extensibilité est l’avantage le plus important de l’héritage. Nous allons voir plus loin qu’une
superclasse n’est autre qu’un super type de toutes ses classes dérivées. Cela veut dire que toute
classe fille peut être considérée comme étant un type compatible avec sa classe mère.
Pour illustrer ce concept, vous allez imaginer qu’au sein de votre département GRH vous
souhaitez diffuser un message à toutes les personnes travaillant dans votre service. Il est clair que
sans l’héritage vous serez dans l’obligation de créer deux méthodes
envoyerMessagePrgrammeur() et envoyerMessageGestionnaire(). La première méthode doit avoir
comme paramètre un objet Programmeur auquel vous allez lui envoyer le message, alors que la
deuxième doit avoir comme paramètre un objet de type Gestionnaire.
Avec l’héritage, un Programmeur et un Gestionnaire sont des sous-types de la classe Employe.

Mohamed HAMMOUDA 54
L’HERITAGE DANS JAVA

Vous pouvez ainsi écrire une méthode envoyerMessage() plus générique ayant comme paramètre
le type Employe. Vous pouvez par la suite invoquer cette méthode en lui passant, selon votre
besoin, un argument de type Programmeur ou bien de type Gestionnaire.

I.2. L’accès aux membres hérités


Les classes Programmeur et Gestionnaire héritent les attributs et les méthodes définis dans la
classe Employé. En conséquence, elles peuvent les utiliser comme si elles étaient définies dans
leurs propres classes mais sous certaines conditions (à discuter dans la section suivante).
Examinez le code suivant:

class Employe {
String nom;
String addresse;
String TelephoneNombre;
float experience;
}
class Gestionnaire extends Employe {
int rapportProjet
void gererEtatProjet() {}
}
class Programmeur extends Employe {
String[] langageProgrammation;
void ecrireCode() {}
void accessMembersSuperclasse() { Accès direct au
name = "HAMMOUDA"; membre de la
}
} superclasse

La question qui se pose maintenant, comment le Programmeur peut-il attribuer une


valeur à un attribut qui est défini dans la classe Employé?
En fait, l’héritage permet à une classe dérivée d’englober sa superclasse. Par conséquent, tous les
membres (attributs et méthodes) de la classe héritée sont disponibles pour la classe dérivée. Ceci
est présenté d’une manière schématique par la figure 57.

Employee Un objet de classe


Employe existe dans une
classe Programmeur
Programmer

Figure 57 – Un objet de classe Employe existe dans une classe Programmeur


En revanche, une classe dérivée peut ne pas accéder à certains membres de sa classe de base. Les
deux sections suivantes mettront l’accent sur les membres de la superclasse qui peuvent ou pas
être accédés par une classe dérivée.

I.3. Les membres hérités qui sont accessibles


Les modificateurs d'accès jouent un rôle important pour définir l’accessibilité des membres
hérités à partir des classes dérivées. Ce qui est sûr, une classe dérivée hérite tous les membres de
sa classe de base, mais l’accès à ces membres dépend de leurs modificateurs d’accès.

Mohamed HAMMOUDA 55
L’HERITAGE DANS JAVA

Ainsi, si un membre hérité est défini avec :


• Le modificateur d’accès par défaut : l’accès est possible que lorsque la classe dérivée réside
dans le même package que la superclasse.

• Le modificateur d’accès protected : avec le mode protégé, les membres de la classe de


base sont accessibles quel que soit l’emplacement des classes filles.

I.4. Les membres hérités qui Ne sont pas accessibles


Une classe dérivée ne peut pas accéder aux membres hérités, dans les cas suivants :

• Les membres de la classe de base sont privés.


• Les membres de la classe de base ont un accès par défaut, et la superclasse et les classes
dérivées ne se trouvent pas dans le même package.

• Les constructeurs de la classe de base. Une classe dérivée peut appeler les constructeurs d'une
classe de base, mais elle ne peut pas les hériter.

II. LES CLASSES ABSTRAITES


Tout au long des sections précédentes, nous avons vu comment factoriser les propriétés et les
méthodes communes des classes Programmeur et Gestionnaire pour en créer une nouvelle
classe Employé. Mais réellement, dans votre service, il n’existe que des Programmeurs et des
Gestionnaire et la notion d’employé n’est utilisée que pour catégoriser ces deux entités de votre
système. Autrement dit, il ne peut pas y exister des objets de type employé. Les seuls objets qui
existent sont soit des objets de type Programmeur , soit des objets de types Gestionnaire. Par
conséquent, la classe Employe ne peut être qu’une classe abstraite qui n’a pas d’existence réelle
dans le système et qui ne doit en aucun cas être instanciée.
Une classe abstraite en java est une classe qui ne peut pas être instanciée. Formellement une
classe abstraite n'est pas différente d'une classe normale/concrète. Tout simplement, elle est
déclarée en ajoutant le mot-clé abstract. Une classe en java doit forcément être abstraite si elle
possède au moins une méthode abstraite. Une méthode abstraite est une méthode qui est
déclarée sans implémentation en lui ajoutant le mot-clé abstract au niveau de sa signature.
Pour illustrer ce nouveau concept, supposons que nous allons développer une application qui
permet de gérer plusieurs formes graphiques. Supposons que les formes sont des cercles et des
rectangles. Chaque forme est caractérisée par les coordonnées de son centre de gravité. Chaque
forme peut changer sa couleur, se déplacer, et calculer sa surface. Un rectangle est caractérisé
par sa longueur et sa largeur, et un cercle est caractérisé par son rayon.
Vous avez sûrement compris que la classe forme ne peut être qu’une classe abstraite vu que, dans
la réalité, nous ne pouvons pas avoir un objet forme concret. En fait, une forme est soit un cercle,
soit un rectangle. Une autre raison qui nous pousse à définir la classe forme comme étant une
classe abstraite c’est qu’il est impossible d’implémenter la méthode calculerSurface(). La figure 58
permet de mettre en évidence cet exemple.

Mohamed HAMMOUDA 56
L’HERITAGE DANS JAVA

class Coordonnee{
int x ;
int y ;
} Forme est une classe abstraite qui
abstract class Forme{ factorise les propriétés et les
Coordonnee coordonnee ; comportements communs des
String couleur ; triangles et des cercles
void deplacer(Coordonnee c,String
couleur){
coordonnee=c ;
this.couleur=couleur ; A ce stade nous ne
} savons pas comment
abstract double calculerSurface() ;
implémenter la
}
class Cercle extends Forme { surface d’une forme
double rayon ;
double calculerSurface() {
return rayon*rayon*3.14;
} A ce stade nous savons
} comment implémenter la
class Rectangle extends Forme {
surface d’un cercle ou bien d’un
double lg ;
double larg; Rectangle
double calculerSurface() {
return lg*larg;
}
}

Figure 56 - Mise en évidence du concept d’abstraction en Java


Certaines règles doivent être respectées dans le cas de l’héritage des classes abstraites :
• Une classe abstraite ne peut en aucun cas être instanciée.
• Une classe abstraite peut être définie comme étant une superclasse malgré qu’elle contienne
de méthode abstraite.

• Une classe dérivée doit implémenter toutes les méthodes abstraites de sa superclasse, sinon
elle doit être définie comme étant une classe dérivée abstraite.

• Vous pouvez utiliser des variables de référence de type superclasse abstraite pour référencer
des objets de ses classes dérivées.

III. LES INTERFACES


Dans la section précédente, nous avons présenté le concept de classe abstraite qui trouve son
intérêt avec le concept d’héritage. Les classes abstraites permettent de regrouper des
fonctionnalités communes à des classes dérivées même si ces classes implémentent
différemment ces fonctionnalités. Par ailleurs, les classes dérivées doivent implémenter les
méthodes abstraites héritées sinon elles sont considérées abstraites.
Si maintenant on considère une classe abstraite qui n’implémente aucune méthode, nous
aboutissons à la notion d’interface. En effet, une interface est une classe abstraite ne contenant
que des constantes et des méthodes abstraites.
Une interface est utilisée pour factoriser des comportements communs pour différentes classes.
Cela permet de définir un ensemble de services visibles depuis l’extérieur, sans se préoccuper de
la façon dont ces services seront réellement implémentés. Une classe qui implémente une
interface doit obligatoirement implémenter chacune des méthodes déclarées dans l’interface, à
moins qu’elle ne soit pas elle-même déclarée abstraite. Toute les méthodes d’une interface sans

Mohamed HAMMOUDA 57
L’HERITAGE DANS JAVA

par défaut publiques, même si vous ne spécifiez pas le mot clé public. La figure 59 permet de
mettre en évidence le concept d’interface.

interface ComportementCommun{ class EtreVivant { interface volable{


void manger() ; float dureeVie ; void voler() ;
void bouger() ; } }
}

class Lion extends EtreVivant implements ComportementCommun, Volable{


String nom ;
public void manger(){
System.out.println(“je mange de la viande”);
}
public void bouger(){
System.out.println(“je bouge mes pattes”);
}
}

class Mouche extends EtreVivant implements ComportementCommun, Volable{


String nom ;
public void manger(){
System.out.println(“je mange du nectar”);
}
public void bouger(){System.out.println(“je bouge mes ailles”);
public void voler(){
System.out.println(je bouge mes ailles pour vouler) ;
}
}

Figure 57 - Implémentation des interfaces

Différence entre une interface et une classe abstraite

Nous venons de voir que les classes abstraites et les interfaces jouent deux rôles différents : les
classes abstraites servent à factoriser du code, alors que les interfaces servent à définir des
contrats de service.
Une question qui s’impose : pourquoi ne pas utiliser des classes abstraites avec seulement
des méthodes abstraites au lieu des interfaces ?
La réponse est simple : dans la plupart des langages actuels (notamment le cas de Java), il n’est
possible pour une classe d’hériter que d’une seule classe parente, mais d’implémenter plusieurs
interfaces. Autrement dit, les interfaces permettent non seulement d’externaliser les services des
classes mais aussi d’assurer l’héritage multiple.
Une autre question : pourquoi java ne permet pas l’héritage multiple ?
Encore une fois la réponse est simple. Pour répondre à cette question vous allez supposer que
vous disposez de deux classes X et Y et une troisième classe Z héritant des deux autres. Vous allez
aussi supposer que les deux classes X et Y dispose chacune d’une méthode maMéthode() avec
deux implémentations tout à fait différentes. Soit le bout de code suivant :
Z obj=new Z() ; obj.maMethode() ;
Laquelle des méthodes héritées va être exécutée ?

Mohamed HAMMOUDA 58
L’HERITAGE DANS JAVA

Dans le cas de l’héritage multiple, la classe Z pourrait accéder à deux méthodes ayant la même
signature mais diffèrent au niveau de leur implémentation. Il sera donc impossible de résoudre
cet appel de méthode. Voilà pourquoi l’héritage multiple n’est pas permis en java.
En revanche, si vous supposez que X et Y sont des interfaces et non pas des classes et que Z
implémente ces deux interfaces, le problème d’ambigüité d’invocation de méthodes n’existe plus
vu qu’une seule méthode maMethode() sera implémentée dans Z et non pas deux.

IV. VARIABLES DE REFERENCE ET OBJETS REFERENCES DE TYPES DIFFERENTS


Nous avons vu, dans ce cours, que lorsque vous faîtes référence à un objet, le type de la variable
de référence doit être le même que celui de l’objet réel référencé. Mais, avec le concept
d’héritage, le type de la variable de référence et le type de l’objet peuvent être différents sous
certaines règles.
Reprenons l’exemple de la figure 58, Une variable de référence sur un objet de type Mouche peut
être définie de plusieurs manières :

• De même type que l’objet : Un objet de type Mouche peut être référé par une variable de
référence de type Mouche : Moche varRefMouche= new Mouche() ;
• De type superclasse : Un objet de type Mouche peut être référé par une variable de référence
de type EtreVivant : EtreVivant varRefMouche= new Mouche() ;
• De type interface : Un objet de type Mouche peut être référé par une variable de référence de
type volable : volable varRefMouche= new Mouche() ;
Dans ce cas, l’accès à un objet diffère selon le type de la variable de référence qui lui correspond.

IV.1. Accés à un objet d’une classe dérivée de même type que sa variable de
référence
Représentons le cas d’un responsable GRH (Gestion des Ressources Humaines) qui joue un double
rôle : c’est un employé de l’entreprise, d’une part, et permet de conduire des entretiens aux
autres employés, d’autre part. De ce fait, la classe GRHResponsable hérite de la classe Employe et
implémente l’interface Entretien.

class Employe { Interface Entretien {


String NOM; public void conduireEntretien();
String addressE; }
String telephoneNb;
float experience;
}

class GRHResponsable extends Employee implements Entretien {


String[] specialisation;
public void conduireEntretien () {
System.out.println("GRHResponsable - Conduire Entretien");
}
}

Figure 58 – Conception des classes GRHResponsable, Employe et Entretien

Mohamed HAMMOUDA 59
L’HERITAGE DANS JAVA

Un objet concret de type GRHResponsable peut accéder aux méthodes et attributs de la


superclasse Employe (en respectant les règles de visibilité), et à la méthode implémentée de
l’interface Entretien. Le bout du code suivant permet de montrer ceci.

class Bureau {
public static void main(String args[]) { Variable hr de type GRHResponsable
GRHResponsable hr = new GRHResponsable(); référence un objet de même type
hr.specialisation = new String[] {“GRH”}; Accès à des variables définies dans la
System.out.println(hr.specialisation[0]); classe GRHResponsable
hr.nom = "Mohamed HAMMOUDA";
Accès à des variables définies dans la
System.out.println(hr.nom);
classe Employe
hr.conduireEntratien();
} Accès à une méthode redéfinie de
} l’interface Entretien

Figure 59 – Accès à un objet d’une classe dérivée de même type que sa variable de référence

IV.2. Accés à un objet d’une classe dérivée à travers une variable de référence de
type superclasse
Nous allons reprendre l’exemple de la section précédente mais cette fois-ci nous allons référer
l’objet GRHResponsable par une variable de référence de type Employe. Observons le
comportement du programme à travers la figure suivante :

class Bureau {
public static void main(String args[]) { Variable hr de type Employe référence
Employe emp = new GRHResponsable(); un objet de type GRHResponsable

emp.specialisation = new String[] {“GRH”}; Les variables définies dans la classe


System.out.println(emp.specialisation[0]); GRHResponsable sont inaccessibles
par par emp ❶
emp.nom = "Mohamed HAMMOUDA";
System.out.println(emp.nom); Accès à des variables définies dans la
classe Employe
emp.conduireEntratien();
} Accèss interdit à la méthode
} conduireEntratien

Figure 60 – Accès à un objet d’une classe dérivée à travers une variable de référence de type
super-classe
Le code en ❶ génère une erreur de compilation parce que la variable emp de type Employe bien
qu’elle référence un objet de type GRHResponsable ne peut accéder aux membres de Employe.
La figure 62 permet d’illustrer ce fonctionnement.

Un objet de classe
emp Employe Employe existe dans la
classe GRHResponsable
Variable référence
GRHResponsable
de type Employe

Figure 61- Une variable de référence de type Employe ne peut voir que les membres définis

Mohamed HAMMOUDA 60
L’HERITAGE DANS JAVA

dans la classe Employe

IV.3. Accès à un objet d’une classe dérivée à travers une variable de référence
de type interface
Reprenons de nouveau l’exemple de la section 3.1 en référant cette fois-ci l’objet
GRHResponsable par une variable de référence de type Entretien et observons le
comportement du programme à travers la figure suivante :

class Bureau {
public static void main(String args[]) { Variable hr de type Employe
Entretien ent = new GRHResponsable(); référence un objet de type
GRHResponsable
ent.specialisation = new String[] {“GRH”};
System.out.println(ent.specialisation[0]); Les variables définies dans la
classe GRHResponsable ainsi que
ent.nom = "Mohamed HAMMOUDA";
la classe Employe sont
System.out.println(ent.nom);
inaccessibles par ent ❶
ent.conduireEntratien();
} Accèss à la méthode
} conduireEntretien

Figure 62- Une variable de type Entretien ne peut voir que les méthodes de l’interface Entretien
Le code en ❶ génère une erreur de compilation parce que la variable de référence ent de type
Entretien bien qu’elle référence un objet de type GRHResponsable ne peut accéder qu’aux
méthodes de l’interface Entretien (autrement dit la méthode conduireEntretien()).
La figure 64 permet d’illustrer ce fonctionnement.

Un objet de class Employe


Employe existe dans la class
Entretien GRHResponsable
Entretien
Variable référence de type GRHResponsable
Une variable de type Entretien ne
Entretien
peut accéder qu’aux méthodes de
l’interface Entretien

Figure 63 - Une variable de type Entretien ne peut voir que les méthodes définies dans
l'interface Entretien

IV.4. Pourquoi référencer des objets par des variables de références de types
différents
Vous demandez peut-être pourquoi nous avons besoin d'une variable de référence de type
superclasse pour accéder à un objet concret d'une classe dérivée alors que cette variable ne peut
pas accéder à tous les membres de l’objet. La réponse est simple: en fait deux raisons nous
poussent à adopter une telle approche pour référencer des objets :
La première raison est que parfois nous voulons se préoccuper seulement d’un comportement
particulier de l’objet concret ou bien d’une partie de ses attributs. C’est le cas, par exemple,
lorsque nous avons référencé un objet GRHResponsable par une variable de type Entretien. Dans
ce cas, c’est seulement les méthodes de l’interface Entretien qui sont accessibles.

Mohamed HAMMOUDA 61
L’HERITAGE DANS JAVA

La deuxième raison est que cette approche nous permet de créer des tableaux (ou bien des
listes) dont les éléments peuvent référencer des objets de types différents. La figure suivante
permet d’illustrer cette situation.

class bureau {
public static void main(String args[]) { Un tableau de type
Employe[] emp = new Employe[2]; Employe

Puisque chaque élément du


emp[0] = new Gestionnaire(); tableau emp est une variable
référence de type Employe, alors
emp[1] = new GRHResponsable();
} ces variables peuvent référencer
} des objets concrets de ses classes
filles

Figure 64 - Un tableau de type Employe peut référencer plusieurs objets de type classe dérivée

V. LE TRANSTYPAGE (CASTING)
Dans la section précédente, nous avons vu que nous pouvons à travers un tableau de type classe
de base référencer plusieurs objets de types classes filles.
Une question évidente se pose : Comment accéder aux membres d’une classe dérivée si
la variable de référence est de type classe de base ?
En fait, la réponse est que nous pouvons forcer une variable de référence de type classe de base
référant un objet de sa classe fille d’accéder aux membres de sa classe fille en utilisant le
mécanisme de transtypage.

V.1. Transtypage d’une variable de référence vers un autre type


Nous avons noté, dans la section précédente, qu’une variable de référence de type Entretien
référant un objet de type GRHResponsable ne peut pas accéder aux membres de la classe
GRHResponsable.
Rappelez-vous de l’instruction suivante : Entretien ent = new GRHResponsable();
Rappelez-vous aussi qu’un accès aux membres de la classe GRHResponsable génère une erreur de
compilation : ent.specialisation = new String[]{"Staf"}

Variable de type Objet


Entretien GRHResponsable

Figure 65 - Une variable de type Entretien référence un objet de type GRHResponsable


En fait, le compilateur java sait que le type de la variable ent est Entretien et que l’interface
Entretien ne possède pas l’attribut specialisation. D'autre part, le JRE sait que l'objet désigné par
la variable ent est de type GRHResponsable. Java vous permet alors de transformer le type de la
variable de référence ent vers le type réel de l’objet qu’elle pointe, comme suit :
((GRHResponsable)ent).specialisation = new String[] {"Staf"};

Mohamed HAMMOUDA 62
L’HERITAGE DANS JAVA

Notez que, pour transformer le type de la variable, il faut la préfixer par le nouveau type délimité
par des parenthèses.

V.2. Le besoin de transtypage


Reprenons l’exemple des formes géométriques. Le code suivant permet de vous rappeler un petit
peu de la structure des différentes classes utilisées.

class Coordonnee{ class Cercle extends Forme {


int x ; double rayon ;
int y ; double calculerSurface() {
} return rayon*rayon*3.14;
abstract class Forme{ }
Coordonnee coordonnee ; }
String couleur ;
void deplacer(Coordonnee c,String class Rectangle extends Forme {
couleur){ double lg ;
coordonnee=c ; double larg;
this.couleur=couleur ;
double calculerSurface() {
}
return lg*larg;
abstract double
}
calculerSurface() ;
} }

Supposons que nous allons, à travers un tableau de type Forme, de créer et référencer un certain
nombre d’objets de types classe filles (Cercle et Rectangle), comme suit :

Forme [] tabForme=new Forme[2] ;


tabForme[0]= new Rectangle() ;
tabForme[1]= new Cercle() ;
98 autres objets de types Cercle et
………
Rectangles créés

Maintenant, supposons que nous souhaitons afficher les informations particulières de chaque
objets.
Je pense que vous avez deviné le problème :
Comment peut-on appliquer le bon transtypage si on ne peut pas savoir avec certitude le
type réel de l’objet à transformer ?
Heureusemen, java dispose d’un opérateur instanceof permettant de savoir si un objet est une
instance d’une classe donnée. Un tel opérateur peut être utilisé comme suit : x instanceof Y pour
dire si x référence un objet de type Y.
Pour chaque variable de référence
Foreach( Forme f : tabForme)
{ f de tabForme
If (f instanceof Cercle)
System.out.println ( ((Cercle)f).rayon ) ; Si le type réel de l’objet référencé
else par f est un Cercle alors transformer
System.out.println ( ((Rectangle)f).larg ) ; f en Cercle et accéder à ses membres
}

Sinon transformer f en Rectangle et


accéder à ses membres

Figure 66 - Utilisation de l’opérateur instanceof pour manipuler des objet concrets de types
différents
En conséquence, pour afficher d’une manière correcte les attributs des objets de types différents

Mohamed HAMMOUDA 63
L’HERITAGE DANS JAVA

référés par les variables référence du tableau tabForme, il suffit de tester les types réels des
objets et par la suite leur appliquer le transtypage adéquat. La figure 67 permet de mettre en
évidence ce principe.

VI. UTILISATION DE THIS ET SUPER POU R ACCEDER A DES OBJETS ET DES


CONSTRUCTEURS
VI.1. La référence d'objet : this
Nous avons déjà vu plus loin dans ce chapitre que le mot clé this est utilisé pour différencier entre
les paramètres des méthodes et les attributs des classes lorsqu’ils ont les mêmes noms. En fait,
il désigne, dans une classe, l'instance courante de la classe elle-même. Nous pouvons également
utiliser this pour différencier un constructeur d’un autre.
Voici un exemple dans lequel la classe Employe définit deux constructeurs, tel que le deuxième
constructeur appelle le premier. En fait, afin que le deuxième constructeur instancie un objet
Employe, il invoque le premier qui en construit une partie de l’objet.

class Employe {
String nom;
String addresse;
Constructeur qui accepte
Employee(String nom) {
this.nom = nom; un argument et instancie
} l’attribut nom
Employe(String nom, String adresse) {
this(nom);
this.adresse = adresse; Un deuxième constructeur
} qui accepte un argument et
}
invoque le premier

VI.2. La référence d'objet: super


Dans la section précédente, nous avons discuté comment le mot clé this se réfère à l’instance de
l’objet courant. De même, super est aussi une référence d'objet mais se réfère à la superclasse.
Lorsque vous utilisez super, pensez aux mots mes parents, ma base, comme illustré dans la figure
68.

Super = parrent
Hérite

Figure 68 - Lorsque vous utilisez super, pensez aux mots mes parents, ma base
La variable de référence super peut être utilisée pour accéder aux attributs et/ou aux méthodes
de la classe de base s’il existe des méthodes ou bien des attributs de mêmes noms.

Mohamed HAMMOUDA 64
L’HERITAGE DANS JAVA

Variable d’intantance
class Employe {
nom dans Employe
String nom;
}
Variable d’intantance
class Programmeur extends Employe { nom dans Programmeur
String nom;
void setNoms() { Affecter une valeur à la
variable d’instance nom de la
this.nom = "Programmeur";
classe Programmeur
super.nom = "Employe";
} Affecter une valeur à la
variable d’instance nom de la
void printNoms() { classe Employe

System.out.println(super.nom);
afficher une valeur de la
System.out.println(this.nom); variable d’instance nom de la
} classe Employe
}
class UtiliserSuperEtThis {
public static void main(String[] args) {
Programmeur programmeur = new Programmer(); afficher une valeur de la
programmeur.setNoms(); variable d’instance nom de la
programmeur.printNoms();
classe Programmeur
}
}

Une variable de référence super peut également être utilisée pour désigner les constructeurs de
la superclasse dans une classe dérivée.
Voici un exemple dans lequel la superclasse Employe définit un constructeur qui assigne des
valeurs par défaut pour ses variables. Sa classe dérivée appelle le constructeur de la superclasse
dans son propre constructeur.

class Employe {
String nom; Constructeur qui accepte un
String adresse; nom et une adresse
Employe(String nom, String adr) {
this.nom = nom;
this.address = adr;
}
}
class Programmer extends Employee { Constructeur qui accepte trois
String language; paramétrés dont deux (nom et
Programmer(String nom, String adr, String lang) { adresse) sont passés comme
super(nom, adresse); argument
this.langage = lang;
}
}

VII. LE POLYMORPHISME
Le concept de polymorphisme est l’un des concepts fondamentaux de la programmation
orientée objet. L'héritage concerne les classes (et leur hiérarchie), alors que le polymorphisme est
relatif aux méthodes des objets. En fait, au début de ce chapitre, nous avons utilisé un exemple
pratique pour expliquer le sens de polymorphisme. La même action peut avoir des significations
différentes pour différents êtres vivants. L'action manger, par exemple, a une signification
différente pour une mouche et un lion. Une mouche peut manger de nectar, tandis qu'un lion

Mohamed HAMMOUDA 65
L’HERITAGE DANS JAVA

peut manger de la viande. Réagissant à la même action, chacun avec sa propre manière, peut
être comparé à du polymorphisme en Java.
Nous parlons de polymorphisme lorsqu’une classe hérite d’une autre classe et que les deux
classes définissent des méthodes avec les mêmes signatures.
En effet, cmme nous l’avons discuté dans la section précédente, un objet peut être référencé par
une variable de référence de sa superclasse. Cette variable n’aura accès qu’aux membres de sa
classe. D’un autre coté, une méthode polymorphe existe dans les deux classes (classe fille et
classe mère, avec la même signature), il est évident alors que l’invocation de cette méthode peut
se faire sans aucun problème.
Une question très évidente qui s’impose dans ce cas : Est-ce que c’est la méthode de la
superclasse qui va être exécuté ou bien c’est celle de la classe fille ?
Pour vous aider à répondre à cette question, une deuxième définition du polymorphisme peut
être formulée. En fait, nous pouvons dire qu’une méthode est polymorphe si son comportement
dépend du type de l’objet réel et non pas du type de la variable de référence. Ceci implique que le
choix du code à exécuter (pour une méthode polymorphe) ne se fait pas statiquement à la
compilation mais dynamiquement à l'exécution.
Pour illustrer ce concept fondamental dans la programmation orientée objets, reprenons les
classes Employe, Programmeur, et Gestionnaire.
Commençons avec la classe Employe qui définit deux méthodes. La méthode commencerProjet()
est une méthode abstraite vu qu’à ce stade nous ne sommes pas sûr de ce qu'il faut faire pour
commencer à travailler sur un projet.
Etant donné que la méthode commencerProjet() est une méthode abstraite alors la classe
Employe est définie comme étant une classe abstraite, comme suit :
abstract class Employe {
public void atteindreBureau() {
System.out.println("bureau – L21, STunise");
}
public abstract void commencerProjet();
}

La classe Programmeur étend la classe Employe, ce qui signifie essentiellement que Programmeur
peut accèder à la méthode atteindreBureau(). La classe Programmeur doit également développer
la méthode abstraite commencerProjet() héritée de Employe. En fait, à ce niveau, nous savons
comment un programmeur devrait commencer son travail. Le plus probablement, il va définir des
classes et les tester. Ce comportement est contenu dans la classe Programmeur conne suit :
class Programmeur extends Employe {
public void commencerProjet () {
definirClasses();
testerCode();
}
private void definirClasses() {System.out.println("définir classes");}
private voitesterCode() {System.out.println("test unitaire du code");
}

Mohamed HAMMOUDA 66
L’HERITAGE DANS JAVA

class Gestionnaire extends Employee {


public void commencerProjet () {
rencontrerClient();
definirPlanProjet();
affecterTachesEquipe();
}
private void rencontrerClient () {
System.out.println("rencontrer le client");
}
private void definirPlanProjet () {
System.out.println("planifier le projet");
}
private void affecterTacheseEquipe () {
System.out.println("affecter les taches aux membres de l’équipe");
}
}

Le même raisonnement est appliqué à la classe Gestionnaire. Très probablement, un Gestionnaire,


pour gérer son projet ou le commencer, il devrait se réunir avec les clients, définir un calendrier
du projet, et affecter du travail aux membres de l'équipe. Voici la définition de la classe
Gestionnaire qui étend la classe Employe et implémente la méthode commencerProjet( ).
Voyons maintenant comment cette méthode se comporte avec différents types d'employés. Voici
le code permettant de tester l’ensemble des classes:
class PolymorphismeAvecClasses {
Emp1 référence un
public static void main(String[] args) { Programmeur alors emp2
Employe emp1 = new Programmeur(); référence un Gestionnaire ❶
Employe emp2 = new Gestionnaire();
Pas de confusion parce
emp1.atteindreBureau();
emp2.atteindreBureau (); que atteindreBureau est
défini seulement dans Employe
emp1.commencerProjet ();
emp2.commencerProjet (); Méthode invoquée de
} Programmeur ❸
}
Méthode invoquée de
Gestionnaire ❹

Le résultat du code est le suivant :

bureau – L21, STunise


bureau – L21, STunise

définir classe
test unitaire du code

rencontrer le client
planifier le projet
affecter les taches aux membres de l’équipe

Figure 69 – Exemple d’utilisation du concept de polymorphisme


Le code en ❶ crée un objet de type Programmeur et l'affecte à une variable de type Employe,
ainsi qu’un objet de type Gestionnaire et l'affecte à une variable de type Employe. Jusqu'ici, tout
va bien!
Le code en ❷ exécute la méthode atteindreBureau(). Du fait que cette méthode est uniquement
définie dans la classe Employé, il n'y a pas de confusion dans la manière avec laquelle elle sera
exécutée, d’où la sortie suivante :

Mohamed HAMMOUDA 67
L’HERITAGE DANS JAVA

bureau – L21, STunise


bureau – L21, STunise
Le code en ❸ exécute le code emp1.commencerProjet() et appelle la méthode
commencerProjet() définie dans la classe Programmeur du fait que emp1 se réfère à un objet
concret de type Programmateur. Voici le résultat de cet appel de méthode:
définir classe
test unitaire du code
Le code en ❹ exécute le code emp2.commencerProjet() et appelle la méthode
commencerProjet() définie dans la classe Gestionnaire du fait que emp2 se réfère à un objet
concret de type Gestionnaire. Voici le résultat de cet appel de méthode:
rencontrer le client
planifier le projet
affecter les taches aux membres de l’équipe
Reste à mentionner qu’une méthode polymorphe définie dans la superclasse n’est pas
obligatoirement abstraite. En effet, si au moment de la définition de la méthode, nous
connaissons la manière avec laquelle cette méthode peut être implémentée, alors il est évident
qu’elle n’est pas abstraite. Rappelez-vous les deux méthodes, deplacer() et calculerSurface()
définies dans la classe Forme. La première a été définie comme une méthode non abstraite
(toutes les formes quel que soit leurs types se déplacent de la même manière), alors que la
deuxième méthode a été définie comme abstraite car nous ne savons pas comment calculer la
surface d’une Forme mais par contre nous savons que toutes les formes ont une surface.

VIII. MECANISME DE RESOLUTION D’ACCES AUX METHODE ET VARIABLES


Vous pouvez utiliser des variables de référence d'une classe de base pour référencer un objet
d'une classe dérivée. Mais il y a une différence majeure dans la façon avec laquelle Java accède
aux variables et aux méthodes à travers ces objets. Avec l'héritage, l’accès aux attributs est
résolu au moment de la compilation (compile time), alors que l’accès aux méthodes est résolu au
moment de l’exécution (runtime). Pour mieux comprendre ce comportement de java, examinons
ensemble le code de la figure 70.

Mohamed HAMMOUDA 68
L’HERITAGE DANS JAVA

class Employe {
String nom = "Employé";

void printNom() {
System.out.println(nom);
}
}
class Programmeur extends Employe { Variable de référence Employe,
objet concret Employe ❶
String name = "Programmeur";

void printNom() {
System.out.println(nom);
Variable de référence Employe,
}
} objet concret Programmeur ❷
class Bureau {
public static void main(String[] args) {
Employe emp = new Employe(); Accès à la variable nom défini
Employe programmeur = new
Programmeur(); dans Employe ❸

System.out.println(emp.nom);
System.out.println(programmeur.nom)
; Les variables sont liées à la compilation.
Parce que le type de la variable
emp.printNom(); programmeur est Employe, c’est
programmeur.printNom();
} l’attribut nom de la classe Employe qui
} est accédé. ❹

Accès à la méthode printNom Les méthodes sont liées à l’exécution.


défini dans Employe ❺ Parce que le type de l’objet créé à
l’exécution est Programmeur, c’est la
méthode printNom de la classe
Programmeur qui est exécutée. ❻

Figure 70 – Accès aux attributs et aux méthodes hérités


Le résultat du code est le suivant :

Employé
Employé
Employé
Programmeur

Voyons ce qui se passe dans le code, étape par étape:


Le code en ❶ crée un objet de classe Employé référencé par une variable de type Employe.
Le code en ❷ crée un objet de la classe Programmeur référencé par une variable de la classe de
base Employe.
Le code en ❸ accède à la variable nom définie dans la classe Employe et affiche Employé.
Le code en ❹ affiche également Employé. Le type de la variable programmeur est Employe.
Comme les variables sont liés au moment de la compilation, le type de l'objet qui est référencé
par la variable emp ne fait pas de différence. emp.nom accède donc à la variable nom définie
dans la classe Employe.
Le code en ❺ affiche Employé. La variable de référence emp et l’objet référencé ont tous les
deux le même type (Employe), il n'y a pas donc de confusion avec l'appel de la méthode.
Le code en ❻ imprime Programmeur. Bien que la méthode printNom est invoquée en utilisant
une variable de référence de type Employe, le JRE au moment de l’exécution est conscient que
cette méthode est invoquée par un objet concret de type Programmeur et donc exécute la
méthode printNom de la classe Programmeur.

Mohamed HAMMOUDA 69
LES EXCEPTIONS DANS JAVA

Chapitre 6
Les Exceptions dans Java

Introduction
Imaginez que vous êtes sur le point de monter à bord d'un avion pour PARIS pour assister à une
importante conférence. A la dernière minute, vous apprenez que le vol a été annulé parce que le
pilote ne se sent pas bien. Heureusement, la compagnie s’arrange rapidement pour trouver un pilote
de remplacement permettant au vol de décoller à l'heure initialement prévue.
Cet exemple illustre comment des conditions exceptionnelles peuvent modifier le scénario normal
d’exécution d'une action et démontre la nécessité de gérer ces conditions de manière appropriée.
En Java, une condition exceptionnelle (comme la maladie d'un pilote) peut affecter le flux normal du
code (opération de vol de la compagnie). Dans ce contexte, l'arrangement pour affecter un nouveau
pilote peut être comparé à un gestionnaire d'exception.
Dans ce chapitre, vous allez apprendre à gérer les exceptions dans un programme java. Ce chapitre
traite ainsi les points suivants:
• Comprendre et identifier les exceptions générées par le code
• Déterminer comment les exceptions modifient le déroulement normal du programme
• Comprendre la nécessité de gérer les exceptions séparément dans votre code
• Utilisation des blocs try-catch-finally pour gérer les exceptions
• Différencier entre les exceptions vérifiées, exceptions non vérifiées et les erreurs
• Appel à des méthodes qui peuvent lever des exceptions

Mohamed HAMMOUDA 70
LES EXCEPTIONS DANS JAVA

CHAPITRE 6 : LES EXCEPTIONS DANS JAVA


I. LES EXCEPTIONS DANS JAVA
I.1. Un avant-gout des exceptionS
Dans la figure71, pensez-vous que les codes en gras dans les classes AccesTableau, OuvrirFichier,
et AccesMethode ont quelque chose en commun?

public class AccesTableau {


public static void main(String args[]) {
String[] etudiant = {"Wahid", "Frad", "Issa"};
System.out.println(etudiant[5].length());
}
}

public class OuvrirFichier { Observez ces


public static void main(String args[]) { bouts de code
FileInputStream fis = new FileInputStream("fichier.txt");
}
}

public class AccesMethode {


public static void main(String args[]) {
maMethode();
}
public static void myMethod() {
System.out.println("myMethod");
maMethode ();
}
}

Figure 71 – Exemples de codes qui peuvent générer des erreurs


Nous sommes certains, étant donné le titre de ce chapitre, que vous avez répondu
convenablement à cette question. En fait, chacun de ces trois codes est susceptible de générer
une exception ou une erreur. Observons-les individuellement:
 Classe AccesTableau : la longueur du tableau des étudiants est 3, tenter d'accéder au
cinquième élément du tableau génère une exception, comme le montre la figure 72.

public class AccesTableau { Hamza


public static void main(String args[]) {
String[] etudiant = {"Wahid", "Frad", "Issa"};
System.out.println(etudiant[5].length()); Frad
}
}
Issa

Génère ArrayIndexOutofBoundsException Position invalide du tableau

Figure 72 – Erreur d’accès à un tableau : l’exception ArrayIndexOutofBoundsException

Mohamed HAMMOUDA 71
LES EXCEPTIONS DANS JAVA

 Classe OuvrirFichier : Le constructeur de la classe FileInputStream génère une exception


FileNotFoundException (comme représenté sur la figure 73) parce qu’il a échoué à instancier un
objet FileInputStream du peut être à un fichier inexistant ou bien défaillant.

Figure 73 – Erreur d’accès à un fichier : l’exception FileNotFoundException


 Classe AccesMethode : Comme vous pouvez le voir dans la figure 74, la méthode maMethode
est appelée d’une manière récursive sans spécifier une condition de sortie. Ces appels récursifs
infinis génèrent une exception StackOverflowError du à la saturation de la mémoire centrale.

public class AccesMethode {


public static void main(String args[]) {
maMethode(); Génère StackOverflowError
}
public static void myMethod() {
System.out.println("myMethod"); Appel récursif de maMethode sans
maMethode (); condition d’arrêt
}
}

Figure 74 – Appel récursif infini : l’exception StackOverflowError

I.2. Pourquoi faut-il traiter les exceptions séparément


Imaginez que vous voulez poster des commentaires sur un site de blogs. Pour cela vous devez
suivre les étapes suivantes:
1- Accéder au site de blogs.
2- Connectez-vous à votre compte.
3- Sélectionnez le blog que vous souhaitez commenter.
4- Publiez vos commentaires.
La liste précédente peut sembler un ensemble idéal d'étapes si chaque étape s’exécute
parfaitement. Cependant la vie n’est pas toujours simple. Dans des conditions réelles, vous êtes
obligés de vérifier si vous avez terminé une étape précédente avant de pouvoir progresser avec la
prochaine étape. Le comportement de l’utilisateur est imprévisible vis-à-vis de chacune de ces
étapes. Il peut se tromper dans l’adresse du site du blog ou même commettre une erreur en
spécifiant les informations de son compte.
La logique exige donc de vérifier un ensemble de conditions avant qu'un utilisateur puisse passer
à l'étape suivante. Cette vérification des conditions à de multiples endroits du code introduit de
nouvelles étapes dans le code d’origine.
Ce qui permet de rendre le code plus difficile à gérer, et détourne l’attention du développeur sur
les taches réelle qu’il essaie d’accomplir. La figure 75 met en évidence ceci.

Mohamed HAMMOUDA 72
LES EXCEPTIONS DANS JAVA

Accès au site web du blog


if (site valide) {
Accès au compte Logique applicative
if (info compte sont correctes){ perdu dans le test des
Sélectionner le blog à commenter
if (problem d’accès à la base de données) { exceptions
Essayer plus tard
else {
Poster votre commentaire
}
}
else {
redéfinir vos info
}
}
else {
corriger l’addresse et essayer encore une fois
}

Figure 75 –Intégration des tests de vérification des erreurs dans le code fonctionnel de
l’application
Observons de prêt à travers la figure76 comment la gestion des exceptions dans java permet de
structurer votre code en séparant le code fonctionnel (tâches à effectuer) des tests à effectuer
afin d’assurer le bon fonctionnement de votre code et faciliter sa maintenance.

try { Logique applicative


Accès au site web du blog bien définie et bien
Accès au compte claire
Sélectionner le blog à commenter
Poster votre commentaire
}
catch (ExceptionSiteInvalide e) {
// définer le code à exécuter lorsque le site est introuvable
}
catch {ExceptionLogin e) { La gestion des
// définer le code à exécuter lorsque le login est incorrecte exceptions est
} séparée de la logique
catch (ExceptionAccèsBaseDeDonnées e) { applicative
// définer le code à exécuter lorsque il y a un problème //d’accès à la base
}

Figure 76 –Séparation des tests de vérification des erreurs du code fonctionnel en utilisant
try-catch

II. LA GESTION DES EXCEPTIONS


Dans cette section, nous allons découvrir comment et quand une exception est levée et ce qui se
passe quand elle est levée. Pour plaisanter un peu, nous pouvons dire que les exceptions dans
java ne font pas l’exception. En fait, une exception java est un objet comme tout autre objet. Tous
les types d'exceptions héritent de la classe java.lang.Throwable.
Quand une partie du code génère une exception (ressource introuvable, mémoire saturée, login
incorrecte, etc.), un objet exception héritant de la classe java.lang.Throwable est créé et initialisé
avec les informations nécessaires (comme son type, une description textuelle de l’erreur). A ce
stade, le gestionnaire des exceptions récupère cette exception et commence à chercher un

Mohamed HAMMOUDA 73
LES EXCEPTIONS DANS JAVA

endroit approprié pour poursuivre l'exécution du programme. Cet endroit approprié est le
gestionnaire d'exception, dont le rôle consiste à récupérer le problème, le traiter ou même
l’ignorer de sorte que le programme continue à fonctionner normalement.

II.1. Les différentes catégories des exceptions


La figure 77 permet de présenter la hiérarchie des classes « mères » des exceptions java.

Java.lang.Object

Java.lang.Throwable

Java.lang.Error Java.lang.Exception

Java.lang.RuntimeException

Figure 77 – Hiérarchie des classes d’exception


On constate donc 3 degrés de gravité :

II.1.1. Les exceptions vérifiées (Checked Exception) :


Avec une exception vérifiée, les méthodes pouvant lever une erreur doivent lister exhaustivement
dans leur prototype (signature de méthode) quelles exceptions sont potentiellement générées à
l’appel, et toute autre méthode qui y fera appel devra indiquer explicitement la manière de les
traiter (au pire des cas en les relançant à leur propre appelant, et donc en les déclarant à nouveau
dans le prototype). L’exception est donc vérifiée au sens où le code obtenu ne laisse aucune place
au hasard et gère explicitement les cas d’erreur.
FileNotFoundException de notre premier exemple est une exception vérifiée, levée par la
méthode FileInputStream. Ceci implique :
- Que la méthode FileInputStream a précisé dans sa signature que c’est une méthode qui peut
générer une exception de type FileNotFoundException :
- FileInputStream (String s) throws FileNotFoundException;
- Que la méthode appelante (la fonction main dans notre cas) doit indiquer explicitement la
maniéré de traiter cette erreur ou bien la faire propager en la signalant dans son prototype.
Dans le cas contraire, une erreur de compilation sera générée par java. La figure 78 permet
d’illustrer ces différents scénarios.
Rq. FileNotFoundException est une exception qui hérite de la classe Exception. En fait, toutes les
exceptions vérifiées doivent hériter de la classe Exception.

Mohamed HAMMOUDA 74
LES EXCEPTIONS DANS JAVA

public class OuvrirFichier {


public static void main(String args[]) {
Erreur de compilation.
FileInputStream fis = new FileInputStream("fichier.txt");
} Exception non gérée
}

public class OuvrirFichier { Exception levée pour la


public static void main(String args[]) throws FileNotFoundException { fonction appelante. Pas
FileInputStream fis = new FileInputStream("fichier.txt"); d’erreur de compilatoin
}
}

public class OuvrirFichier {


public static void main(String args[]) {
try{
FileInputStream fis = new FileInputStream("fichier.txt");
}
Catch(FileNotFoundException ex){ Exception géré à travers
//traiter l’exception un gestionnaire d’erreur
} try-catch-finally
}
}

Figure 78 – Exemple de gestion des exceptions vérifiées

II.1.2. Les exceptions non vérifiées


Avec une exception non vérifiée, les méthodes pouvant les générer ne les déclarent pas
explicitement, et aucun programme n’est obligé à part le compilateur de les traiter. Dans le pire
des cas, l’exception remontera toute la pile d’appel et arrêtera sauvagement votre application.
ArrayIndexOutofBoundsException est une erreur non vérifiée. Java n’impose pas qu’elle soit
traitée. En cas où elle n’a pas été traitée, elle se propagera à travers les fonctions appelantes
jusqu’à rencontrer un gestionnaire d’exception permettant de la traiter, dans le cas contraire le
programme s’arrête.
Rq. ArrayIndexOutofBoundsException est une exception qui hérite de la classe
RunTimeException. En fait, toutes les exceptions non vérifiées doivent hériter de la classe
Exception.

II.1.3. Les erreurs


Les exceptions de type Error représentent des erreurs critiques qui ne sont pas censées être
gérées en temps normal. Par exemple, une exception de type OutOfMemoryError est levée
lorsqu’il n’y a plus de mémoire disponible dans le système.

II.2. Lever une exception


Lever une exception dans un code annonce que nous venons de détecter un scénario de
problème pouvant provoquer le dysfonctionnement de notre programme. Si l’exception levée
hérite directement de la classe Exception, alors il faut ajouter à l’entête de cette méthode un bout
de code indiquant qu’elle peut générer une telle exception. Dans le cas contraire (exception de
type RunTimeException : exception non vérifiée), nous aurons le choix d’ajouter ce bout de code

Mohamed HAMMOUDA 75
LES EXCEPTIONS DANS JAVA

ou pas. Lorsqu’une méthode, X par exemple, a été invoquée dans une deuxième méthode Y, si
l’exception est de type Exception alors Y doit soit gérer l’exception qui peut être provoquée par X,
soit la déclarer à son tour dans sa signature de méthode. Dans le cas contraire (exception de type
RunTimeException : exception non vérifiée), Y peut soit gérer l’exception, soit l’ignorer et par
conséquent elle sera propager à la méthode appelante. Pour lever une exception, plusieurs
étapes doivent être réalisées :
• Déclarer une exception héritant de la classe Exception (pour les exceptions vérifiées), ou
bien de la classe RunTimeException (pour les exceptions non vérifiées).
• Détecter dans une méthode X le problème pouvant générer cette exception.
• Lever l’exception, et modifier l’entête de X si l’exception est une exception vérifiée.
• Si X a été invoquée dans une méthode Y :
 Si l’exception est une exception vérifiée, alors il faut obligatoirement la gérer dans
Y ou bien indiquer que Y peut aussi générer une telle exception.
 Si l’exception est une exception non vérifiée, alors Y peut soit la gérer soit l’ignorer.
Nous allons, à travers un exemple simple de calcul de factoriel, mettre en œuvre le mécanisme de
traitement des exceptions vérifiées.

Déclaration d’une
class StopException extends Exception{} exception vérifiée
héritant de la classe
class Factorielle { Exception
int factorielle (int n) {
int resultat = 1; Détecter un problème qui peut
if (n<0) entraver l’exécution du
throw new StopException() ; programme et lever une
if (n<= n; i++) { exception de type
resultat= resultat *i; StopException
}
return resultat;
} Méthode ne gère l’exception
void methodeYPropageException () throws StopException{ générée par factorielle mais la
System.out.println(factorielle(-100)) ; propage à une autre méthode
}
appelante
void methodeYGereException(){
try {
System.out.println(factorielle(-100)) ; Méthode qui gère l’exception
} StopException dans un bloc try-
Catch(StopException ex){ catch
System.out.println(factorielle(" erreur ") ;
}
} Méthode génère une erreur de
Void methodeYErreurCompilatopn(){
compilation parce que la méthode
System.out.println(factorielle(-100)) ;
} ne gère pas l’exception vérifiée
}

Figure 79 – Le mécanisme de traitement des exceptions vérifiées

Mohamed HAMMOUDA 76
LES EXCEPTIONS DANS JAVA

II.3. le gestionnaire des exceptions : les blocs try-catch-finally


Lorsque nous travaillons avec des gestionnaires d'exception, nous utilisons les blocs try (essayer),
catch (intercepter), et finally (enfin). Avant de commencer à travailler avec ces concepts, nous
allons répondre à trois questions simples:
Essayez quoi ? : D'abord, vous essayez d'exécuter votre code. S’il ne s'exécute pas comme prévu,
une exception est générée
Intercepter quoi ? Vous interceptez l’objet exception généré par le code enfermé dans try.
Finalement qu'est-ce que faire ? Enfin, vous exécutez un ensemble de codes, que ce soit il y a eu
des exceptions ou pas dans votre code.
Afin de mieux cerner ce concept fondamental pour la stabilité et la performance de votre
application, nous allons commencer par exposer un petit exemple permettant d’expliquer
d’avantage les composants d’un gestionnaire des exceptions.

try {
Tester ce code ... // ❶ Tester ce code
Si vous avez une }
catch (TypeException objException) { Si tout va bien, branchez
exception présentez
la ici ... // ❷ vous ici. Exécutez ❸ puis

Si vous avez terminé le


} passez à ❹
Finally{
traitement de votre
.. // ❸
exception, exécutez ❸ }
puis passez à ❹ …//❹

Figure 80 – Les composants d’un gestionnaire d’exception


Le code en ❶ est normalement exécuté. Si une exception est levée lors de cette exécution, les
instructions restantes dans le code en ❶ sont abandonnées. Si la classe de l’exception levée
dans le bloc ❶ est de type TypeException alors le code en ❷ est exécuté (car l’exception est
récupérée).
Dans le code ❷, on peut faire référence à l’exception en utilisant le nom donné à celle-ci
(objException). Dans le cas où la classe de l’exception générée n’est pas de type TypeException,
le code en ❷ est ignoré, parce que nous n’avons prévu que le traitement d’une seule
exception qui doit être de type TypeException ou bien une classe qui hérite de TypeException.
Le code en ❸ est exécuté que ce soit les blocs ❶ ou ❷ ont été exécutés ou pas. Ainsi le
bloc finally est généralement utilisé pour libérer des ressources à la fin de chaque bloc logique
de code. Par exemple, la fermeture de la connexion aux bases de données ou le levé des
verrous sur des variables partagées.
Pour simplifier, nous pouvons dire que si tout vas bien dans ❶, tout le code en ❶ est
exécuté puis tout le code en ❸ est exécuté, et le programme continue son exécution à partir
de ❹. Evidemment le code en ❷ est ignoré.
Maintenant, si un problème est rencontré dans le bloc ❶, alors l’exécution des instructions de
ce bloc est interrompue et un objet exception est généré. S’il existe un bloc catch qui traite ce

Mohamed HAMMOUDA 77
LES EXCEPTIONS DANS JAVA

type d’exception alors l’exception est traitée, puis le programme reprend à partir de ❹ après
avoir exécuté la partie ❸. Dans le cas contraire, c’est le bloc ❸ qui est exécuté avant que le
programme ne lève l’exception au système d’exploitation.

II.4. Gérer plusieurs exceptions


Dans l’exemple précédent, nous avons appris comment gérer une seule exception dans un bloc de
code. Nous avons compris que pour pourvoir intercepter une exception dans le bloc catch, il faut
impérativement que le type de cette exception soit bien mentionné dans la partie déclarative du
bloc catch. Par défaut, cette exception est propagée au programme appelant.
La question qui se pose maintenant : qu’est ce qu’il faut faire pour gérer plusieurs
exceptions pouvant être générées dans le code ?
En fait, java fournit une réponse à ce problème en vous permettant de définir plusieurs clauses
catch pour un même try. L’exemple suivant présente une fonction principale (main) dans laquelle
une méthode susceptible de générer plusieurs types d’exceptions a été invoquée. Afin de traiter
toutes les exceptions, plusieurs clauses catch ont été définies pour mieux cerner les exceptions
qui peuvent être générées par la méthode meth().

Class UseActions2 {
Public static void main(String[] args) {
Action Obj = new Action1();
System.out.printLn(“Début du programme.”);
try {
Obj.meth() ;
}
catch ( ArithemeticException E ) {
System.out.printLn(‘Interception ArithemeticException’);
}
catch ( ArrayStroreException E ){
System.out.printLn(‘Interception ArrayStroreException’);
}
catch ( ClassCastException E ){
System.out.printLn(‘Interception ClassCastException ’);
}
System.out.printLn(‘Fin du programme.’) ;
}
}

Figure 81 – Exemple de gestion de plusieurs exceptions

Une dernière question, l’ordre des catch est-il important ?


En fait, dans un gestionnaire try...catch comprenant plusieurs clauses, la recherche de la clause
catch contenant le traitement de la classe d'exception appropriée s’effectue séquentiellement
dans l’ordre d’écriture des lignes de code. La recherche va s'effectuer comme si le programme
contenait des if...else if... imbriqués. Les tests seraient effectués sur l'appartenance de l'objet
d'exception à une classe à l'aide de l'opérateur instanceof. Soit le pseudo-code java suivant :

Mohamed HAMMOUDA 78
LES EXCEPTIONS DANS JAVA

try {
//bloc de code à protéger générant un objet exception
} if (<Objetexception>
catch ( TypeException1 ex ) { //Traitement TypeException1 } instanceof TypeException1)
{ //Traitement TypeException1
catch ( TypeException2 ex ) { //Traitement TypeException2 }
}
.....
catch ( TypeExceptionk ex ) { //Traitement TypeExceptionk }

Figure 82 – Plusieurs types d’exceptions dans un même bloc try


Rappelons que l'opérateur instanceof agit sur une classe et ses classes filles (sur une hiérarchie de
classes), c'est à dire que tout objet de classe TypeExceptionX est aussi considéré comme un objet
de classe mère. Par conséquent, un objet de classe TypeExceptionX est aussi considéré un objet
de la classe Exception qui est la classe mère de toutes les exceptions Java. On choisira donc,
lorsqu’il y a une hiérarchie entre les exceptions à intercepter, de placer le code dans les
gestionnaires dans l’ordre inverse de la hiérarchie.

public class OuvrirFichier {


public static void main(String args[]) {
try{
FileInputStream fis = new FileInputStream("fichier.txt");
}

Catch (Exception ex ) { ❶
System.out.println("tout va bien ") ;
}

Catch(FileNotFoundException ex ) { ❷
System.out.println("attention !!!!! fichier introuvable ") ;
}
}
}

Figure 83 – L’ordre d’exécution des gestionnaires d’exception


Reprenons l’exemple de la lecture d’un fichier texte présenté au début du chapitre. Nous allons
faire exprès de ne pas respecter cette règle. Discutons ensemble le code de la figure 83.
Supposons que le constructeur de la classe FileInputStream génère une exception de type
FileNotFoundException. Dans ce cas, et comme nous l’avons expliqué, l’exécution va être
interrompue et un branchement aux blocs catch du gestionnaire des exceptions est effectué. Le
premier catch de notre programme permet de résoudre les exceptions de type Exception ou bien,
comme nous l’avons précisé, toute exception dérivée de Exception.
Rappelez-vous que FileNotFoundException est une sous-classe de la classe Exception, ce qui
implique que FileNotFoundException est un sous-type de la classe Exception. Ainsi, un objet de
type FileNotFoundException est une Exception. En se basant sur cette logique, nous pouvons dire
que notre exception générée par le constructeur de la classe FileInputStream est traité au niveau
de la première clause catch qui affiche un message « tout va bien ». La deuxième clause catch,
bien qu’elle permet de traiter spécifiquement une exception de type FileNotFoundException, sera
ignorée et le programme continuera à s’exécuter juste après.

Mohamed HAMMOUDA 79
LES EXCEPTIONS DANS JAVA

Ce n’est pas logique, n’est-ce pas ? En fait, nous avons essayé, à travers cet exemple, de justifier le
positionnement des clauses catch dans un gestionnaire d’exceptions en se basant sur le
raisonnement par absurde. Par conséquent, nous pouvons dire que les clauses catch doivent
traiter les exceptions du plus spécifiques aux plus générales. Notez bien que lorsque plusieurs
clauses catch traitent des exceptions n’ayant pas un lien hiérarchique entre elles, l’ordre de leur
positionnement sera aléatoire. Reprenons le dernier exemple mais cette fois-ci avec le bon ordre
des clauses catch.

public class OuvrirFichier {


public static void main(String args[]) {
try{
FileInputStream fis = new FileInputStream("fichier.txt"); }

Catch(FileNotFoundException ex){
System.out.println("attention !!!!! fichier introuvable ") ; }
Catch (Exception ex){
System.out.println("tout va bien ") ; }
}
}

Figure 84 – Positionnement des gestionnaires d’exception FileNotFoundException et


Exception

II.5. Les exceptions prédéfinies de java


Nous allons maintenant vous présenter les exceptions prédéfinies de java les plus fréquemment
rencontrées.

II.5.1. NullPointerException :
Cette exception est levée par la JVM si vous essayez d'accéder à une méthode ou à une variable à
travers une variable de référence pointant vers null, autrement dit, à chaque fois que vous
essayez d’accéder à un membre d’un objet non encore instancié. La figure 85 présente la
hiérarchie des classes de NullPointerException.

Java.lang.Throwable

Java.lang.Exception

Java.lang.RuntimeException

Java.lang.NullPointerException

Figure 85 – La hiérarchie des classes de NullPointerException

Mohamed HAMMOUDA 80
LES EXCEPTIONS DANS JAVA

II.5.2. ArrayIndexOutOfBoundsException

Java.lang.Throwable

Java.lang.Exception

Java.lang.RuntimeException

Java.lang.IndexOutOfBoundsExeption

Java.lang.ArrayIndexOutOfBoundsExeption

Figure 86 – La hiérarchie des classes de ArrayIndexOutOfBoundsException


Comme le montre la figure 86, ArrayIndexOutOfBoundsException est une exception de type
RunTimeException. Elle est levée lorsqu’un bout de code tente d'accéder à un tableau au-delà de
ses limites (une position inférieure à 0 ou bien supérieure ou égale à sa longueur).

II.5.3. ClassCastException :
ClassCastException est levée lorsque la JVM essaye de convertir un objet vers une classe dérivée
pour laquelle il n’est pas une instance.

Java.lang.Throwable

Java.lang.Exception

Java.lang.RuntimeException

Java.lang.ClassCastException

Figure 87 – La hiérarchie des classes de ClassCastException


C’est un peu délicat n’est-ce pas ?
Nous allons reprendre l’exemple de la classe Forme du chapitre précédant et discuter ensemble le
code suivant afin d’éclaircir d’avantage cette exception :

Mohamed HAMMOUDA 81
LES EXCEPTIONS DANS JAVA

Forme f = new Forme() ;


Forme f1=new Cercle() ;
Forme f2 = new Rectangle() ;
Cercle c=(Cercle)f ; ❶
Cercle c1=(Cercle)f1 ;❷
Cercle c2 =(Rectangle)f2 ;❸

Figure 88 – Exemple d’exception ClassCastException


Dans ❶, l’objet réel pointé par f est une Forme. Nous avons essayé de transformer cet objet
pointé par une variable de type Forme vers une classe dérivée Cercle. La JVM échoue à réaliser
cette opération étant donné que l’instance de f (objet réel/Objet concret) n’est pas un Cercle.
Dans ❷, l’objet réel pointé par f1 est un Cercle. Nous avons essayé de transformer cet objet
pointé par une variable de type Forme vers une classe dérivée Cercle. La JVM réussie à réaliser
ceci étant donné que l’instance réelle de f1 est un Cercle.
Dans ❸, le système génère une erreur de compilation et non pas une erreur d’exécution étant
donné que le compilateur java refuse de référencer un objet de type Rectangle par une variable
de référence de type Cercle.

II.5.4. NumberFormatException :
Que pensez-vous si vous tentez de convertir les chaînes de caractères "87" et " ??007X88 # " à
des valeurs numériques ?
Il est clair que pour la première valeur il n’y a pas de problème, alors que pour la deuxième il faut
faire attention.
Comme le montre la figure 89, NumberFormatException est une exception de type
RunTimeException. Elle est levée pour indiquer que la JVM ne peut pas répondre à une demande
de conversion d’une chaîne (avec un format inapproprié) à un type numérique spécifique. La
commande suivante génère une exception de type NumberFormatException :
System.out.println(Integer.parseInt("12ABCD"));

Java.lang.Throwable

Java.lang.Exception

Java.lang.RuntimeException

Java.lang.IllegalArgumentException

Java.lang.NumberFormatException

Figure 89 – La hiérarchie des classes de NumberFormatException

Mohamed HAMMOUDA 82