0% ont trouvé ce document utile (0 vote)
139 vues58 pages

Cours Java

Transféré par

peow tyuik
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
139 vues58 pages

Cours Java

Transféré par

peow tyuik
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

ISET-Sousse

Introduction.
En quoi l’approche objet est-elle tellement attractive ?
La stabilité de la modélisation par rapport aux entités du monde réel, la construction itérative
facilitée par le couplage faible entre composants et la cohésion forte au sein d’un même
composant, la possibilité de réutiliser des éléments d’un développement à un autre. Certain
insistent également sur la simplicité du modèle qui fait appel seulement à cinq concepts
fondateurs (les objets, les messages, les classes, la généralisation et le polymorphisme) pour
exprimer de manière uniforme l’analyse, la conception et la réalisation d’une application
informatique.
D’une manière générale, toute méthode de construction de logiciels doit prendre en compte
l’organisation, la mise en relation et l’articulation de structures pour en faire émerger un
comportement macroscopique complexe : le système à réaliser. L’étude d’un système doit
prendre en considération l’agencement collectif des parties et conduire à une vue plus globale des
éléments qui le composent.
La construction d’un logiciel est par conséquent une suite d’itérations de genre « division
réunion » ; il faut décomposer, diviser pour comprendre et il faut composer, réunir pour
construire. Cela conduit à une situation paradoxale « il faut diviser pour réunir »
Face à ce paradoxe, le processus de décomposition a été dirigé traditionnellement par un critère
fonctionnel. Les fonctions du systèmes sont identifiées, puis décomposées en sous fonctions et
cela récursivement jusqu’à l’obtention d’éléments simples, directement représentables dans les
langage de programmation (par des fonctions et des procédures)

Fonction principale

Sous fonction 1 Sous fonction 2

Sous Sous Sous Sous


fonction 1.1 fonction 1.2 fonction 2.1 fonction 2.2

L’architecture logicielle est alors le reflet des fonctions du système. Cette démarche apporte des
résultats satisfaisants lorsque les fonctions sont bien identifiées et lorsqu’elles sont stables dans le
temps. Toute fois étant données que la fonction induit une structure, les évolutions fonctionnelles
peuvent provoquer des modifications structurelles lourdes du fait du couplage statique entre
architecture et fonctions.

Cours Programmation Orientée Objet 1/58


ISET-Sousse

L’approche objet propose une méthode de décomposition, non basée uniquement sur ce que le
système fait, mais plutôt sur l’intégration de ce que le système est le fait. Initialement basée sur
l’utilisation d’objet en tant qu’abstraction du monde réel, l’approche objet a pour but une
modélisation des propriétés statiques et dynamiques de l’environnement dans lequel sont définis
les besoins. Les fonctions se représentent alors par des formes de collaboration entre les objets
qui composent le système. Le couplage devient dynamique, et les évolutions fonctionnelles ne
remettent plus en cause la structure statique du logiciel.
La force de l’objet provient de sa double capacité à décomposer « différencier » et recomposer
« réunir » du fait de la richesse de ses mécanismes d’intégrations, provient de sa capacité à
regrouper ce qui a été séparé, à construire le complexe à partir de l’élémentaire et à intégrer
statiquement et dynamiquement les constituants du système.
Notion d’objet
Dans les langage impératifs que vous connaissez, les déclarations des structures de données et les
déclarations des fonctions restent formellement séparées même quand les structures de données
sont associés à des groupes de fonctions qui les modifient.

Fonction1

Fonction4 Données Fonction2

Fonction3

Plutôt de décomposer un programme en deux parties, une qui déclare les structures de données et
une qui définit les fonctions associées, la programmation objet décompose le programme en
classes regroupant chaque structure de données avec les fonctions qui la modifient. Les instances
de ces classes sont appelées des objets. Un programme sera plus vu comme un programme
principal qui appelle un ensemble de fonctions, mais comme un ensemble de composants
autonomes qui interagissent entre eux pour faire émerger un résultat global.

Données

Fonctions d ‘accès aux données

Conclusion.
La POO permet de créer des programmes modulaires dont le code est facilement réutilisable dans
d'autres applications (faible couplage + technique de l'héritage). Par ailleurs, elle assure l'intégrité
des données (par un mécanisme de masquage des membres de classes appelé encapsulation), ce
qui facilite la conception et la mise au point (déboggage).

Cours Programmation Orientée Objet 2/58


ISET-Sousse

L’approche objet

Cours Programmation Orientée Objet 3/58


ISET-Sousse

1. Présentation de la notion d'objet


1.1. Définition.
L’objet est une unité atomique formée de l’union d’un état et d’un comportement. Il fournit une
relation d’encapsulation qui assure à la fois une cohésion interne très forte et un faible couplage
entre le dit objet et l’extérieur.
Le monde réel est constitué d’objet matériels de toutes sorte : grains de sable, étoiles, voiture…,
notre perception intuitive de ce que constitue un objet est fondée sur le concept de masse c’est à
dire la quantité de matière qui caractérise un corps. Par extension il est possible de définir
d’autres objets sans masse comme les comptes bancaire ou encore les équations mathématiques.
Les objets peuvent également appartenir à des mondes virtuels comme des communautés de
personnes à titre d’exemple.
Les objets informatiques définissent une représentation abstraite des entités d’un monde réel ou
virtuel dans le but de les piloter ou de les simuler.
Comme les être vivants, les objets du monde réel naissent et meurent, la modélisation objet
permet de représenter le cycle de vie des objet au travers de leurs interactions.
1.2. Caractéristiques fondamentales d’un objet.
Tout objet présente les trois caractéristiques suivantes : un état, un comportement et une identité.

Objet = Etat + Comportement + Identité

Un objet sans état ou sans comportement peut exister mais dans tous les cas il possède une
identité.
1.2.1. L’état.
L’état regroupe les valeurs instantanées de tous les attributs d’un objet (ses propriétés) sachant
qu’un attribut est une information qualifiant l’objet qui le contient. Chaque attribut peut prendre
une valeur dans un domaine de définition donnée. L’état d’un objet, à un instant donné,
correspond à une sélection de valeurs, parmi toutes les valeurs possibles des différents attributs.
Le diagramme suivants montre un objet voiture qui contient les valeurs de trois attributs
différents : la couleur, la masse et la puissance fiscale.

Figure 1.2.1.1. Objet voiture

Cours Programmation Orientée Objet 4/58


ISET-Sousse

L’état évolue au cours du temps ; ainsi, lorsque la voiture roule, la quantité de carburant diminue,
certaines composantes de l’état peuvent êtres constantes : c’est le cas par exemple de la marque
de la voiture, ou encore du pays de sa construction.

Figure 1.2.1.2 Evolutions de l’état d’un objet


en conséquence des comportements passés

1.2.2. Le comportement.
Le comportement regroupe toutes les compétences d’un objet et décrit les actions et les réactions
de cet objet, il est composé d’un ensemble d’opérations appelées méthodes. Les opérations d’un
objet sont déclenchées suite à une stimulation externe représentée sous forme d’un message
envoyé par un autre objet.
Le diagramme suivant, selon le valeur du message reçu, l’Opération 1 ou l’Opération 2 est
déclenchée.

Un autre objet

Un message

Opération 1 Opération 2

Un objet

Figure 1.2.2.1. Invocation de méthode par l’envoi de message

En réponse à un message, l’objet destinataire déclenche un comportement. Le message sert à


sélectionner l’opération à exécuter

Cours Programmation Orientée Objet 5/58


ISET-Sousse

L’état et le comportement sont liés ; en effet, le comportement à un instant donné dépend de l’état
courant, et l’état peut être modifié par le comportement. Il est possible de faire atterrir un avion à
la condition qu’il soit en train de voler ; en d’autres termes, le comportement Atterrir n’est valide
que si l’information En vol est valide. Après atterrissage, l’information En vol devient invalide, et
l’opération Atterrir n’a plus de sens. L’exemple suivant montre les liens entre l’état et le
comportement.

: Avion
[En vol]

Atterrir

Décoller :Avion
:Tour de contôle
[Au sol]

Figure 1.2.2.2. Relation entre comportement et état

1.2.3. L’identité.
Tout objet possède une identité qui caractérise son existence propre. L’identité permet de
distinguer tout objet de façon non ambiguë, et cela indépendamment de son état. Ainsi il est
possible de distinguer deux objets dont toutes les valeurs d’attributs sont identiques.

Figure 1.2.3.Chaque objet possède une id différente

Chaque objet possède une identité attribuée de manière implicite à la création de l’objet et jamais
modifié.

Cours Programmation Orientée Objet 6/58


ISET-Sousse

1.3. Communication entre objets.


Le comportement global d’une application repose donc sur la communication entre les objets qui
le composent. De ce fait l’étude des formes de communication entre objets du domaine est de
première importance dans la modélisation objet.
1.3.1. Catégories de comportement.
Les objets interagissent pour réaliser les fonctionnalités de l’application. Selon la nature des
interactions il est possible de classer le comportement des objets en trois catégories : les acteurs,
les serveurs et les agents.
Les acteurs sont toujours des objets à l’origine d’une interaction. Ce sont généralement des objets
actifs c’est à dire possède un fil d’exécution (thread) et qu’il passe la main aux autres objets. Les
serveurs, au contraire ne sont jamais à l’origine d’une interaction, mais sont toujours les
destinataires des messages. Dans ce cas, le flot de contrôle est passé au serveur par l’objet qui
envoie le message et est récupéré après exécution du service.
Les agents cumulent les caractéristiques des acteurs et des serveurs. Ces objets ont un
comportement très proche de celui des humains ; ils peuvent interagir avec les autres objets à tout
moment, de leur propre initiative ou suite à une sollicitation externe.
Les agents sont à la base du mécanisme de délégation qui permet à un objet de se comporter un
paravent devant un autre objet. De cette manière, un client peut communiquer avec un serveur
qu’il ne connaît pas directement et, de plus le serveur peut changer entre deux passages de
messages.
Dans l’exemple suivant, le client communique indirectement avec le premier serveur sans
connaître et sans savoir qu’il existe deux autres serveurs. Le routage de message du client vers le
serveur est assuré dynamiquement par l’agent intermédiaire.

Routage
dynamique
Serveur1

Un client Un agent Serveur2

Serveur3
Objet pavant

Figure 1.3.1. Mécanisme de délégation

Cours Programmation Orientée Objet 7/58


ISET-Sousse

1.3.2. Le concept de message.


C’est l’unité de communication entre objets, les messages est le support d’une relation de
communication qui relie, de façon dynamique les objets qui ont été séparés par le processus de
décomposition.
Les objets interagissent entre eux par l'envoi de messages. Le message qu'envoie un premier objet
à un deuxième objet consiste en l'appel d'une méthode du deuxième objet. Le deuxième objet va
alors effectuer une certaine tâche et, le cas échéant, retourner un résultat au premier objet. Un
message permet donc à un objet de modifier indirectement l'état d'un autre objet. Une méthode
peut elle-même envoyer des messages à d'autres objets et les résultats qui lui sont retournés lui
permettent de changer l'état de l'objet auquel elle appartient et de produire elle-même un résultat.
Le message est un intégrateur dynamique qui permet de reconstituer une fonction de l’application
par la mise en collaboration d’un groupe d’objets.

2. Les classes.
2.1 Définition
Une classe constitue un modèle de construction d’objet. Il s’agit d’une représentation abstraite
d’une entité concrète (ville, véhicule, étudiant) ou abstraite (date, réunion, planning de
réservation). La classe peut être considérée comme un moule à partir duquel on peut créer des
objets.
Une classe est un type de structure de données qui unit la liste des attributs d'un objet et les
méthodes qui opèrent sur ces attributs. Voici l'exemple classique de la classe Compte qui
représente un compte en banque qui possède 3 attributs (solde, numéro et propriétaire) et définit 3
méthodes (création, depot, retrait)

EN JAVA : EQUIVALENT EN C :
class Compte typedef struct
{ {
int solde; int solde;
int numero; int numero;
String proprietaire; char* proprietaire;
} Compte;
void creation(int num, String prop)
{ void creation(Compte *c, int num, char* prop)
solde = 0; {
numero = num; c->solde = 0;
proprietaire = prop; c->numero = num;
} c->proprietaire = prop;
}
void depot(int montant)
{ void depot(Compte* c, int montant)
solde = solde + montant; {
} c->solde = c->solde + montant;
}
void retrait(int montant)
{ void retrait(Compte* c, int montant)
solde = solde - montant; {
} c->solde = c->solde - montant;
} }

Cours Programmation Orientée Objet 8/58


ISET-Sousse

2.2 Les membres d’une classe :


Une classe se compose d’une collection de données enregistrées au sein de champs nommés, et
de code organisé en méthodes nommées qui agissent sur ces données.

Fig.2.2. Représentation d’une classe

2.2.1. Les attributs (partie statique).


Les attributs d’une classe correspondent aux propriétés de la classe. Il sont définis par un nom, un
type et éventuellement une valeur initiale.
Chaque objet instance d’une classe, donne des valeurs particulières à tous les attributs définis
dans sa classe et fixe par là même son état.
2.2.2. Les méthodes (partie dynamique).
Les méthodes représentent l'ensemble des actions, procédures, fonctions ou opérations que l'on
peut associer à une classe. L'ensemble des méthodes de la classe définit le
"COMPORTEMENT".

2.3. Instanciation.
Les objets informatiques sont construits à partir de la classe, par un processus appelé
instanciation. De ce fait l’objet est une instance d’une classe.
L’instance est une copie en mémoire de la classe pour laquelle les valeurs des attributs ont été
fixées et le code des méthodes ajouté au code de l'application ; c’est une représentation
« vivante ». d’une classe.

On peut alors déclarer des objets de type Compte. On dit que ces objets sont des instances de la
classe Compte. En Java, la déclaration d'un objet ne crée qu'une référence (pointeur) sur cet objet.
Il faut ensuite allouer cet objet en mémoire grâce à l'opérateur new.

EN JAVA : EQUIVALENT EN C :
Compte unCompte; Compte* unCompte;
unCompte = new Compte(); unCompte = (Compte*) alloc(sizeof(Compte));

Cours Programmation Orientée Objet 9/58


ISET-Sousse

Une fois l'objet créé, on peut accéder à ses attributs ou lui demander d'exécuter ses méthodes.
Pour ceci, on mentionne le nom de l'objet suivi d'un point suivi d'un nom d'attribut ou de méthode
et de ses paramètres :

EN JAVA: EQUIVALENT EN C:
unCompte.creation(1234, "Aymen"); creation(unCompte, 1234, "Aymen");
unCompte.depot(1000); depot(unCompte, 1000);
unCompte.retrait(450); retrait(unCompte, 450);
System.out.println("Il reste " + unCompte.solde + " printf("Il reste %f a %s", unCompte->solde,
a " + unCompte.proprietaire); unCompte->proprietaire);

Class Instance

solde = 1200
numero = 1234
Instanciation propriétaire = Aymen

solde = 700
numero = 1255
proprietaire = Imen

Figure 2.3. Instanciation d’une classe en deux objets

On peut ne pas préfixer un nom d'attribut ou de méthode si on l'écrit à l'intérieur de la définition


d'une méthode. L'attribut ou la méthode désignés sont alors ceux de l'objet auquel appartient la
méthode où ils sont écrits. Ex : on ajoute la méthode suivante à la classe Compte :

void virement(int montant, Compte destinataire)


{
retrait(montant);
destinataire.depot(montant);
}

Cours Programmation Orientée Objet 10/58


ISET-Sousse

Présentation générale de JAVA

I. Les caractéristiques
Java possède un certain nombre de caractéristiques qui ont largement contribué à son énorme
succès.

Java est interprété.


Le code source est compilé en pseudo code ou byte code puis exécuté par un interpréteur Java : la
Java Virtual Machine (JVM). La JVM est présente sur Unix, Win32, Mac, OS/2, Netscape, IE, ...
Ce concept est à la base du slogan de Sun pour Java : WORA (Write Once, Run Anywhere :
écrire une fois, exécuté partout). En effet, le byte code, s'il ne contient pas de code spécifique à
une plate-forme particulière peut être exécuté et obtenir les même résultats sut toutes les
machines disposant d'une JVM.

Java est indépendante de toute plate-forme (portable)


Il n'y a pas de compilation spécifique pour chaque plate forme. Le code reste indépendant de la
machine sur laquelle il s'exécute. Il est possible d'exécuter des programmes Java sur tous les
environnements qui possèdent une Java Virtual Machine. Cette indépendance est assurée au
niveau du code source grâce à Unicode et au niveau du byte code.

Java est orienté objet.


Comme la plupart des langages récents, java est orienté objet. Chaque fichier source contient la
définition d'une ou plusieurs classes qui sont utilisées les unes avec les autres pour former une
application. Java n'est complètement objet par il définit des types primitifs (entier, caractère,
flottant, booléen,...).

Java est simple


Le choix de ses auteurs a été d'abandonner des éléments mal compris ou mal exploités des autres
langages tels que la notion de pointeurs (pour éviter les incidents en manipulant directement la
mémoire), 'héritage multiple et la surcharge des opérateurs, ...
Java est fortement typé toutes les variables sont typées et il n'existe pas de conversion
automatique qui risquerait une perte de données. Si une telle conversion doit être réalisée, le
développeur doit obligatoirement utiliser un cast ou une méthode statique pour la réaliser.

Java assure la gestion de la mémoire.


L'allocation de la mémoire pour un objet est automatique à sa création et Java récupère
automatiquement la mémoire inutilisée grâce au garbage collector qui restitue les zones de
mémoire laissées libres suite à la destruction des objets.

Java assure la sécurité.


L sécurité fait partie intégrante du système d'exécution et du compilateur. Un programme Java
planté ne menace pas le système d'exploitation. Il ne peut pas y avoir d'accès direct à la mémoire.
L'accès au disque dur est réglementé dans une applet.

Cours Programmation Orientée Objet 11/58


ISET-Sousse

Les applets fonctionnant sur le Web sous soumis aux restrictions suivantes dans la version 1.0 de
Java :
 aucun programme ne peut ouvrir, lire, écrire ou effacer un fichier sur le système de
l'utilisateur
 aucun programme ne peut lancer un autre programme sur le système de l'utilisateur
 toute fenêtre créée par le programme est clairement identifiée comme étant une fenêtre
Java, ce qui interdit par exemple la création d'une fausse fenêtre demandant un mot de
passe
 les programmes ne peuvent pas se connecter à d'autres sites Web que celui dont ils
proviennent.

Java est économe.


Le pseudo code a une taille relativement petite car les bibliothèques de classes requises ne sont
liées qu'à l'exécution.

Java est multitâche.


Il permet l'utilisation de threads qui sont des unités d'exécution isolées. La JVM elle même utilise
plusieurs threads.

Java est distribué.


Il présente des API réseau (java.net.Socket, java.net.URL, ...) permettant de développer des
applications distribuées.
 Chargement / génération de code dynamique.
 Applet.
 Servlet.
 Protocole / Content handler.
 Remote Method Invocation.
 JavaIDL (CORBA).

Java est libre.


L’environnement de développement standard est distribué librement par Sun; d’autre part le
langage n’est pas propriétaire et de ce fait plusieurs environnements de développement et
machines virtuelles sont disponibles (vous pouvez même écrire votre propre machine virtuelle !)

II. Les techniques de base de programmation en Java


II.1. L’édition du fichier source
N'importe quel éditeur de texte peut être utilisé pour éditer un fichier source Java. Il est
nécessaire de compiler le source pour le transformer en J-code ou byte-code Java qui sera lui
exécuté par la machine virtuelle.
Pour être compilé, le programme doit être enregistré au format de caractères Unicode : une
conversion automatique est faite par le JDK si nécessaire.

Cours Programmation Orientée Objet 12/58


ISET-Sousse

II.2. La compilation d'un code source


Une fois le code source du programme est écrit, la seconde étape consiste en la compilation du
code source en du pseudo-code machine. Ici réside donc une différence majeure avec des
langages compilés comme le C ou le C++. En effet, pour ces langages, la compilation d’une
source permet d’obtenir directement un code exécutable (contenant des instructions comprises
par la machine). Puisque ce code contient des instructions machine, il ne peut être exécuté que
sur la plate-forme pour laquelle il a été prévu. Pour Java, au contraire, la compilation d’une
source permet d’obtenir non pas un code exécutable contenant des instructions machine, mais un
pseudo-code contenant des instructions de bas-niveau pouvant être interprétées par une machine
virtuelle Java. Ce pseudo-code pourra donc être exécuté (interprété) sur différents systèmes
d’exploitation. La compilation d’un programme Java s’effectue à l’aide d’un compilateur Java
(comme javac) et génère un fichier compilé avec l’extension .class.

test.java javac test.java test.class

Pour compiler un fichier source il suffit d'invoquer la commande javac avec le nom du fichier
source avec son extension .java
javac NomFichier.java
Le nom du fichier doit correspondre au nom de la classe principale en respectant la casse même si
le système d'exploitation n'y est pas sensible. Suite à la compilation, le pseudo code Java est
enregistré sous le nom NomFichier.class

II.3. L'exécution d'un programme


Il est maintenant possible d’exécuter le programme Java. Puisque le fichier compilé ne contient
pas d’instructions directement compréhensibles par la machine du pseudo-code, nous devons
utiliser une machine virtuelle Java ou JVM (Java Virtual Machine). Une machine virtuelle permet
l’exécution de programmes, la JVM livrée dans le JDK (Java Development Kit) fourni par Sun se
nomme tout simplement java. Pour exécuter un programme compilé, il faut passer à cette
commande un argument qui est le nom du fichier compilé (sans son extension). Dans notre
programe exemple, la commande à entrer est donc java test.
Ce n’est pas n‘importe quelle classe définie et compilée peut être exécuter, une classe ne peut être
exécutée que si elle contient la méthode main() définie de la manière suivante :
public static void main(String[ ] args)

II.4. Conventions de nommage en Java


Traditionnellement, les identificateurs Java respectent les règles suivantes :
 les noms de classes commencent par une majuscule
 les noms de packages, de variables et de méthodes commencent par une minuscule

Cours Programmation Orientée Objet 13/58


ISET-Sousse

 les noms de constantes sont en majuscules, et si le nom de la constante est formé de


plusieurs mots, les différents mots sont séparés du caractère _
 dans les identificateurs formés à partir de plusieurs mots, les différents mots
(éventuellement à l'exception du premier en vertu d'une règle précédente) commencent
par une majuscule (par exemple ArbreBinaire ou StackOverFlowError pour des
classes, toString ou isEmpty pour des méthodes).

II.5. Contraintes de programmation


Si ce qui précède est pure affaire de tradition et de convention (qu'il est néanmoins conseillé de
respecter), le langage impose par ailleurs un certain nombre de contraintes :

 on ne peut écrire du code qu'à l'intérieur d'une classe ;


 le code d'une classe publique doit nécessairement être écrit dans un fichier d'extension
.java portant exactement le même nom que la classe (même casse minuscule/majuscule
des lettres (le «bytecode» se trouvera dans un fichier de même nom avec l'extension
.class).
Ainsi, la définition du source de la classe de nom maClasse sera dans un fichier de nom
maClasse.java et le compilateur java produira le «bytecode» correspondant dans un
fichier de nom maClasse.class ;
 Les arguments récupérés dans la fonction dans le tableau de chaînes de référence donnée
en argument (ici args) seront les arguments de la ligne de commande (les indices dans le
tableau commençant à 0).
 Afin de pouvoir donner lieu à une exécution, une classe doit contenir, comme nous
l'avons vu dans l'exemple du calcul de pi, une méthode définie de la manière suivante :
public static void main(String[ ] args)

Les arguments récupérés dans la fonction dans le tableau de chaînes de référence donnée
en argument (ici args) seront les arguments de la ligne de commande (les indices dans le
tableau commençant à 0), ainsi :
C:\> type Main.java
public class Main{
public static void main(String[ ] args){
for(int i = 0; i < args.length; i++)
System.out.println(args[i] + " ");
}
}
--> java Main aaa bbb ccc
aaa
bbb
ccc
C:\>

Cours Programmation Orientée Objet 14/58


ISET-Sousse

III. Installation et configuration de la JVM


Nous avons vu précédemment les différentes étapes nécessaires à la réalisation d’un programme
Java : écriture, compilation, et exécution. La compilation et l’exécution nécessite l’installation
d’un JDK et d’une JVM (pouvant être inclue dans le JDK). Afin de pouvoir lancer les
commandes javac et java, il peut être nécessaire d’inclure le chemin de ces commandes dans
la variable d’environ-nement PATH. De plus, si notre programme fait appel à des classes non
inclues dans le système, il sera également nécessaire de mettre à jour la variable d’environnement
CLASSPATH qui recense les emplacements des différentes classes.

Le langage JAVA

1. Introduction
Nous traitons Java d'abord comme un langage de programmation classique. Nous aborderons les
objets ultérieurement.
Dans un programme on trouve deux choses
 des données
 les instructions qui les manipulent

On s'efforce généralement de séparer les données des instructions :

Données

Instructions

2. Les données de Java


Java utilise les types de données suivants :
 les nombres entiers
 les nombres réels
 les caractères et chaînes de caractères
 les booléens
 les objets

2.1. Les types de données prédéfinies

Type Codage Domaine

Cours Programmation Orientée Objet 15/58


ISET-Sousse

char 2 octets caractère Unicode


int 4 octets [-231, 231-1]
long 8 octets [-263, 263 -1]
byte 1 octet [-27 , 27 -1]
short 2 octets [-215, 215-1]
float 4 octets [3.4 10-38, 3.4 10+38] en valeur
absolue
double 8 octets [1.7 10-308 , 1.7 10+308] en valeur
absolue
boolean 1 bit true, false
String référence d'objet chaîne de caractères
Date référence d'objet date
Character référence d'objet char
Integer référence d'objet int
Long référence d'objet long
Byte référence d'objet byte
Float référence d'objet float
Double référence d'objet double
Boolean référence d'objet boolean

2.2. Notation des données littérales


entier 145, -7, 0xFF (hexadécimal)
réel double 134.789, -45E-18 (-45 10-18)
réel float 134.789F, -45E-18F (-45 10-18)
caractère 'A', 'b'
chaîne de caractères "aujourd'hui"
booléen true, false
date new Date(13,10,1954) (jour, mois, an)

2.3. Déclaration des données


2.3.1. Rôle des déclarations
Un programme manipule des données caractérisées par un nom et un type. Ces données sont
stockées en mémoire. Au moment de la traduction du programme, le compilateur affecte à
chaque donnée un emplacement en mémoire caractérisé par une adresse et une taille. Il le fait en
s'aidant des déclarations faites par le programmeur.
Par ailleurs celles-ci permettent au compilateur de détecter des erreurs de programmation. Ainsi
l'opération {x=x*2;} sera déclarée erronée si x est une chaîne de caractères par exemple.

2.3.2. Déclaration des constantes


La syntaxe de déclaration d'une constante est la suivante :
final type nom=valeur; //définit constante nom=valeur
ex : final float PI=3.141592F;

Remarque. Pourquoi déclarer des constantes ?

Cours Programmation Orientée Objet 16/58


ISET-Sousse

1. La lecture du programme sera plus aisée si l'on a donné à la constante un nom significatif :
ex : final float taux_tva=0.18;
2. La modification du programme sera plus aisée si la "constante" vient à changer. Ainsi dans
le cas précédent, si le taux de tva passe à 33%, la seule modification à faire sera de modifier
l'instruction définissant sa valeur :
final float taux_tva=0.33;
Si l'on avait utilisé 0.18 explicitement dans le programme, ce serait alors de nombreuses
instructions qu'il faudrait modifier.

2.3.3. Déclaration des variables


Une variable est identifiée par un nom et se rapporte à un type de données. Le nom d'une variable
Java a n caractères, le premier alphabétique, les autres alphabétiques ou numériques. Java fait la
différence entre majuscules et minuscules. Ainsi les variables FIN et fin sont différentes.
Les variables peuvent être initialisées lors de leur déclaration. La syntaxe de déclaration d'une ou
plusieurs variables est :
identificateur_de_type variable1,variable2,...,variablen;
où identificateur_de_type est un type prédéfini ou bien un type objet défini par le programmeur.

2.4. Les conversions entre nombres et chaînes de caractères


nombre -> chaîne "" + nombre
chaine -> int Integer.parseInt(chaine)
chaîne -> long Long.parseLong(chaine)
chaîne -> double Double.valueOf(chaine).doubleValue()
chaîne -> float Float.valueOf(chaine).floatValue()
Le programme suivant, présente les principales techniques de conversion entre nombres et
chaînes de caractères. La conversion d'une chaîne vers un nombre peut échouer si la chaîne ne
représente pas un nombre valide.

public class conv1{


public static void main(String arg[]){
String S;
final int i=10;
final long l=100000;
final float f=(float)45.78;
double d=-14.98;

// nombre --> chaîne


S=""+i;
affiche(S);
S=""+l;
affiche(S);

Cours Programmation Orientée Objet 17/58


ISET-Sousse

S=""+f;
affiche(S);
S=""+d;
affiche(S);

//boolean --> chaîne


final boolean b=false;
S=""+new Boolean(b);
affiche(S);

// chaîne --> int


int i1;
i1=Integer.parseInt("10");
affiche(""+i1);
try{
i1=Integer.parseInt("10.67");
affiche(""+i1);
} catch (Exception e){
affiche("Erreur "+e);
}

// chaîne --> long


long l1;
l1=Long.parseLong("100");
affiche(""+l1);
try{
l1=Long.parseLong("10.675");
affiche(""+l1);
} catch (Exception e){
affiche("Erreur "+e);
}

// chaîne --> double


double d1;
d1=Double.valueOf("100.87").doubleValue();
affiche(""+d1);
try{
d1=Double.valueOf("abcd").doubleValue();
affiche(""+d1);
} catch (Exception e){
affiche("Erreur "+e);
}

// chaîne --> float


float f1;
f1=Float.valueOf("100.87").floatValue();
affiche(""+f1);

Cours Programmation Orientée Objet 18/58


ISET-Sousse

try{
d1=Float.valueOf("abcd").floatValue();
affiche(""+f1);
} catch (Exception e){
affiche("Erreur "+e);
}

} // fin main
} // fin classe

2.5. Les expressions


On s'intéresse ici à l'opération variable=expression; L'expression peut être de type : arithmétique,
relationnelle, booléenne, caractères
2.5.1. Les opérateurs
+
-
*
/ // int / int -> int
% // modulo
== // ATTENTION syntaxe du test d'égalité identique à C !!!!
!= / différent
< <= > >=
&& // ET logique
|| / OU logique
! // NON logique
^ // XOR bit à bit
~ // NOT bit à bit
<< // décalage à gauche bit à bit
>> // décalage à droite bit à bit
>>> // décalage à droite avec remplissage avec des 0 bit à bit
& // ET bit à bit
|/ / OU bit à bit
new création d'une nouvelle instance de classe

2.5.2. Interprétation de l'opération d'affectation


L'opération variable=expression; est elle-même une expression dont l'évaluation se déroule de la
façon suivante :
 la partie droite de l'affectation est évaluée : le résultat est une valeur V.
 la valeur V est affectée à la variable
 la valeur V est aussi la valeur de l'affectation vue cette fois en tant qu'expression.

Cours Programmation Orientée Objet 19/58


ISET-Sousse

C'est ainsi que l'opération V1=V2=expression est légale. A cause de la priorité, c'est l'opérateur =
le plus à droite qui va être évalué.
On a donc V1=(V2=expression). L'expression V2=expression est évaluée et a pour valeur V.
L'évaluation de cette expression a provoqué l'affectation de V à V2. L'opérateur = suivant est
alors évalué sous la forme V1=V. La valeur de cette expression est encore V. Son évaluation
provoque l'affectation de V à V1. Ainsi donc, l'opération V1=V2=expression est une expression
dont l'évaluation provoque l'affectation de la valeur de expression aux variables V1 et V2 ; et
rend comme résultat la valeur de expression.
On peut généraliser à une expression du type : V1=V2=....=Vn=expression

2.5.3 Expression arithmétique


Les opérateurs des expressions arithmétiques sont les suivants :
+ addition
- soustraction
* multiplication
/ division : le résultat est le quotient exact si l'un au moins des opérandes est réel. Si
es deux opérandes sont entiers le résultat est le quotient entier. Ainsi 5/2 -> 2 et
5.0/2 ->2.5.
% Division : le résultat est le reste quelque soit la nature des opérandes, le quotient
étant lui entier. C'est donc l'opération modulo.
Il existe diverses fonctions mathématiques :
double sqrt(double x) racine carrée
double cos(double x) Cosinus
double sin(double x) Sinus
double tan(double x) Tangente
double pow(double x,double y) x à la puissance y (x>0)
double exp(double x) Exponentielle
double log(double x) Logarithme népérien
double abs(double x) valeur absolue

Priorités dans l'évaluation des expressions arithmétiques


La priorité des opérateurs lors de l'évaluation d'une expression arithmétique est la suivante (du
plus prioritaire au moins prioritaire) :
[fonctions], [ ( )],[ *, /, %], [+, -]
Les opérateurs d'un même bloc [ ] ont même priorité.
2.5.4 Expressions relationnelles
Les opérateurs sont les suivants : <, <=, ==, !=, >, >=

Cours Programmation Orientée Objet 20/58


ISET-Sousse

ordre de priorité
>, >=, <, <=
==, !=
Le résultat d'une expression relationnelle est le booléen false si expression est fausse true sinon.
Exemple :
boolean fin;
int x;
fin=x>4;

Comparaison de deux caractères


Soient deux caractères C1 et C2. Il est possible de les comparer avec les opérateurs
<, <=, ==, !=, >, >=
Ce sont alors leurs codes ASCII, qui sont des nombres, qui sont alors comparés. On rappelle que
selon l'ordre ASCII on a les relations suivantes :
espace < .. < '0' < '1' < .. < '9' < .. < 'A' < 'B' < .. < 'Z' < .. < 'a' < 'b' < .. <'z'
Comparaison de deux chaînes de caractères
Elles sont comparées caractère par caractère. La première inégalité rencontrée entre deux
caractères induit une inégalité de même sens sur les chaînes.
Exemples :
Soit à comparer les chaînes "Chat" < "Chien"

2.5.5. Expressions booléennes


Les opérateurs sont && (and) ||(or) et ! (not). Le résultat d'une expression booléenne est un
booléen.
Ordre de priorité ! , &&, ||
Exemple :
int fin;
int x;
fin= x>2 && x<4;
Les opérateurs relationnels ont priorité sur les opérateurs && et ||.

2.5.6. L'opérateur ?
L'expression expr_cond ? expr1 : expr2 est évaluée de la façon suivante :
1. l'expression expr_cond est évaluée. C'est une expression conditionnelle à valeur vrai ou
faux
2. Si elle est vraie, la valeur de l'expression est celle de expr1. expr2 n'est pas évaluée.
3. Si elle est fausse, c'est l'inverse qui se produit : la valeur de l'expression est celle de expr2.
expr1 n'est pas évaluée.
Exemple.

Cours Programmation Orientée Objet 21/58


ISET-Sousse

i= (j>4 ? j+1 : j-1);


Affectera à la variable i : j+1 si j>4, j-1 sinon c'est la même chose que d'écrire if(j>4)
i=j+1; else i=j-1; mais c'est plus concis.

2.5.7. Les changements de type


Il est possible, dans une expression, de changer momentanément le codage d'une valeur. On
appelle cela changer le type d'une donnée ou en anglais type casting. La syntaxe du changement
du type d'une valeur dans une expression est la suivante (type) valeur.
La valeur prend alors le type indiqué. Cela entraîne un changement de codage de la valeur.

Exemple :
int i, j;
float isurj;
isurj= (float)i/j; // priorité de () sur /
Ici il est nécessaire de changer le type de i ou j en réel sinon la division donnera le quotient entier
et non réel.
i est une valeur codée de façon exacte sur 2 octets
(float) i est la même valeur codée de façon approchée en réel sur 4 octets
Il y a donc transcodage de la valeur de i. Ce transcodage n'a lieu que le temps d'un calcul, la
variable i conservant toujours son type int.

2.6 Les tableaux de données


Un tableau Java est un objet permettant de rassembler sous un même identificateur des données
de même type. Sa déclaration est la suivante :
Type Tableau[] ;
ou
Type[] Tableau ;
Les deux syntaxes sont légales.
Exemple de déclaration
int tab[] ;
int [] tab;
On a déclaré une variable qui stocke l’adresse d’un tableau d’entier, pour le moment cette
variable contient la valeur nulle (null).
Il faut ensuite allouer le tableau et affecter son adresse à la variable.
2.6.1 Allocation de tableaux
Type Tableau[]=new Type[n] ou Type[] Tableau=new Type[n]

Cours Programmation Orientée Objet 22/58


ISET-Sousse

L’entier n est le nombre de données que peut contenir le tableau. La syntaxe Tableau[i] désigne
la donnée n° i où i appartient à l'intervalle [0, n-1]. Toute référence à la donnée Tableau[i] où i
n'appartient pas à l'intervalle [0, n-1] provoquera une exception.
// tableau de 10 référence nulles vers 10 String (et non pas une chaîne de 10 caractères !)
String names [] = new String [10] ;
// tableau de 2 références vers des Points, créées et initialisées en même temps que le tableau.
Points hits[2] = new Point { { 10,20} , {11,21} } ;
// tableau de 3 références vers 3 String, créées et initialisées en même temps que le tableau
Tring riz [] = {« rond » , « long » , « parfumé » } ; // initialisé par les données
2.6.1 Accès à un élément
names.length  attribut du tableau, fournit le nombre d’éléments alloués pour le tableau
hits[0].x  fournit la valeur 10
hits[1].y  fournit la valeur 21

2.6.1 Tableaux multidimensionnels

Un tableau à deux dimensions pourra être déclaré comme suit :


Type Tableau[][]=new Type[n][p] ;
ou
Type[][] Tableau=new Type[n][p]
La syntaxe Tableau[i] désigne la donnée n° i de Tableau où i appartient à l'intervalle [0,n-1].
Tableau[i] est lui-même un tableau : Tableau[i][j] désigne la donnée n° j de Tableau[i] où j
appartient à l'intervalle [0,p-1]. Toute référence à une donnée de Tableau avec des index
incorrects génère une erreur fatale.
int coords [][] = new [4][4] ;

Exemple :
public class test1{
public static void main(String arg[]){
float[][] taux=new float[2][2];
taux[1][0]=0.24F;
taux[1][1]=0.33F;
System.out.println(taux[1].length);
System.out.println(taux[1][1]);
}
}
et les résultats de l'exécution :

Cours Programmation Orientée Objet 23/58


ISET-Sousse

2
0.33

Un tableau est un objet possédant l'attribut length : c'est la taille du tableau.

4. Les instructions de contrôle du déroulement du programme


4.1 Arrêt
La méthode exit définie dans la classe System permet d'arrêter l'exécution d'un programme.
syntaxe void exit(int status)

Cette méthode arrête le processus en cours et rend la valeur status au processus père, elle
provoque la fin du processus en cours et rend la main au processus appelant. La valeur de status
peut être utilisée par celui-ci. Sous DOS, cette variable status est rendue à DOS dans la variable
système ERRORLEVEL dont la valeur peut être testée dans un fichier batch. Sous Unix, c'est la
variable $? qui récupère la valeur de status si l'interpréteur de commandes est le Bourne Shell.

Exemple :
System.exit(0);
pour arrêter le programme avec une valeur d'état à 0.

4.2 Structure de choix simple


Syntaxe : if (condition) {actions_condition_vraie;} else {actions_condition_fausse;}
Notes.
 la condition est entourée de parenthèses.
 chaque action est terminée par point-virgule.
 les accolades ne sont pas terminées par point-virgule.
 les accolades ne sont nécessaires que s'il y a plus d'une action.
 la clause else peut être absente.
 Il n'y a pas de then.
L'équivalent algorithmique de cette structure est la structure si .. alors … sinon :
si condition
alors actions_condition_vraie
sinon actions_condition_fausse
finsi

Cours Programmation Orientée Objet 24/58


ISET-Sousse

Exemple
if (x>0) { nx=nx+1;sx=sx+x;} else dx=dx-x;

On peut imbriquer les structures de choix :


if(condition1)
if (condition2)
{......}
else //condition2
{......}
else //condition1
{.......}
Se pose parfois le problème suivant :
public static void main(void) {
int n=5;
if(n>1)
if(n>6)
System.out.println(">6");
else System.out.println("<=6");
}
Dans l'exemple précédent, le else se rapporte à quel if ? La règle est qu'un else se rapporte
toujours au if le plus proche : if(n>6) dans l'exemple. Considérons un autre exemple :
public static void main(void){
int n=0;
if(n>1)
if(n>6)
System.out.println(">6");
else; // else du if(n>6) : rien à faire
else
System.out.println("<=1"); // else du if(n>1)
}
Ici nous voulions mettre un else au if(n>1) et pas de else au if(n>6). A cause de la remarque
précédente, nous sommes obligés de mettre un else au if(n>6), dans lequel il n'y a aucune
instruction.
4.3 Structure conditionnelle à choix multiple
La syntaxe est la suivante :
switch (expression) {
case v1:
actions1;
break;
case v2:
actions2;
break;
default: actions_sinon;
}

Cours Programmation Orientée Objet 25/58


ISET-Sousse

Notes
 La valeur de l'expression de contrôle, ne peut être qu'un entier ou un caractère.
 l'expression de contrôle est entourée de parenthèses.
 la clause default peut être absente.
 les valeurs vi sont des valeurs possibles de l'expression. Si l'expression a pour valeur vi ,
les actions derrière la clause case vi sont exécutées.
 l'instruction break fait sortir de la structure de cas. Si elle est absente à la fin du bloc
d'instructions de la valeur vi, l'exécution se poursuit alors avec les instructions de la valeur
vi+1.

Exemple
En algorithmique
selon la valeur de choix
cas 0
arrêt
cas 1
exécuter module M1
cas 2
exécuter module M2
sinon
erreur<--vrai
findescas

En Java
int choix, erreur;
switch switch(choix){
case 0: System.exit(0);
case 1: M1();break break;
case 2: M2();break break;
default default: erreur=1;
}

4.4 Structure de répétition


4.4.1 Nombre de répétitions connu
Syntaxe
for (i=id;i<=if if;i=i+ip){
actions;
}

Notes
 les 3 arguments du for sont à l'intérieur d'une parenthèse.
 les 3 arguments du for sont séparés par des points-virgules.
 chaque action du for est terminée par un point-virgule.
 l'accolade n'est nécessaire que s'il y a plus d'une action.
 l'accolade n'est pas suivie de point-virgule.

Cours Programmation Orientée Objet 26/58


ISET-Sousse

L'équivalent algorithmique est la structure pour :


pour i variant de id à if avec un pas de ip
actions
finpour

qu'on peut traduire par une structure tantque :


i  id
tantque i<= if
actions
i  i+ip
fintantque

4.4.2 Nombre de répétitions inconnu


Il existe de nombreuses structures en Java pour ce cas.
Structure tantque (while)
while(condition) {
actions;
}

On boucle tant que la condition est vérifiée. La boucle peut ne jamais être exécutée.

Notes.
 la condition est entourée de parenthèses.
 chaque action est terminée par point-virgule.
 l'accolade n'est nécessaire que s'il y a plus d'une action.
 l'accolade n'est pas suivie de point-virgule.

La structure algorithmique correspondante est la structure tantque :


tantque condition
actions
fintantque

Structure répéter jusqu'à (do while)


La syntaxe est la suivante :

do{
instructions;
}while (condition);

On boucle jusqu'à ce que la condition devienne fausse ou tant que la condition est vraie. Ici la
boucle est faite au moins une fois.
Note.
 la condition est entourée de parenthèses.

Cours Programmation Orientée Objet 27/58


ISET-Sousse

 chaque action est terminée par point-virgule.


 l'accolade n'est nécessaire que s'il y a plus d'une action.
 l'accolade n'est pas suivie de point-virgule.
La structure algorithmique correspondante est la structure répéter … jusqu'à :
répéter
actions
jusqu'à condition

Structure pour générale (for)


La syntaxe est la suivante :
for (instructions_départ ; condition ; instructions_fin_boucle){
instructions;
}

On boucle tant que la condition est vraie (évaluée avant chaque tour de boucle).
Instructions_départ sont effectuées avant d'entrer dans la boucle pour la première fois.
Instructions_fin_boucle sont exécutées après chaque tour de boucle.
Notes.
 les 3 arguments du for sont à l'intérieur de parenthèses.
 les 3 arguments du for sont séparés par des points-virgules.
 chaque action du for est terminée par un point-virgule.
 l'accolade n'est nécessaire que s'il y a plus d'une action.
 l'accolade n'est pas suivie de point-virgule.
 les différentes instructions dans instructions_depart et instructions_fin_boucle sont
séparées par des virgules.
La structure algorithmique correspondante est la suivante :
instructions_départ
tantque condition
actions
instructions_fin_boucle
fintantque

Exemples
Les programmes suivants calculent tous la somme des n premiers nombres entiers.
1. for (i=1, somme=0 ; i<=n ; i=i+1)
somme = somme + a[i];
2. for (i=1, somme=0;i<=n;somme=somme+a[i], i=i+1);
3. i=1; somme=0;
while (i<=n)
{somme+=i; i++;}
4. i=1; somme=0;
do somme+=i++;
while (i<=n);

Cours Programmation Orientée Objet 28/58


ISET-Sousse

Instructions de gestion de boucle


break fait sortir de la boucle for, while, do ... while.
continue fait passer à l'itération suivante des boucles for, while, do ... while

5. La structure d'un programme Java


Un programme Java n'utilisant pas de classe définie par l'utilisateur ni de fonctions autres que la
fonction principale main qui pourra avoir la structure suivante :

public class test1{


public static void main(String arg[]){
… code du programme
}// main
} // class
La fonction main, appelée aussi méthode est exécutée la première lors de l'exécution d'un
programme Java. Elle doit avoir obligatoirement la signature précédente :

public static void main(String arg[]){


ou
public static void main(String[] arg){

Le nom de l'argument arg peut être quelconque. C'est un tableau de chaînes de caractères
représentant les arguments de la ligne de commande. Nous y reviendrons un peu plus loin.
Au début du code source et avant la définition de la classe, il est usuel de trouver des instructions
d'importation de classes. Par exemple :

import java.io.*;
public class test1{
public static void main(String arg[]){
… code du programme
}// main
}

6 Arguments du programme principal


La fonction principale main admet comme paramètres un tableau de chaînes : String[]. Ce
tableau contient les arguments de la ligne de commande utilisée pour lancer l'application. Ainsi si
on lance le programme P avec la commande :
java P arg0 arg1 … argn

si la fonction main est déclarée comme suit :


public static void main(String[] arg);

Cours Programmation Orientée Objet 29/58


ISET-Sousse

on aura arg[0]="arg0", arg[1]="arg1" … Voici un exemple :

import java.io.*;
public class param1{
public static void main(String[] arg){
int i;
System.out.println("Nombre d'arguments="+arg.length);
for (i=0;i<arg.length;i++)
System.out.println("arg["+i+"]="+arg[i]);
}
}

Les résultats obtenus sont les suivants :


dos> java param1 a b c
Nombre d'arguments=3
arg[0]=a
arg[1]=b
arg[2]=c

7. Les instructions élémentaires de Java


On distingue
1. les instructions élémentaires exécutées par l'ordinateur.
2. les instructions de contrôle du déroulement du programme.

Les instructions élémentaires apparaissent clairement lorsqu'on considère la structure d'un micro-
ordinateur et de ses périphériques.

Ecran
UC RAM

Disque
Clavier

1. lecture d'informations provenant du clavier


2. traitement d'informations
3. écriture d'informations à l'écran

Cours Programmation Orientée Objet 30/58


ISET-Sousse

4. lecture d'informations provenant d'un fichier disque


5. écriture d'informations dans un fichier disque

7.1 Ecriture sur écran


La syntaxe de l'instruction d'écriture sur l'écran est la suivante :
System.out.println(expression)
ou
System.err.println(expression)

Où expression est tout type de donnée qui puisse être converti en chaîne de caractères pour être
affiché à l'écran. Dans l'exemple précédent, nous avons vu deux instructions d'écriture :
System.out.println(taux[1].length);
System.out.println(taux[1][1]);
System.out écrit dans un fichier texte qui est par défaut l'écran. Il en est de même pour
System.err.

Ces fichiers portent un numéro (ou descripteur) respectivement 1 et 2. Le flux d'entrée du clavier
(System.in) est également considéré comme un fichier texte, de descripteur 0.

Dos comme Unix supportent le tubage (pipe) de commandes :


commande1 | commande2
Tout ce que commande1 écrit avec System.out est tubé (redirigé) vers l'entrée System.in de
commande2. Dit autrement, commande2 lit avec System.in, les données produites par commande2
avec System.out qui ne sont donc plus affichées à l'écran. Ce système est très utilisé sous Unix.
Dans ce tubage, le flux System.err n'est lui pas redirigé : il écrit sur l'écran. C'est pourquoi il est
utilisé pour écrire les messages d'erreurs (d'où son nom err) : on est assuré que lors d'un tubage
de commandes, les messages d'erreur continueront à s'afficher à l'écran. On prendra donc
l'habitude d'écrire les messages d'erreur à l'écran avec le flux System.err plutôt qu'avec le flux
System.out.

7.2 Lecture de données tapées au clavier


Le flux de données provenant du clavier est désigné par l'objet System.in de type InputStream. Ce
type d'objets permet de lire des données caractère par caractère. C'est au programmeur de
retrouver ensuite dans ce flux de caractères les informations qui l'intéressent. Le type
InputStream ne permet pas de lire d'un seul coup une ligne de texte. Le type BufferedReader le
permet avec la méthode readLine.
Afin de pouvoir lire des lignes de texte tapées au clavier, on crée à partir du flux d'entrée
System.in de type InputStream, un autre flux d'entrée de type BufferedReader cette fois :
BufferedReader IN=new BufferedReader(new InputStreamReader(System.in));
Nous n'expliquerons pas ici les détails de cette instruction qui fait intervenir la notion de
constructions d'objets. Nous l'utiliserons telle-quelle.
La construction d'un flux peut échouer : une erreur fatale, appelée exception en Java, est alors
générée. A chaque fois qu'une méthode est susceptible de générer une exception, le compilateur

Cours Programmation Orientée Objet 31/58


ISET-Sousse

Java exige qu'elle soit gérée par le programmeur. Aussi, pour créer le flux d'entrée précédent, il
faudra en réalité écrire :
BufferedReader IN = null;
try {
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
System.err.println("Erreur " +e);
System.exit(1);
}

De nouveau, on ne cherchera pas à expliquer ici la gestion des exceptions. Une fois le flux IN
précédent construit, on peut lire une ligne de texte par l'instruction :
String ligne;
ligne=IN.readLine();
La ligne tapée au clavier est rangée dans la variable ligne et peut ensuite être exploitée par le
programme.

Héritage

Problématique
Une application a besoin de services dont une partie seulement est proposée par une classe déjà
définie (classe dont on ne possède pas nécessairement le code source), on veut utiliser ce code
sans le réécrire, c’est à dire définir une nouvelle classe à partir de la classe existante.

1. Définitions
L’héritage est l’un des concepts fondamentaux de la programmation par objets. Il
contribue à la modularité et la réutilisabilité. C’est un mécanisme qui permet à une entité
d’hériter des attributs et des comportements d’une autre entité.

L'héritage est une notion-clé de la programmation objet. Le mécanisme d'héritage consiste à créer
une nouvelle classe à partir d'une classe qui existe déjà en la complétant pour qu'elle permette de
créer des objets plus spécifiques. Si la classe B hérite (ou dérive) de A alors on dira que A est la
superclasse ou la classe-mère de B, tandis que B est la sous-classe ou la classe-fille ou dérivée de A

Quand B dérive de A, B possède implicitement tous les attributs et toutes les méthodes de A.
Dans la déclaration de B, on ajoute des attributs et des méthodes supplémentaires ou de
remplacements. Faire dériver une classe B à partir d'une classe A n'a de sens que si toutes les
instances possibles de B forment un sous-ensemble de l'ensemble de toutes les instances
possibles de A.

Cours Programmation Orientée Objet 32/58


ISET-Sousse

Par exemple un étudiant est une personne. Nous pouvons donc définir l’entité Etudiant en
réutilisant l’entité Personne. Il suffit simplement de compléter, d’une part les attributs de
l’entité personne par une filière d’inscription, une année dans cette filière et une moyenne,
d’autre part, les comportements de la personne pour enregistrer une moyenne, mais aussi
compléter les comportements saisir, afficher …

Schématiquement un héritage se décrit comme sur la figure 1. Sur cette figure, il n’existe pas de
comportement de comparaison propre à l’entité Etudiant. Pour comparer l’étudiant à un autre
étudiant, nous utiliserons le comportement comparerA de l’entité Personne. Etant donné que
l’étudiant hérite de l’entité Personne, il hérite de tout son comportement et donc de la
comparaison sans qu’il soit besoin de la redéfinir dans l’entité Etudiant. Ainsi, en langage java,
l’instruction
E.comparerA(NOM_PRENOM)
fait appel au comportement de E défini dans la classe Personne, pour comparer
alphabétiquement les identités des deux étudiants. ‘NOM_PRENOM’
En langage Java, pour dire qu’une classe hérite d’une autre classe, on utilise le mot réservé
« extends »

public class Etudiant extends Personne

Personne
Attributs
nom
prénom
age
adresse
Méthodes
saisir
afficher
comparerA

Etudiant
Attributs
filière
année
moyenne
Méthodes
saisir
afficher
setMoyenne
getMoyenne

Figure 4 : l'entité Etudiant hérite de l'entité Personne

En langage Java, toutes les classes héritent d’une autre classe qui, par défaut est une classe
prédéfinie nommée ‘Object’. Cette classe prédéfinie n’hérite d’aucune autre classe ; elle est en

Cours Programmation Orientée Objet 33/58


ISET-Sousse

haut de la hiérarchie des classes. L’héritage de la classe Object n’a pas besoin d’être spécifié, il
est automatique.
La classe Etudiant est une sous classe de la classe Personne. La classe Personne est la classe
mère ou super classe de la classe Etudiant. La classe Personne est une sous classe de la classe
Object.
Exemple :
public classe Etudiant extends Personne {
public String filiere;
public String annee;
private float moyenne = -1;

public boolean setMoyenne(float moyenne) {


// description de la méthode
}

// description des autres méthodes

} // fin de la classe

2. Utilisation des instances d’une classe héritée


Un objet instance de Etudiant possède les attributs définis dans Etudiant ainsi que les attributs
définis dans Personne (un Etudiant est aussi un Personne)
Un objet instance de Etudiant répond aux messages définis par les méthodes décrites dans la
classe Etudiant et aussi à ceux définis par les méthodes de la classe Personne
Nous voyons sur l’exemple précédant que les nom, prénom age et adresse n’ont pas été
redéfinis. Seuls les attributs spécifiques à l’étudiant ont été définis. Etant donné qu’un Etudiant
EST une personne, il a forcément un nom, un prénom un age et une adresse. Ainsi, si nous
déclarons :
Etudiant E = new Etudiant() ;
alors E.nom existe, de même que E.prenom, E.age, E.adresse, E. filiere, E.annee et
E.moyenne. Un étudiant a, ici, sept caractéristiques, les quatre d’une personne, plus les trois
particularités d’un étudiant. Seules les trois particularités d’un étudiant sont à définir dans la
classe Etudiant, les quatre autres caractéristiques sont réutilisées de la classe Personne.

3. La hiérarchie de classe : Généralisation et Spécification :


3.1. Spécialisation
La hiérarchie de classes permet de décrire des classes plus spécialisées à partir de classes plus
abstraites.
Véhicule

Véhicule-Terrerstre Véhicule-Marin

Cours Programmation Orientée Objet 34/58


ISET-Sousse

Dans l’exemple ci-dessus, un Véhicule_Terrestre EST un Véhicule. De même un


Véhicule_Marin EST un Véhicule.
La classe Véhicule contient tous les traits communs à tous les véhicules qui en sont dérivés.
Note : les noms sont singuliers, car une classe ne contient pas tous les objets, mais est une
structure de description d’un objet quelconque appartenant à la classe

De la classe des véhicules, décrire la classe Véhicule_Marin en dehors de la hiérarchie, sachant


qu’un véhicule marin flotte et dispose d’un tonnage.

3.2. Généralisation
De même qu’il est possible de spécialiser des classes à partir d’autres classes, il est possible de
généraliser une classe à partir d’un ensemble de classes.
La généralisation est l’opération inverse de la spécialisation. Elle consiste à créer une classe qui
contient les traits communs à plusieurs classes.
Homme Singe
Exemple : Généraliser les deux classes
age poids
suivantes à l’aide d’une classe Primate: poids couleurPoils
numeroSS
couleurDesYeux
sauter( )
courrir( ) crier( )
sauter( ) aimer( )
se-mettre-en-colère( ) se-mettre-en-colère( )
aimer( )
écrire( )

Solution : c’est la classe ancêtre


Primate
poids
sauter()
se-lettre-en-colère()

4. La construction
Du point de vue sémantique, par rapport à la mémoire centrale, qu’est il sensé se passer quand
une classe A hérite d’une classe B et qu’un objet de la classe A est créé ?
Rappelons qu’en java, tout objet hérite d’une classe sauf la classe Object, et que ce sont les
instructions ‘new <Constructeur> ’ qui sont chargées de créer les instances des classes. Pour
les instances de la classe Object la sémantique opérationnelle de l’instruction new vue au
chapitre précédent reste valable mais pas pour les autres classes. La généralisation suivante
permet de prendre en compte la notion d’héritage.
Nous pouvons considérer que l’instruction ‘new <Constructeur> ’ de la classe A est chargée
de :
 Créer une instance de la classe
 Exécuter l’algorithme <Constructeur> de la classe A
 Retourner un pointeur (adresse, type de l’objet) pointant vers l’espace réservé pour
cet objet.

Créer une instance d’une classe A qui hérite d’une classe B consiste en :

Cours Programmation Orientée Objet 35/58


ISET-Sousse

 Créer une instance de la super-classe B. (cette action est omise dans le cas où A est la
classe Object).
 A l’intérieur de l’espace de l’objet qui vient d’être créé, réserver l’espace nécessaire pour
un objet de type A.
 A l’intérieur de ce nouvel espace, réserver la place nécessaire à chaque attribut de l’objet
de type A. Ces espaces contiendront des valeurs inconnues sauf s’ils sont explicitement
initialisés, auquel cas, ils contiendront les valeurs d’initialisation.

4.1 Chaînage de constructeurs hiérarchique.


Chaque fois qu'un objet est créé les constructeurs sont invoqués en remontant en séquence de
classe en classe dans la hiérarchie jusqu'à la classe Object, c'est le corps du constructeur de la
classe Object qui est toujours exécuté en premier, suivi du corps des constructeurs des différentes
classes en redescendant dans la hiérarchie.
Si A hérite de B qui hérite de C, alors pour créer un objet de type A, il faudra créer un objet de
type B, mais pour créer un objet de type B il faut créer un objet de type C. En fin de compte,
l’espace de l’objet de type A se trouvera à l’intérieur de l’espace de l’objet de type B qui lui
même se trouvera à l’intérieur de l’espace de l’objet de type C. On peut remarquer que c’est
l’objet du haut de la hiérarchie de classes qui est créé en premier. Ici, pour créer un objet de type
A, une instance de la classe C est d’abord créée. Une instance de la classe B est ensuite créée à
l’intérieur de l’objet de type C précédemment créé. Enfin, un objet de type A est créé à l’intérieur
de l’instance de la classe B.
L'appel à un constructeur de la super classe doit toujours être la première instruction dans le
corps du constructeur, si la première instruction d'un constructeur n'est pas un appel explicite à
l’un des constructeur de la superclasse, alors JAVA insère implicitement l'appel super()
4.2 Constructeur par défaut
Lorsqu'une classe ne définit pas explicitement de constructeur, elle possède un constructeur par
défaut : de corps vide, sans paramètres et qui devient inexistant si un autre constructeur existe.
Constructeur par défaut garantit chaînage des constructeurs

public class Object {


public Object()
{
...
}
...
}

public A() {
super();
public class A extends Object { }
// attributs
String nom; Constructeur par défaut implicite
// méthodes
String getNom() {
return nom;
}
...
}
Cours Programmation Orientée Objet 36/58
ISET-Sousse

Remarque.
Si on le constructeur par défaut est masqué dans une classe mère, il faut définir
explicitement le constructeur par défaut aux classe dérivées

public class ClasseA { Constructeur explicite masque


double x; constructeur par défaut Pas de
// constructeur constructeur sans paramètres
public ClasseA(double x)
{
this.x = x;
}
}
public ClasseB(){
super();
}
public class ClasseB extends ClasseA {
double y = 0; Constructeur par défaut implicite
// pas de constructeur
}

Lors de la compilation de la classe B, le compilateur génère un message d’erreur


C:>javac ClasseB.java
ClasseB.java:3: No constructor matching ClasseA() found in class ClasseA.
public ClasseB() {
^
1 error
Compilation exited abnormally with code 1 at Fri Dec 08 13:38:24

Pour construire une instance d’une classe A donnée, il faut construire auparavant une instance de
la classe mère de A. Appelons B la classe mère de A. Par suite, l’instance de la classe mère doit
être initialisée avant l’instance de la classe A qu’elle contient. Ainsi, au moment de l’initialisation
de l’objet de type A, les attributs de l’objet de type B dont il hérite sont initialisés et peuvent être
utilisés pour les attributs spécifiques à l’objet de type A. Pour réaliser cela, il existe quelques
conventions résumées ci-dessous :

Convention 1
Si une classe A ne possède pas de constructeur explicitement défini, elle est affectée d’un
constructeur vide par défaut :

Cours Programmation Orientée Objet 37/58


ISET-Sousse

public A() {}
Convention 2
Toute classe qui possède au moins un constructeur explicitement défini, ne possède pas de
constructeur par défaut.
Convention 3
Excepté pour la classe Object, la première instruction d’un constructeur est l’appel d’un
constructeur de la classe mère ou d’un autre constructeur de la classe courante. Si cette
instruction ne figure pas explicitement dans le constructeur, un appel au constructeur sans
argument de la classe mère est ajouté automatiquement par le compilateur.

Ces conventions assurent que pour chaque objet emboîté, un constructeur est exécuté.

4.3. Exemple
Reprenons notre exemple de la classe Etudiant. L’instruction déclaration suivante
Etudiant E = new Etudiant() ;
après avoir créé le doublet E, va lancer l’exécution ‘new Etudiant()’. L’instruction new va
donc créer une instance de la classe Object, puis, à l’intérieur de celle-ci une instance de la
classe Personne, et à l’intérieur de cette dernière, une instance de la classe Etudiant. Cette
situation est illustrée figure 2.

Figure 5 : création d'une instance de la classe Etudiant

Un étudiant est une personne particulière. Il s’agit donc d’une personne ayant des attributs et des
comportements supplémentaires. C’est ce qui explique que les attributs supplémentaires et donc

Cours Programmation Orientée Objet 38/58


ISET-Sousse

toutes les particularités de l’Etudiant soient à l’intérieur de la Personne. On remarquera que, dans
notre définition de la classe Etudiant, le champ moyenne des attributs spécifiques à l’étudiant est
initialisé. Ceci explique que sur le schéma 2 ce champ porte une valeur spécifique, donnée par le
programmeur alors que les autres champs ont une valeur par défaut, attribuée par Java à la
création de l’objet.
Dans la mesure où nous n’avons pas défini de constructeur pour la classe Etudiant, elle possède
un constructeur par défaut :
public Etudiant() {} ; (cf. convention 1 ci-dessus).
Ce constructeur par défaut doit appeler en première instruction un constructeur. D’après la
convention 3, il s’appellera Personne(). Ce constructeur que nous avons déjà défini
précédemment ne fait rien hormis appeler le constructeur de la classe Object.
Supposons que dans la classe Etudiant nous ayons le constructeur :
public Etudiant(String nom, String prenom, int age,
String filiere, String annee) {
Personne(nom, prenom, age);
this.filiere = filiere ;
this.annee = annee ;
}

après l’exécution de l’instruction :


P = new Etudiant(«DUPONT»,«Jacques»,20,«Informatique»,«Licence»);
Nous obtiendrons la configuration de la figure 3

Figure 6 : création d'une instance de la classe Etudiant (version 2)

Cours Programmation Orientée Objet 39/58


ISET-Sousse

La convention 3 nous permet, au lieu d’appeler un constructeur de la classe mère, d’appeler un


autre constructeur de sa propre classe. Ceci permet de compléter certains constructeur sans être
obligé de tout reprendre. Par exemple, nous pourrions définir, respectivement dans les classes
Personne et Etudiant, les constructeurs :

public Personne (String nom, String prenom, int age, String adresse) {
Personne(nom, prenom, age);
this.adresse = adresse ;
}

public Etudiant(String nom, String prenom, int age, String adresse


String filiere, String annee) {
Personne(nom, prenom, age, adresse);
this.filiere = filiere ;
this.annee = annee ;
}

alors après l’exécution de l’instruction


P = new Etudiant(«DUPONT»,«Jacques»,20,«Paris»,«Informatique»,«Licence»);
nous obtiendrons la configuration de la figure 4 , où cette fois ci, l’adresse est initialisée.

Figure 7 : création d'une instance de la classe Etudiant (version 3)

Cours Programmation Orientée Objet 40/58


ISET-Sousse

Pour les appels d’un constructeur dans un autre constructeur, il est possible d’utiliser les mots clé
this et super à la place des noms explicites de constructeurs. Ce qui donne, dans les deux cas
précédents les écritures équivalentes :

Public Personne (String nom, String prenom, int age, String adresse) {
this(nom, prenom, age);
this.adresse = adresse ;
}

public Etudiant(String nom, String prenom, int age, String adresse


String filiere, String annee) {
super(nom, prenom, age, adresse);
this.filiere = filiere ;
this.annee = annee ;
}

En ce qui concerne la visibilité, nous avons les mêmes règles que pour les espaces emboîtés. Ceci
est valable tant pour les attributs que pour les méthodes, comme nous l’avons vu dans la section
2.5 du chapitre précédent. Toutefois l’appel d’une méthode dans une autre méthode peut poser
d’autres problèmes que nous étudierons dans la section 7 sur la redéfinition. Ainsi, si E est un
étudiant, l’appel à la méthode setMoyenne par E.setMoyenne créera un espace pour
l’algorithme correspondant à setMoyenne, à l’intérieur de l’espace de l’étudiant E cf. figure 5.

Figure 8 : appel de la méthode setMoyenne, d'un objet de type Etudiant

Cours Programmation Orientée Objet 41/58


ISET-Sousse

Les règles de visibilité font que de l’intérieur de l’algorithme setMoyenne, l’attribut moyenne
de l’étudiant E n’est pas vu. D’ou la nécessité de le désigner explicitement comme attribut de
l’objet par this.moyenne, ce qui est fait dans l’algorithme donné page Erreur ! Signet non
défini.. En revanche il n’y a pas de variable d’entrée ni intermédiaire dans l’algorithme
getMoyenne défini page Erreur ! Signet non défini., si bien que this n’est pas nécessaire.
C’est donc la valeur de l’attribut moyenne de E qui sera prise comme valeur de l’expression à
rendre.

Si maintenant, nous voulons comparer l’Etudiant E à un autre etudiant F, la méthode


comparerA est définie dans la classe Personne, mais pas dans la classe Etudiant. Par
conséquent, E hérite de ce comportement, et l’appel du comportement de comparaison
E.comparerA(F, NOM_PRENOM) créera un espace pour l’algorithme correspondant à
comparerA, à l’intérieur de l’espace de l’étudiant E considéré comme une Personne et non
pas comme un Etudiant cf. figure 6.

Figure 9 : appel de la méthode comparerA, d'un objet de type Etudiant

5. Transtypage

Cours Programmation Orientée Objet 42/58


ISET-Sousse

exemple :

class Mammifère { ... }

class Chien extends Mammifère { ... void mord(){..}... }

class chat extends Mammifère { ... }

class Rat { ... }

...

public static void main(String [] argv ) {

Mamifère m = new Mammifère();


Chien rex= new Chien();
Chat catator = new Chat();

// on peut écrire
m=rex;
// ici le type réel de m devient Chien mais ça ne pose aucun problème
// car un chien a tous les attributs et méthodes de Mammifère.

rex = m;
// ici, on a un problème, le mammifère ne dispose pas des
// nouveaux attributs du chien.

rex = (chien) m;
// cette commande sera acceptée à la compilation mais elle
// peut poser des problèmes à l'exécution si le type réel de
// m n'est pas chien ( ou une classe dérivée de chien.
// en fait, on force la compilation mais on ne converti pas m.

catator = rex; //refusé à la compilation

catator = (chat) rex; // on ne peut pas le faire.

Règle 1 : mère = fille toujours OK( il fait que le type réel d'un objet peut être différent du type
déclaré.

Règle 2 : fille = (fille) mère OK mais peut planter à l'exécution donc il faut éviter ou s'assurer des
conséquences.

Règle 3 : Classe1=Classe2 jamais ok si il n'y a aucune relation de parenté entre les deux classes.

Cours Programmation Orientée Objet 43/58


ISET-Sousse

CC c1; // ici, on déclare que c1 est de type CC.

On peut aussi utiliser le transtypage pour mettre des objets de classes différentes dans un même
tableau.

exemple : On veut un tableau pouvant contenir des CompteEpargne et des CompteBancaire.

CompteBancaire [] tab = new CompteBancaire [3];


tab[0] = new CompteBancaire();
tab[1] = new CompteEpargne();
tab[3] = new CompteBancaire();
for(int i=0;i<tab.length;i++) tab[i].affiche();

Regle 4 : un objet peut toujours être traité comme s'il était du type de celui de sa classe mère.
L'inverse est faux.

L'ancêtre de toutes les classes existe, il s'agit de la classe Object.


( si on a : class Truc {...} c'est comme si on avait class Truc extends Object {... } )

6. Masquage et redéfinition
En programmation objet, il est possible pour un attribut d’une classe dérivée de porter le même
nom qu’un attribut d’une classe ancêtre. De même, il est possible pour une méthode d’une classe
dérivée de porter la même signature (même nom, et même suite des types de ses paramètres)
qu’une méthode d’une classe ancêtre. Dans les deux cas, cette possibilité est assortie de
conditions et va suivre quelques règles que nous allons étudier dans les sections suivantes. (Par
exemple si un attribut ou une méthode est finale, nous n’avons pas cette possibilité).
Ces règles vont se répartir entre deux phénomènes : le masquage qui concerne les attributs et les
méthodes et la redéfinition qui concerne exclusivement les méthodes. De manière simplifiée :
 Le masquage est la faculté de cacher un attribut ou une méthode d’une classe ancêtre qui,
sans cela, serait visible.
 La redéfinition est la faculté, dans un objet donné, d’utiliser une méthode définie dans une
classe dérivée, à partir d’une méthode d’une classe ancêtre.

Si l’on se place au niveau des espaces emboîtés, le masquage s’intéresse à ce qui se passe en
sortant des espaces vers des espaces englobants, alors que la redéfinition donne la possibilité
d’aller vers des espaces englobés.
Notations : de l’intérieur d’une classe, il est possible d’atteindre un attribut ou une méthode de la
classe mère dont elle hérite directement, en utilisant le mot clé super. Par exemple : super.a ou
super.afficher(String, …). Bien entendu, ceci n’a de sens que si, d’une part, il y a ambiguïté
(deux attributs de même nom ou deux méthodes de même signature), et si, d’autre part, les règles
de visibilité sont respectées. Par ailleurs, la notation super.super n’est pas autorisée.

Remarque : il ne faut pas confondre la possibilité d’avoir des méthodes ayant la même signature
dans une classe et une classe dérivée, avec la surcharge ou le polymorphisme qui est la possibilité
d’avoir, dans une même classe, des méthodes de même nom mais de signatures distinctes.

Cours Programmation Orientée Objet 44/58


ISET-Sousse

6.1. Le masquage
Nous ne parlerons, dans cette section, que des attributs. Nous traiterons globalement les
comportements (les méthodes) dans la section suivante (section 6.2) car les méthodes se
comportent en général différemment des attributs.
Lorsque, dans une méthode nous rencontrons une variable ou un attribut non défini dans cette
méthode, où aller chercher l’espace correspondant ? C’est la sémantique des espaces emboîtés
qui permet de le déterminer. Comme nous l’avons vu, la recherche se fait dans l’ordre suivant

1. Dans l’espace de la méthode


2. Dans la partie statique de la méthode
3. Dans l’espace de l’instance dans laquelle est ouverte la méthode

Puis la recherche dans l’instance se fait de manière récursive dans l’ordre suivant :
1. Dans la partie non statique de l’instance
2. Dans sa partie statique
3. Dans l’espace de sa super-instance.

Toutefois la recherche peut être infructueuse si, en remontant ainsi, le premier attribut rencontré
et portant le nom recherché est qualifié par un modificateur le rendant invisible à un objet d’une
classe dérivée d’où est partie la recherche.
Attention : il ne peut y avoir deux attributs portant le même nom dans une même classe. En
particulier, il ne peut y avoir d’attributs portant le même nom à la fois dans la partie statique et
dans la partie non statique d’une classe. En revanche, une variable locale à une méthode peut
porter le même nom qu’un attribut de la classe dans laquelle elle est définie.

Puisqu’il est possible d’avoir des attributs portant le même nom dans une classe et dans ses sous-
classes, lorsqu’on est dans l’une des sous-classes ou encore plus bas dans une classe dérivée, on
dit que l’attribut de la sous-classe cache ou masque l’attribut de la classe mère, lorsqu’on se
trouve dans la sous-classe ou dans une de ses classes dérivées.

Il n’y a pas de contraintes particulières sur les modificateurs pour le masquage d’attributs excepté
qu’un attribut final ne peut être masqué. Si un attribut est déclaré final, aucune de ses sous-
classes, à quelque niveau que ce soit, ne peut avoir d’attributs portant son nom.
Par exemple, un attribut privé et/ou non statique peut masquer un attribut public et/ou statique et
inversement.
Du point de vue notation nous pouvons distinguer quatre cas :
Attribut seul : appliquer la règle de recherche donnée ci-dessus, à partir du lieu où l’attribut est
rencontré,
Attribut attaché à un objet (x.a, this.a ou super.a) : appliquer la règle de recherche à partir de
l’espace de l’objet désigné, correspondant au type de cet objet.
Attribut attaché à un objet transtypé : appliquer la règle de recherche à partir de l’espace de
l’objet désigné, correspondant au nouveau type.
Attribut attaché à une classe : si l’attribut n’existe pas dans la classe, appliquer la règle de
recherche à partir de cette classe. Si un attribut non statique masque l’attribut statique, une erreur

Cours Programmation Orientée Objet 45/58


ISET-Sousse

de compilation est alors émise. Toutefois, c’est une idée irrationnelle d’attacher un attribut
statique à une classe dérivée et non à la classe dans laquelle il a été défini.

Exemple : soient une classe A, B une sous classe de A, et C une sous classe de B. Chacune de ces
classes contient un attribut public entier a, initialisé respectivement à 1 dans A, à 2 dans B et à 3
dans C. Alors,
C z = new C(); // z est un objet de type C
B y = new B(); // y est un objet de type B
A x = new A(); // x est un objet de type A

z.a vaut 3
y.a vaut 2
x.a vaut 1
(B)z.a vaut 2
(A)z.a vaut 1
(A)y.a vaut 1
(C)y.a vaut 3
(C)x.a vaut 3

Si on est dans l’objet z, dans l’espace dérivé le plus bas, (B)this.a vaut 2 et (A)this.a vaut 1.

6.2. La redéfinition
6.2.1. Généralités
Dans cette section, nous abordons le cas des méthodes portant la même signature dans une classe
et une classe dérivée. Pour que cela soit possible, ces méthodes doivent suivre quelques règles :

Règle 1 : Les droits d’accès


Si deux méthodes ont la même signature dans une classe et dans l’une de ses classes dérivées, la
visibilité de la méthode de la classe dérivée ne peut être moindre que celle de la classe ancêtre,
excepté si la méthode de la classe n’est pas visible depuis la classe dérivée (cas sans modificateur
et la classe dérivée est dans un autre package)

Classe ancêtre
Classe dérivée public protected <aucun> private
Public oui oui oui oui
Protected non oui oui oui
<aucun> non non oui oui
private non non non oui

La règle 1 ne concerne que la visibilité, et donc s’applique tant aux méthodes de classe (statiques)
qu’aux méthodes d’instance (non statiques).
class A {
public void f (int n) {…}
}
class B extends A {

Cours Programmation Orientée Objet 46/58


ISET-Sousse

private void f (int n) {…} //tentative de redéfinition de f de A


}
La compilation de la classe B est rejetée par le compilateur. Si elle était acceptée, un objet de
classe A aurait accès à la méthode f, alors qu’un objet de classe B n’y aurait plus accès : c’est
pourquoi la redéfinition d’une méthode ne doit pas diminuer les droits d’accès à cette méthode.
En revanche, elle peut les augmenter, comme dans l’exemple suivant :
class A {
private void f (int n) {…}
}
class B extends A {
public void f (int n) {…} // redéfinition de f avec extension des
} // droits d’accès
Règle 2 : si deux méthodes ont la même signature dans une classe et dans l’une de ses classes
dérivées, elles doivent être ou bien toutes les deux statiques, ou bien toutes les deux non
statiques, excepté si la méthode de la classe n’est pas visible depuis la classe dérivée.
Par exemple, masquer une méthode non statique sans modificateur par une méthode statique n’est
en général, pas autorisé. Toutefois, il peut y avoir une méthode statique de même signature dans
la classe dérivée si la méthode non statique n’est pas visible (une classe dérivée est dans un
package distinct de celui de la classe dans laquelle figure la méthode non statique).
Règle 3 : Valeur de retour
Si deux méthodes ont la même signature dans une classe et dans l’une de ses classes dérivées,
elles doivent avoir le même type de retour, excepté si la méthode de la classe n’est pas visible
depuis la classe dérivée.

Règle 4 : dans une classe, il ne peut y avoir de méthode ayant une signature identique à une
méthode finale visible appartenant à l’une de ses classes ancêtres.

6.2.2. Redéfinition
Exemple :
définissons dans la classe Personne la méthode :
public void afficher(String finDeLigne)
{
System.out.print(nom+" " + prenom);
System.out.print(" (" + age +" ans )");
System.out.print (" ; " + adresse);
System.out.print (finDeLigne);
}

définissons dans la classe Etudiant la méthode :


public void afficher(String finDeLigne)
{
super.afficher(“ ; ”);
System.out.print(filiere);
System.out.print(" : " + anne);
System.out.print (" [ " + moyenne + "/20 ");

Cours Programmation Orientée Objet 47/58


ISET-Sousse

System.out.print (finDeLigne);
}

Considérons la classe TableauDePersonnes qui contient trois attributs :


private Personne personne[] ; // tableau de Personnes
private int taille ; // taille logique, le nombre réel de personnes
// dans le tableau
private int tailleMax ; // taille physique

Supposons que le tableau de Personne t contienne 3 personnes (taille vaut 3) et que la troisième
personne soit un étudiant. Un étudiant étant une personne, elle sera acceptée en tant que telle dans
le tableau (transtypage naturel).

personne[0] = (« ABID », « Ali », 20, « Sousse »)


personne[1] = (« GADOUR », « Mohamed », 18, « Monastir »)
personne[2] = (« Ben SAÏD », « Rim », 20, « Nabeul », « Informatique »,
« Licence », 12.56)
De plus supposons que TableauDePersonne contienne la méthode :
public void afficher(void)
{
for (int i = 0 ; i<taille ; i++)
personne[i].afficher(“\n”);
}

Nous obtiendrons, en lançant personne.afficher(), le résultat suivant :

ABID Ali (20 ans) Sousse


GADOUR Mohamed (18 ans) Monastir
Ben SAÏD Rim (20 ans) Nabeul ; Informatique : Licence [12.56/20]

Ce qui est remarquable dans cet exemple, est que, pour la troisième personne, la méthode utilisée
est celle de l’étudiant, alors que personne est un tableau de Personne. Si, à la place de la méthode
afficher, nous avions fait appel à un attribut a, et que cet attribut soit défini dans les deux classes
Personne et Etudiant, alors seul l’attribut de la classe Personne aurait été utilisé, que l’élément
stocké dans le tableau soit une personne ou un étudiant (voir masquage). Tandis qu’avec les
méthodes, c’est la méthode de la classe la plus dérivée qui est utilisée.
Ceci a un intérêt car si l’on cherche les caractéristiques d’une personne, toutes ses
caractéristiques connues sont affichées. Si ce n’est qu’une personne sans autre connaissances,
alors seules les caractéristiques de la personne seront affichées. Mais si nous en connaissons plus,
par exemple pour l’étudiant, alors nous aurons un affichage plus important. Ce phénomène
s’appelle la redéfinition. C’est une caractéristique importante de la technologie objet. Les
attributs ne sont pas redéfinis. Deux attributs peuvent porter le même nom dans des classes
distinctes d’une même filiation, mais on ne parle pas de redéfinition. On parle seulement de
masquage. En revanche, pour les méthodes, nous avons cette nouvelle notion de redéfinition.
Toutefois, la redéfinition ne s’applique pas à toutes les méthodes.
Règle 5 : les méthodes statiques, les méthodes privées et les méthodes finales suivent les règles
du masquage et non celles de la redéfinition.

Cours Programmation Orientée Objet 48/58


ISET-Sousse

Exemple : soient une classe A, une classe B qui hérite de A, et une classe C qui hérite de B.
Supposons que A, B, C contiennent toutes les trois une méthode publique non statique m(). Alors
la méthode m() de C redéfinit la méthode m() de B, qui elle même redéfinit la méthode m() de A.
La méthode m() de A contient l’unique instruction :

System.out.println(« Je suis dans A ») ;

Les méthodes m() de B et C sont identiques au nom de la classe près. Supposons de plus que dans
la classe A et uniquement dans A soit définie la méthode f() suivante, qui fait un simple appel à la
méthode m() ;
public void f() {
m() ;
}
soient x un objet de type A, y un objet de type B et z un objet de type C. Alors :
l’instruction x.f() affichera : Je suis dans A
l’instruction y.f()affichera : Je suis dans B
l’instruction z.f()affichera : Je suis dans C

Il faut remarquer, dans le dernier cas, que la méthode f() ne figurant pas dans la classe C, est
héritée de la classe A et que depuis l’espace correspondant, elle revient chercher la méthode m()
de la classe C.

La redéfinition est une notion différente du masquage en ce sens que :


 Le masquage concerne une recherche d’attribut ou de méthode en sortant des espaces. Si
l’élément recherché ne se trouve pas dans un espace, il est recherché dans un espace
englobant le premier espace et en respectant les règles de visibilité.
 La redéfinition concerne la recherche de méthodes dans des espaces englobés. (donc en
sens contraire du masquage, figure 15).

Figure 10 : sens des recherches pour le masquage et pour la redéfinition


La redéfinition vient du fait que, à l’intérieur d’une méthode tout appel à une autre méthode n’est
pas figé, mais est déterminée dynamiquement au moment de son utilisation.

Cours Programmation Orientée Objet 49/58


ISET-Sousse

Un appel classique de méthode non statique f(..) passe par un objet u de type X. Cet objet bien
qu’instance de la classe X, peut résulter du transtypage d’une classe Y dérivée de X. Or nous
avons vu que l’adresse de u est celle de Y : u contient la valeur (adresse de Y, X). La recherche
dynamique va se faire prioritairement entre l’espace du niveau de Y et l’espace du niveau de X.
Dans l’exemple ci-dessus, illustré figure 15, si t est une instance de la classe A initialisée par t =
z, l’instruction t.m() cherchera m() dans l’espace de niveau C, puis de niveau B puis de niveau A.
La recherche suivra alors l’algorithme suivant, dans lequel, en entrée nous avons
MethodeArecherche la signature de la méthode à rechercher
NiveauMini désigne l’espace de niveau minimal dans lequel est recherchée la méthode
(ici celui de Y). C’est toujours le plus interne de la zone de recherche.
NiveauMaxi désigne l’espace de niveau maximal dans lequel est recherchée la méthode. Il
s’agit de l’espace du niveau de la classe réelle de l’objet (ici celui de X). C’est toujours le
plus externe de la zone de recherche. Ce niveau sera exclu de la recherche ci-dessous.
Variables intermédiaires sont
niveauCourant désigne l’espace dans lequel est recherchée la méthode. Au début de
l’algorithme, il s’agit de l’espace du niveau de la classe réelle de l’objet (ici celui de Y).
C’est toujours le plus interne de la zone de recherche.
NiveauMéthodeAAppliquer est le niveau de la méthode à appliquer. Au début de
l’algorithme elle est initialisée à vide (0)
Modificateurs est un doublet contenant la liste des modificateurs de la méthode à
rechercher dans un niveau courant précédent et ce niveau précédent. Au début de
l’algorithme elle est initialisée à vide (0,0).
Nous dirons que des modificateurs M1 d’une méthode sont prioritaires sur des modificateurs M2
d’une méthode de même signature dans une classe dérivée, soit si M1 contient le modificateur
static, soit si M1 rend non visible la méthode modifiée par M1 à partir du niveau de M2.
Debut :
niveauCourant  niveauMini
niveauMethodeAAppliquer  0
modificateurs  0
tant que (niveauCourant <= niveau Maxi) faire
niveauCourant  recherche le niveau de la méthodeARecherche par
la règle des espaces emboîtés à partir du niveauCourant
sans tenir compte de la visibilité (retourne 0 si non trouvée).
Si (niveauCourant est vide) ou si (niveauMaxi < niveauCourant
et methodeArechercher du niveauCourant est non visible
depuis niveauMaxi),
Alors la méthode n’existe pas dans l’espace niveau maximum
ni au dessus. Rendre (0,0). Il n’y a pas de méthode dont
la signature corresponde à la méthode recherchée
Sinon si (niveauMethodeAAppliquer = 0)
ou si (modificateurs prioritaires sur les modificateurs
de methodeARecherche du niveauCourant)
NiveauMethodeAAppliquer  niveauCourant
Fin des si
niveauCourant  niveau immediatement superieur à niveauCourant
modificateur  (modificateurs de niveauCourant, niveauCourant)
fin tant que
rendre niveauMethodeAAppliquer.
fin

Cours Programmation Orientée Objet 50/58


ISET-Sousse

Remarquons que cet algorithme permet de faire se succéder la notion de redéfinition (recherche
vers les espaces englobés) et celle du masquage (recherche vers les espaces englobants) si la
première est infructueuse. En particulier, pour les méthodes statiques, privées ou finales du
niveau maximum, tout se passe comme s’il n’y avait que la notion de masquage.
Ecrivons par exemple, une méthode saisir pour la classe Etudiant
Titre de l’algorithme : saisir
Entrées : un flot d’entrée pour prendre les informations. Nous utiliserons pour simplifier une
instance de la classe Ask fournie dans la bibliothèque utilitaire. Nous nommerons l’objet
correspondant : demande.
Finalité : remplir les attributs de l’objet. (modifier son état).
Sortie : un booléen vrai si la saisie s’est terminée correctement, faux sinon.
Stratégie : nous utiliserons, tout d’abord le comportement saisir de la classe Personne, pour
récupérer les valeurs des attributs de l’étudiant en tant que Personne. Puis, si la saisie s’est bien
passée jusque là, nous saisirons les valeurs des attributs filiere et année, en utilisant les méthodes
de l’objet demande. Les chaînes de caractères seront traduites en majuscules par la méthode
toUpperCase de la classe String.
La moyenne n’est pas saisie. Elle le sera au moment des examens, par la méthode setMoyenne.
Variables intermédiaires : Aucune
Méthodes utilisées : les deux méthodes de l’objet demande utilisées, renvoient les exceptions
suivantes : IOException si le flot d’entrée n’est pas correct, et NullPointerException en cas
d’erreur de saisie due à la terminaison du flot d’entrée avant la fin de la saisie. Cette dernière
erreur survient, par exemple, lorsque la saisie se fait à partir d’un fichier qui ne contient pas assez
de données (la fin du fichier est atteinte avant la fin de la saisie).
 ChaineCompacte(String commentaire, int tailleMax) de la classe Ask.. Elle est
chargée de récupérer sur le flot d’entrée correspondant à l’objet de demande courant, une
chaîne de caractères, d’y supprimer les blancs commençants et finissants, et de réduire à
un seul espace les suites de blancs intermédiaires. La chaîne finale aura au maximum
tailleMax caractères. La saisie de la chaîne finale aura au maximum tailleMax
caractères. Cette méthode rend un pointeur vers l’objet de type String résultant.
 Entier(String commentaire, int max, int min) de la classe Ask. Elle est chargée
de récupérerr sur le flot d’entrée correspondant à l’objet de demande courant, un entier de
type int compris entre les valeurs min et max incluses. La saisie de l’entier se fera après
affichage du message si le flot d’entrée est le flot standard. Cette méthode rend l’entier
saisi.
 toUpperCase() de la classe String. Cette méthode crée un nouvel objet de
type String représentant une suite de caractères identique en longueur à la
chaîne en cours de conversion, et dont chaque caractère est la traduction
en majuscule du caractère correspondant dans la chaîne en cours de
conversion
 saisir(demande) de la classe Personne. Cette méthode est chargée de
récupérer les valeurs des attributs d’une Personne sur le flot d’entrée
associé à l’objet demande. Elle rend le booléen false si le nom de la
personne est null, true si non.

Cours Programmation Orientée Objet 51/58


ISET-Sousse

public boolean saisir(Ask demande)


throws IOException, NullPointerException
{
if (!super.saisir(demande))
return false ;
this.filiere = (demande.chaineCompacte("filiere",30)).toUpperCase() ;
this.annee = (demande.chaineCompacte("annee",20)).toUpperCase() ;
return true ;
}
Il y a un certain nombre de règles à respecter pour les contrôles d’accès :
 Un attribut ou une méthode privés ne sont pas hérités
 Un attribut ou une méthode protected ne peut être redéfinie que protected ou public. (Elle
ne peut avoir un contrôle d’accès plus sévère que dans la classe mère).
 Un attribut ou une méthode public ne peut être redéfinie que public. (Elle ne peut avoir un
contrôle d’accès plus sévère que dans la classe mère).
 Un attribut ou une méthode sans modificateur peut être redéfinie public, protected ou
private ou laissée sans modificateur.

7. La classe Final :

une classe final est une classe générale. On ne peut JAMAIS hériter d'une classe final.

class Generale {

final void methode_generale(){...} // ici, on peut hériter de la classe Générale mais on ne peut pas
redéfinir la méthode methode_generale().

Approfondissement

Voici un exemple complet montrant ce qui se passe exactement dans une situation délicate : des
appels de méthodes static dans des méthodes héritées...

class A {

static void a() {

System.out.println("methode a");
b();

static void b() {

System.out.println("méthode b qui réalise une chose pour la classe A");

Cours Programmation Orientée Objet 52/58


ISET-Sousse

class B extends A {

static void b() {

System.out.println("cette méthode est mieux " );

public static void main(String [] argv){

B b = new B();
b.a(); // ici, la méthode b() appelée sera celle de la classe A

Encapsulation

A. Paquetage.
Un paquetage [package] est le regroupement sous un nom d'un ensemble de classes. Les
paquetages de Java sont hiérarchisés sous la forme d'une arborescence. En chaque noeud de
l'arborescence peut se trouver un paquetage.
Un paquetage se désigne par un ensemble d'identifiants séparés par des points. Ex :
java.awt.geom est un paquetage de Java contenant des classes définissant des objets
géométriques. A l'arborescence logique des paquetages correspond l'arborescence physique des
répertoires et fichiers mémorisants les fichiers *.class des paquetages. Ex : les classes du
paquetage java.awt.geom sont stockées dans le répertoire java\awt\geom à partir du répertoire
racine des paquetages.
1. Attribution d’une classe à un paquetage
Un paquetage est caractérisé par un nom qui est soit un simple identificateur, soit une suite
d’identificateurs séparés par des points, comme dans :
 mesClasses

Cours Programmation Orientée Objet 53/58


ISET-Sousse

 utilitaires.mathematiques
 utilitaires.tris
Pour inclure les classes d'un fichier source dans un paquetage, il faut placer la commande
package monPaquetage; en tête du fichier. Les fichiers *.java créés seront stockés dans le
répertoire MonPaquetage.
Cette instruction est suffisante, même lorsque le fichier concerné est le premier auquel on attribue
le nom de paquetage en question. En effet, la notion de paquetage est une notion "logique",
n’ayant qu’un rapport partiel avec la localisation effective des classes ou des fichiers au sein de
répertoires
De même, lorsqu’on recourt à des noms de paquetages hiérarchisés (comme Utilitaires.Tris), il
ne s’agit toujours que d’une facilité d’organisation logique des noms de paquetage. En effet, on
ne pourra jamais désigner simultanément deux paquetages tels que
Utilitaires.Mathematiques et Utilitaires.Tris en se contentant de citer Utilitaires. Qui plus
est, ce dernier pourra très bien correspondre à d’autres classes, sans rapport avec les précédentes.

2. Utilisation d’une classe d’un paquetage


Lorsque, dans un programme, vous faites référence à une classe, le compilateur la recherche dans
le paquetage par défaut. Pour utiliser une classe appartenant à un autre paquetage, il est
nécessaire de fournir l’information correspondante au compilateur. Pour ce faire, vous pouvez :
 citer le nom du paquetage avec le nom de la classe,
 utiliser une instruction import en y citant soit une classe particulière d’un paquetage, soit
tout un paquetage.

Pour utiliser une ou plusieurs classes définies dans un paquetage, il y a plusieurs solutions :
a. En citant le nom de la classe. Expliciter le paquetage de la classe en préfixant le nom
de la classe par son nom de paquetage : monPaquetage.A a = new A();
b. En important une classe. Utiliser en tête du fichier l'instruction import suivi du nom du
paquetage et de la classe : import monPaquetage.A; On n'a alors plus besoin de préfixer
le nom de la classe dans le reste du fichier : A a = new A();
c. En important un paquetage. Importer toutes classes d'un paquetage : import
monPaquetage.*; On n'a alors plus besoin de préfixer aucun nom de classe du
paquetage dans le reste du fichier.

B. Encapsulation.
L'encapsulation est une notion importante de la programmation objet et du génie logiciel. Elle
consiste à masquer le plus possible les détails d'implémentation et le fonctionnement interne des
objets. Cette dissimulation permet de découpler les objets constituants un programme afin que la
modification de la structure interne d'une classe ne remet pas en cause le code utilisant celle-ci.
L'encapsulation permet aussi la réutilisation d'une classe dans un autre contexte (cas des
bibliothèques).

Cours Programmation Orientée Objet 54/58


ISET-Sousse

Les private protected - (package) public

La classe elle même Oui Oui Oui Oui


Sous -classes d'un autre Non Oui Oui Oui
package
Classes (non sous - classes) non Non Oui Oui
d'un autre package
Classes du même Non Non Non Oui
Package

données propres à un objet ne sont accessibles qu'au travers des méthodes de cet objet sécurité
des données : elles ne sont accessibles qu'au travers de méthodes en lesquelles on peut avoir
confiance masquer l'implémentation : l'implémentation d'une classe peut être modifiée sans
remettre en cause le code utilisant celle-ci
B.1. Niveaux d'accès
En Java, on peut restreindre l'accès à des classes, des méthodes et des attributs. Cette restriction
les rend invisibles et inréférençables en dehors de leurs niveaux d'accès. Concernant les membres
(méthodes et attributs) des classes, il existe 4 niveaux d'accès (visibilité):
 public accessible à toute autre classe, le membre est accessible de n'importe où.
 protected est accessible dans la classe où il est défini, dans toutes ses sous-classes et dans
toutes les classes du même package
 package (visibilité par défaut) le membre n'est accessible que dans les classes du même
package que celui de la classe où il est défini
 private n'est accessible qu'à l'intérieur de la classe où il est défini

Tableau 1 Visibilité des variables et Visibilité des variables et méthodes

Cours Programmation Orientée Objet 55/58


ISET-Sousse

Concernant les classes, il y a deux niveaux d'accès : public ou privé paquetage. Dans un même
fichier, une seule classe au maximum peut être déclarée publique. Si une classe contient la
méthode statique main alors c'est cette classe qui doit être publique.

B.2. Pourquoi restreindre les accès ?


Le principe de base de l'encapsulation est de rendre tout le moins accessible possible : par défaut,
tous les attributs et les méthodes sont privés et les classes sont privées paquetage. L'allégement de
toute restriction doit être motivée.
B.2.1. La modularité et le découplage
Si l'accès d'une classe est privé paquetage alors on sait que si on la modifié ou on la supprime,
aucune classe extérieur au paquetage n'en sera affectée. Il n'y aura qu'à modifier éventuellement
certaines classes du paquetage.
Si l'accès d'un membre d'une classe est privé (resp. privé paquetage) alors on pourra le modifier
ou le supprimer sans qu'aucune autre classe (resp. aucune classe en dehors du paquetage) ne soit
affectée.
En pratique, afin de rendre opaque la structure d'un objet pour permettre de modifier cette
structure ultérieurement, dans toutes les classes, tous les attributs seront privés. Dans notre
exemple de la classe Compte, on va rendre privé l'accès aux attributs et à la méthode retrait à un
seul paramètre, afin qu'un objet extérieur ne puisse pas modifier une instance de Compte
n'importe comment :
class Compte
{
private int solde;
private int numero;
private String proprietaire;
public Compte(int numero, String prop){...}
public Compte(int numero){...}
public void depot(int montant){...}
private void retrait(int montant){...}
public boolean retrait(int montant, String prop){...}
public boolean transfertPerso(int montant){...}
public void virement(int montant, Compte dest)
}
Néanmoins, on peut avoir besoin de connaître le solde d'un compte. Comme on doit interdire de
modifier l'attribut solde de l'extérieur de la classe, on va simplement écrire la méthode :
public int solde() { return solde;} .

De façon plus générale, si on a besoin de connaître la valeur d'un attribut ou de le modifier, on


effectuera cette opération via une méthode (non privée) dite d'accès. Les méthodes fournissant la
valeur d'un attribut sont appelées des accesseurs. Les méthodes transmettant une nouvelle valeur
à affecter à un attribut sont appelées des modifieurs.
Exemple : on veut créer une classe Complexe pour représenter des nombres complexes. Un
nombre complexe z a deux formes : z = x+i.y ou z = ρ.ei.θ. Comme ρ et θ peuvent se calculer en

Cours Programmation Orientée Objet 56/58


ISET-Sousse

fonction de x et y, on peut décider de déclarer les attributs flottants partieReelle et


partieImaginaire ainsi que les 8 méthodes suivantes :
float getRe(), void setRe(float), float getIm(), void setIm(float),
float getRho() void setRho(float), float getTheta(), void setTheta(float).

Les 4 premières lisent et affectent directement les attributs partieReelle et partieImaginaire tandis
que les 4 dernières le font indirectement via des calculs (mais l'utilisateur de la classe n'est pas
censé le savoir). Plus tard on peut décider de remplacer les attributs partieReelle et
partieImaginaire par rho et theta ou d'ajouter ces deux derniers. Il faudra alors modifier (une
partie) de ces 8 méthodes mais il n'y aura pas besoin de modifier le code des classes utilisant la
classe Complexe : la modification de la structure interne de cette classe est transparente pour les
classes qui l'utilisent.

B.2.2. Sécurité des données : elles ne sont accessibles qu'au travers de méthodes en lesquelles
on peut avoir confiance
A titre d’exemple, il est conseillé de déclarer moyenne dans la classe Etudiant avec le
modificateur private. En effet c’est un attribut sensible, pour lequel des vérifications doivent
être faites avant de prendre en compte sa valeur. Par exemple, la moyenne doit être comprise
entre 0 et 20. Si la moyenne n’est pas attribuée, alors, par convention, la valeur de la moyenne est
–1. Parfois, pour des problèmes de sécurité, seules quelques personnes ont l’autorisation de
modifier la moyenne … En protégeant ainsi l’attribut moyenne par le modificateur private, on
se préserve de modifications inappropriées ou non autorisées. On appelle cette technique de
protection : l’encapsulation. L’encapsulation impose, dans ce cas, de pouvoir accéder à la
moyenne par un autre moyen. Il faut donc au moins deux méthodes pour cela : la méthode
setMoyenne pour modifier la valeur de la moyenne de manière appropriée, et la méthode
getMoyenne pour obtenir sa valeur. Voyons un exemple de méthode setMoyenne :
Entrées : un flottant moyenne,
Hypothèses complémentaires : ce flottant doit valoir soit –1 si la moyenne n’est pas attribuée, soit
être comprise entre 0 et 20 inclus.
Finalité : modifier la moyenne si la valeur en entrée est acceptable.
Sortie : Un booléen vrai si la valeur en entrée est acceptable, faux sinon,
Conclusion complémentaire : l’attribut moyenne est modifié si la valeur en entrée est
acceptable.
Stratégie : si la valeur en entrée est acceptable, modifier la valeur de l’attribut moyenne
Variable intermédiaire : aucune
Méthodes utilisées : aucune.
public boolean setMoyenne(float moyenne) {
if (moyenne != -1 && (moyenne <0 || moyenne > 20))
return false ;
this.moyenne = moyenne ;
return true
}

Cours Programmation Orientée Objet 57/58


ISET-Sousse

On aurait pu également insérer dans cet algorithme un appel à une méthode d’autorisation qui
demande un mot de passe, afin que seules les personnes autorisées aient doit d’accès à la
modification.
Le fait que la moyenne ne soit pas publique et donc que l’écriture E.moyenne soit illégale, nous
impose d’avoir une méthode pour obtenir sa valeur. Dans notre cas, cette méthode contiendra un
simple retour de valeur (voir une demande d’autorisation) :

public float getMoyenne() {


return moyenne ;
}

Cours Programmation Orientée Objet 58/58

Vous aimerez peut-être aussi