Vous êtes sur la page 1sur 134

ROYAUME DU MAROC

Office de la Formation Professionnelle et de la Promotion du Travail


DIRECTION RECHERCHE ET INGENIERIE DE FORMATION

OFPPT

RESUME THEORIQUE
&
GUIDE DES TRAVAUX PRATIQUES

MODULE N°4 :

Titre : Programmation orientée objet

SECTEUR : Tertiaire
SPECIALITE : Technicien Spécialisé en Développement Informatique
NIVEAU : TS

Septembre 2006
REMERCIEMENT
La DRIF remercie les personnes qui ont contribué à l’élaboration du présent document.

Pour la supervision :

MME.BENNANI WAFAE DIRECTRICE CDC TERTIAIRE & TIC


M. ESSABKI NOURDDINE CHEF DE DIVISION CCFF

Pour la conception :

- JELLAL ABDELILAH Formateur animateur au CDC Tertiaire & TIC

Pour la validation :

Les utilisateurs de ce document sont invités à


communiquer à la DRIF toutes les remarques
et suggestions afin de les prendre en
considération pour l’enrichissement et
l’amélioration de ce programme.

Said Slaoui
DRIF
OBJECTIF OPERATIONNELS DE PREMIER NIVEAU
DE COMPORTEMENT
PRECISIONS SUR LE COMPORTEMENT CRITERES PARTICULIERS DE
ATTENDU PERFORMANCE

A. Programmer des classes (métier ou • Création et instanciation judicieuse


service) dans un langage orienté objet des classes
(JAVA) • Définition correcte des propriétés et
des méthodes de cette classe.
• Implémentation appropriée des
concepts
d ’encapsulation, héritage et
polymorphisme
B. Exploiter les classes de collections
• Utilisation adéquate des classes de
collections(listes,piles,tableaux,dicti
onnaire...)
• Implémentation judicieuse des
relations entre les classes dont la
cardinalité est multiple
• Identification correcte du rôle de
chaque type de collection par
rapport aux spécification d’une
application
• Justesse d’ajout,suppression ou
modification d’objet d’une collection
• Association adéquate d’un
traitement à chaque instance
C. Programmer les exceptions d’objet contenu dans une collection

• Identification correcte des


événements d’exception associés
D. Gérer les flux (entrée/sortie) aux classes
• Traitement correct des exceptions
dans la classe appelante
• Codage judicieux des instructions
d’entée/sortie pour des fichiers
ASCII avec les classes techniques
E. Réaliser les tests unitaires du langage utilisé.

• Constitution judicieuse d’un jeu


d’essai contenant tous les cas
F. Documenter le programme possibles que le programme est
censé de développer
• Justesse de réalisation du jeu
d’essai

• Documentation judicieuse des


paramètres en entrée
• Documentation judicieuse des
paramètres en sortie
• Respect du formalisme de
documentation d’un programme
informatique

OBJECTIFS OPERATIONNELS DE SECOND NIVEAU

LE STAGIARE DOIT MAITRISER LES SAVOIR, SAVOIR-FAIRE, SAVOIR -PERCEVOIR OU


SAVOIR-ETRE JUGES PREALABLES AUX APPRENTISSAGES DIRECTEMENT REQUIS POUR
L’ATTEINTE DE L’OBJECTIF DE PREMIER NIVEAU, TELS QUE :

Avant d’apprendre à Programmer des classes (metier ou service) dans un


langage orienté objet (JAVA..) (A) :

1. Expliquer les concepts liés à la programmation orienté objet


2. Définir et coder un algorithme
3. Expliquer la notion de propriété et méthode d’une classe .
4. Expliquer le principe d’encapsulation, héritage et polymorphisme,
instanciation

Avant d’apprendre à Exploiter les classes de collections (B)

5. Expliquer l’intérêt des classes de collections


6. Identifier les classes de collections

Avant d’apprendre à programmer les exceptions (D ) :

7. Expliquer la notion d’événements d’exception

Avant d’apprendre à Documenter le programme (F) :

8. Expliquer l’intérêt de documenter un programme informatique


9. Définir un formalisme de documentation à respecter
RESUME THEORIQUE ET TRAVAUX
PRATIQUES

Cours & Exemples pratiques


I - Classes et objets : Introduction à la P.O.O.

La programmation classique : Structures de


données

Algorithmes

Programme

Dans la programmation traditionnelle (années 70 et 80) un programme faisait appel à des fonctions ou
procédures ( en PASCAL) qui traitaient des données ou des structures de données (plus généralement). Le
principe était de trouver l'algorithme puis ensuite de trouver la structure de donnée la mieux appropriée pour le
passage de paramètre aux différentes fonctions. Cette programmation rendait la structure de données "fragile" car
n'importe quelle fonction pouvait accéder et changer les champs de cette structure . De plus la réutilisation du
code de certaines fonctions était difficile car rien n'était compartimenté.

La programmation objet ou P.O.O.

Objet de la classe xxx

Données (notées Fonctions de


attributs ou traitements des
champs) données ou méthodes

Dans la programmation objet chaque paquets de données (qui aurait fait parti d'une structure en
programmation classique) est dans une classe. Les différents traitements à réaliser sur ces données sont faits par
des fonctions appartenant à la classe. L'accès aux données ne se fera que par des fonctions de la classe. Le fait de
compartimenter le code et les données dans une classe permet la réutilisation du code.

Exemples :
Classe Bateau : champs : Capitaine, type, couleur, position, vitesse,…
Fonctions ou méthodes : marche, stop, plus_vite, moins_vite
Classe Fenetre champs : positionX,positionY,TailleX,TailleY,Couleur,Nom,…
Fonctions ou méthodes : Créer, Réduire,Fermer,Déplacer, …

Avantages de la P.O.O.

La découpe du problème en classes et donc en objets (instances de la classe) permet la réutilisation du


code, chose plus difficile avec un programme classique.

Une classe peut hériter d'une autre classe ce qui permet d'utiliser les fonctions et les données de la
classe parent et de rajouter les données et fonctions de la nouvelle classe( en programmation
graphique toute nouvelle fenêtre windows est un objet hérité de la classe Fenetre dans laquelle on va
rajouter le code et les données de l'application).

Les fonctions de la classe n'ont pas un grand nombre d'arguments puisque les données sont lisibles par
les fonctions de la classe.

Les données de la classes sont protégées de l'extérieur : sécurité.

Grâce à la P.O.O., on gagne en rapidité de fabrication du code, puisqu'il est possible d'utiliser un
grand nombre de classe déjà existante.
Exemple :Le type abstrait Personne

Cahier des charges


Une personne est décrite par deux informations :
- son nom,
- la société où elle travaille.
Une personne est capable de s'identifier, en affichant les informations qui la caractérisent.

Programmation classique, en langage C


#include <stdio.h>
#include <string.h>

struct Personne { /* une personne est décrite par son nom et sa société */
char Nom[25];
char Societe[32];
};

void Presente(struct Personne P)


{ /* ce traitement permet d'afficher la description d'une personne */
printf ("Je m'appelle %s\n", P.Nom);
printf ("Je travaille à %s\n", P.Societe);
}
void main()
{
/* on définit la variable Individu qui décrit une personne */
struct Personne Individu;
/* on initialise cette variable */
strcpy(Individu.Nom, "DURAND");
strcpy(Individu.Societe, "OUTILS JAVA SA");
/* on affiche la description de Individu */
Presente(Individu);
}
Problèmes
Les informations qui concernent une personne ne sont pas protégées.
En effet, tout utilisateur du type composé struct Personne est libre de modifier Nom ou Societe sans restriction.
Programmation objet

Vocabulaire et définitions
Un objet est caractérisé par ses informations et ses comportements : en programmation, on dira qu’un objet
encapsule données et traitements.
Un objet reçoit des messages qui déclenchent ses comportements.
La programmation orientée objet apporte une sécurité supplémentaire par rapport à la programmation
classique puisque l’objet contrôle chacun des comportements qui lui sont propres.
Les propriétés des objets de données et comportements semblables sont décrites par une même classe.

La classe, support de l’encapsulation


Une classe décrit une famille d’objets.
p1, un objet Personne
la classe Personne
Dupond ima
nom societe

nom societe
deux instanciations
System.out.println(
"Je m'appelle " + nom); p2, un autre objet Personne
System.out.println(
"Je travaille à " + societe);

presenteToi() Durand Outils Java


nom societe

Les objets regroupés dans une même classe CCC sont appelés les instances de CCC.
Les données d’un objet sont ses variables d’instance.
Les comportements communs à toutes les instances sont représentés par des méthodes d’instance.

Une première version de la classe Personne

Pour le cahier des charges :


Une personne est décrite par deux informations :
- son nom, enregistré en majuscules
- la société où elle travaille.
Une personne est capable de s'identifier, en affichant les informations qui la caractérisent.
on peut définir :
class Personne {
// deux variables d’instance
public String nom;
public String societe;
// une méthode
public void presenteToi() {
System.out.println("Je m'appelle " + nom);
System.out.println("Je travaille à " + societe);
}
public static void main (String arg[])
{
Personne p = new Personne();//création d'une instance de la classe Personne
p.nom = "DURAND";
p.societe="OUTILS JAVA SA";
p.presenteToi(); //appel à méthode presenteToi()
}
} // class Personne, version provisoire

On obtient alors le même fonctionnement que le programme écrit en C précédemment.

Un cahier des charges plus complet

Précisons les propriétés des objets Personne


A Une personne est décrite par deux informations :
son nom et la société où elle travaille.
B Une personne a toujours un nom. Ce nom ne peut pas changer.
C'est une chaîne de caractères, dans laquelle les lettres sont en majuscules.
C Une personne n'est pas forcément associée à une société. Dans ce cas, elle est considérée comme un
travailleur indépendant ou comme une personne sans activité.
Une personne doit pouvoir indiquer si elle est ou non salariée (c'est-à-dire si sa société est identifiée).
D Lorsqu'une personne est associée à une société, cette société est identifiée par une chaîne ne comportant pas
plus de 32 caractères, dans laquelle les lettres sont en majuscules.
E Une personne peut changer de société ou même perdre toute association à une société.
F Une personne est capable de s'identifier,en affichant les informations qui la caractérisent.

L’accès aux informations doit être contrôlé


Si la classe Personne définit deux variables d’instance nom et societe, le programmeur qui utilise un objet
Personne ne doit pas pouvoir affecter sans contrôle une nouvelle valeur à l’une ou l’autre de ces variables.
Accès public ou privé ?

Seconde implémentation de la classe


class Personne {
private String nom;
private String societe;
public void presenteToi() {
System.out.println("Je m'appelle " + nom);
System.out.println("Je travaille à " + societe);
}
} // class Personne, version provisoire

Maintenant, si on écrit :
Personne p = new Personne();
p.nom = "durand"; // nom en minuscules : la règle B n’est pas respectée

Le compilateur refuse la seconde instruction : p.nom, privée, est inaccessible pour l’utilisateur de la classe
Personne.
Ainsi il n'est plus possible de venir changer les données de la classe (ou propriétés) directement. On pourra
changer la valeur de ces propriétés à travers des méthodes qui vérifieront la justesse des informations entrées.

mots clés : public ou private

public les variables et méthodes d’une classe CCC, définies avec le modificateur public sont accessibles partout
où CCC est accessible.
private les variables et méthodes d’une classe CCC, définies avec ce modificateur ne sont accessibles que dans la
classe CCC.

Les deux acteurs de la programmation objet


Le concepteur de la classe CCC :
est celui qui définit CCC : dans les méthodes de CCC, il a directement accès aux variables d’instance et
aux méthodes privées

Un utilisateur de la classe CCC :


est aussi un programmeur mais il n’a directement accès qu’aux variables et méthodes publiques de CCC.
Contrôlons les instanciations

L’instanciation par défaut


Avec la définition de la classe Personne de la page précédente, si nous exécutons :
Personne p = new Personne(); p.presenteToi();

nous voyons s’afficher :


Je m’appelle null Je travaille à null

Quand le concepteur de la classe n’a pas spécifié de mécanisme d’instanciation, Java en fournit un par
défaut, encore appelé constructeur par défaut.
Le constructeur par défaut initialise chaque variable d’instance vi avec une valeur par défaut :
- null si vi est de type objet,
- valeur par défaut du type ttt si ttt est un type primitif.

Définir un constructeur
class Personne {
private String nom;
private String societe;
public Personne (String leNom) {
nom = leNom.toUpperCase(); // cahier des charges, règle B
}
// etc. cf. page précédente
}

Avec le constructeur Personne(String) on peut instancier un objet Personne dont le nom est conforme aux
spécifications :
Personne p = new Personne("durand");
et on ne peut plus instancier d’objet de nom indéterminé :
Personne p = new Personne();// erreur de compilation : il n’y a plus de constructeur par défaut

Le constructeur est une des composantes importantes de la programmation objet. Toute nouvelle instance est
créée en utilisant un constructeur de clase.
Une nouvelle version de la classe
Dans cette nouvelle version, on retrouve le constructeur de la classe . Ce constructeur (même nom que la
classe) initialise les propriétés nom et societe de la classe Personne.

class Personne {
private String nom;
private String societe; // l’absence de société sera signalée par "?"
public Personne (String nom) {
// construit un objet Personne de nom invariable et de société inconnue
this.nom = nom.toUpperCase();
// qualification avec this pour distinguer la variable d'instance du paramètre
societe = new String("?");
}
public void presenteToi() {
System.out.println("Je m'appelle " + nom);
if (societe.equals("?"))
System.out.println("Je ne suis pas salarié");
else System.out.println("Je travaille à " + societe);
}
} // class Personne, deuxième version

Utilisation de this
Dans un constructeur, le mot-clef this désigne l'objet qui est construit.
Dans une méthode, ce mot-clef désigne l’objet qui traite le message :

// une autre méthode de la classe Personne


public void memeSocieteQue(Personne uneAutre) {
this.societe = uneAutre.societe; // ici, this est facultatif
}

Récapitulons
Le constructeur par défaut est encore appelé constructeur sans paramètres ou constructeur implicite.
Un constructeur est une méthode particulière, en général publique et dont l’identificateur est le même que
celui de la classe et qui est toujours définie sans type de renvoi.
Dès qu’un constructeur explicite est défini, le constructeur par défaut n’est plus disponible, sauf si le
programmeur le rétablit en définissant explicitement un constructeur sans paramètres.
Types primitifs et types objets
Dans la séquence
Personne p;
byte age; // aucune valeur de age n’est supérieure à 127
String prenom = "Delphine";
- la variable p est une référence non initialisée sur le type objet Personne,
- la variable age est une variable du type primitif byte,
- la variable prenom est une référence sur le type objet String, initialisée avec l’objet String "Delphine".

Les types primitifs : Rappels

Entiers avec signe : Réels représentés en Caractères : Valeurs logiques :


virgule flottante :
byte (8 bits), float (32 bits), char (16 bits, Unicode) boolean, deux valeurs true
short (16 bits), double (64 bits) et false.
int (32 bits),
long (64 bits).

Les types primitifs ont déjà été vus précédemment.

Variables, valeurs et affectations :

Une variable de type primitif contient sa valeur.


int a; a ?
// on définit une variable de type int

a = 245;
a 245
// on lui affecte la valeur 245
L’emplacement mémoire de la variable contient la valeur associée à la variable.
Une variable de type objet désigne sa valeur

String ch ; ch ?
// on déclare une référence de type String

ch = new String("Dupond" ) ; ch
// construction d’un nouvel objet String
// référencé par ch "Dupond"

L’emplacement mémoire de la variable contient une référence sur l’objet associé à la variable.
La valeur par défaut d’une référence est null, quel que soit le type objet associé.

Valeurs par défaut


Une variable d’instance non initialisée par un constructeur explicite est toujours initialisée par Java avec la
valeur par défaut de son type.
Toutes les autres variables ne sont pas initialisées par défaut : elles doivent obligatoirement être initialisées
par le programmeur avant la première utilisation de leur valeur.

Affectation de variables de types primitifs


L’exécution de la séquence suivante :
int a, b;
a = 2356;
b = a;
a = a + 4;
System.out.println("a vaut " + a);
System.out.println("b vaut " + b);

affichera
a vaut 2360
b vaut 2356

Les valeurs de a et b sont distinctes

Affectation d’une référence


Supposons que la classe Personne définit la méthode vaDansSociete qui permet d’affecter une valeur à la variable
d’instance societe.
L’exécution de la séquence :
Personne p1, p2;
p1 = new Personne("Meunier");
p1.presenteToi();
p2 = p1;
p2.vaDansSociete("Outils Java SA");
p1.presenteToi();
affichera
Je m’appelle MEUNIER
Je ne suis pas salarié
Je m’appelle MEUNIER
Je travaille à Outils Java SA

Les références p1 et p2 désignent le même objet. P1 Meunier


nom
Outils Java SA
societe
P2 P1 = P2
Pile d’exécution, tas d’allocation
Comme dans la plupart des langages, Java gère une pile d’exécution :
chaque méthode appelée ajoute à cette pile son environnement, c’est-à-dire ses variables locales et ses
paramètres.
Plus généralement, l’exécution d’un bloc de code empile un environnement, qui est dépilé quand l’exécution
du bloc est terminée.
Une variable de pile (variable locale ou paramètre) peut être de type primitif ou de type objet. Elle est
détruite quand son environnement est dépilé.
Un objet n’est jamais enregistré dans la pile. Il est construit dans un espace d’allocation dynamique, le tas
d’allocation, géré par le processeur Java.
Un objet ne peut être détruit tant qu’il existe au moins une référence sur lui. C’est le ramasse-miettes
(garbage collector) de la machine virtuelle Java qui récupérera l’espace que cet objet occupait : le
programmeur n’a pas à se soucier de cette opération.

Exemple d’exécution

{ // bloc A
Personne p1 = null;
int age = 54;
Meunier
{ // bloc B
Personne p2 = new Personne("Meunier"); ?
p2.presenteToi();
p1 = p2; tas
} // fin bloc B
... // etc. Bloc B P2
} // fin bloc A
Bloc A P1 54

pile
Définissons des accesseurs

Comment changer de société ?


Nous n’avons aucun moyen d’associer une société à une personne puisque la variable d’instance societe est
privée.
Il faut donc définir de nouvelles méthodes publiques dans la classe qui permettront de modifier et d’afficher la
société d’une personne.

Créer deux méthodes supplémentaires


public String taSociete() { // accès en consultation
return societe;
}

public void vaDansSociete(String entreprise) {


// accès en modification
societe = entreprise; // attention au cahier des charges !
}

Les méthodes taSociete et vaDansSociete sont respectivement des accesseurs en consultation et en modification
de la variable d'instance societe.

Intérêt des accesseurs

Gérer l'accessibilité des variables privées


on peut limiter l'accès aux données à la lecture (avec uniquement un accesseur en consultation) ou
étendre l'accès en lecture/écriture (avec deux accesseurs en consultation et en modification).

Gérer l'intégrité des données


l'accesseur en modification comporte souvent des contrôles qui permettent de valider la nouvelle
valeur de la variable.
Une classe Personne sécurisée
class Personne {
private String nom;
private String societe; // l’absence de société sera signalée par "?"
public Personne (String nom) {
// construit un objet Personne de nom invariable et de société inconnue
this.nom = nom.toUpperCase();
// qualification avec this pour distinguer la variable d'instance du paramètre
societe = new String("?");
}
public void presenteToi() {
System.out.println("Je m'appelle " + nom);
if (societe.equals("?"))
System.out.println("Je ne suis pas salarié");
else System.out.println("Je travaille à " + societe);
}

public String tonNom() { // accès en consultation


return nom;
}
public String taSociete() { // accès en consultation
if ( societe.equals("?") )
return new String("société inconnue");
else return societe;
}
public void quitteTaSociete() {
if ( societe.equals("?") ) {
presenteToi();
System.out.println("impossible de quitter la société");
System.exit(1); // arrêt de l'exécution, code d'erreur 1
}
societe = "?"; // car ici, on est sûr qu'il y a une société à quitter
}
private String valideSociete(String sNom) {
// méthode-filtre : renvoie sNom s'il représente un nom de société acceptable
if ( sNom.length() > 32 || sNom.equals("?") ) {
// en Java, || représente l'opérateur logique OU
System.out.println("classe Personne, société incorrecte : "
+ sNom);
System.exit(2); // arrêt de l'exécution, code d'erreur 2
}
// ici, on est sûr que sNom est valide
return sNom;
}
public void vaDansSociete(String entreprise) {
// avant d'aller dans une société, il faut avoir quitté la précédente
if ( ! societe.equals("?") ) {
// en Java, ! représente l'opérateur logique NON
presenteToi();
System.out.println("erreur : 1-quitteTaSociete, 2-vaDansSociete");
System.exit(1);
}
societe = valideSociete(entreprise).toUpperCase();
}
Un ou plusieurs constructeurs ?

Revenons sur le premier constructeur


public Personne (String nom) {
// construit un objet Personne de nom invariable et de société inconnue
this.nom = nom.toUpperCase();
societe = new String("?");
}

Pour instancier l'individu Meunier, de la société Outils Java,il faut exécuter :

Personne p2 = new Personne("Meunier");


p2.vaDansSociete("Outils java");

On peut souhaiter faire cette instanciation en une seule opération.

Un deuxième constructeur
public Personne (String nom, String entrep) {
// construit un objet Personne de nom fixe et de société connue
this.nom = nom.toUpperCase();
societe = valideSociete(entrep).toUpperCase();
}

Notion de signature
Pour le compilateur, il n'y a pas d'ambiguïté entre les deux constructeurs. Ainsi, l’exécution de :
new Personne("Meunier", "outils java");

fait appel au deuxième constructeur car celui-ci utilise deux chaînes en paramètre.

Plus généralement, la signature d’un constructeur ou d’une méthode comprend son identificateur, la liste et les
types de ses paramètres. Le compilateur détermine la méthode à exécuter en fonction de sa signature.
Des méthodes différentes peuvent porter le même nom, à partir du moment où leurs signatures diffèrent.
Modificateurs static et final

Améliorer la gestion des personnes sans société

class Personne {
private String pasDeSociete = "?";
private String nom;
private String societe;
... // etc.
}

Quels reproches peut-on faire à cette implémentation ?


A un instant donné, il y aura autant d'exemplaires de la constante qu'il y a d'instances existantes pour la
classe Personne.
La variable pasDeSociete peut être modifiée par toute méthode de la classe.

Une variable de classe constante

class Personne {
private static final String pasDeSociete = "?";
private String nom;
private String societe;
... // etc.
}

final ce modificateur impose que la définition comporte une valeur d'initialisation et empêche toute
affectation ultérieure d'une autre valeur. Il permet de définir une information constante.

static ce modificateur indique que toutes les instances de la classe se partageront un exemplaire
unique : une variable de classe.

Variables et méthodes de classe

Définition
On peut parfois souhaiter disposer de données communes et accessibles à toutes les instances d’une même classe.
Une variable permanente et unique pour toutes les instances d’une même classe s’appelle une variable de
classe.
Une méthode de classe représente un comportement associé à la classe elle-même et non pas à une instance
particulière de cette classe.

En Java, une variable de classe ou une méthode de classe, est définie avec le modificateur static.
class Personne {
// variables de classe
private static final String pasDeSociete = "?";
private static String nomDuFichier;
// méthodes de classe
public static void choisirFichier() {
}
// etc.
}

Exemple d'utilisation
La classe System fournit une variable de classe publique, out, que l'on exploite dans l'instruction suivante :

System.out.println("utilisation du flux de sortie");

Elle fournit également une méthode de classe publique exit :

System.exit(0);
Comment exécuter une classe ?

Définir une classe


Toute application écrite en JAVA se trouve dans un fichier source nom.java, dans lequel on trouve la classe
Nom :
Class Nom{
// variables de la classe privées ou publiques
// méthodes de la classe privées ou publiques
}

Pour qu’une classe puisse être exécutée, il faut qu’il y ait la fonction :
public static void main( String [] arg){
// code du programme
}

Créer un exécutable : 3 solutions


La solution la plus simple consiste à créer une classe TestNom (qui créé une instance de la classe Nom)ne
comportant que la fonction main :
Class TestNom{
public static void main( String [] arg){
Nom monNom = new Nom(“toto”);
MonNom.methode(); // test des différentes méthodes
}

Une autre solution consiste à placer la fonction main() dans la classe Nom et à créer une instance de la
classe Nom dans la fonction main().
Class Nom{
// variables de la classe privées ou publiques
// méthodes de la classe privées ou publiques

public static void main( String [] arg){


Nom monNom = new Nom(“toto”);
MonNom.methode(); // test des différentes méthodes
}
}

Enfin il est possible de tester les méthodes de la classe, sans faire une instanciation de la classe Nom. Il suffit
de déclarer les méthodes en static (déconseillé en général). Attention, des méthodes statiques ne peuvent
manipuler que des variables statiques.
Class Nom{
// variables statiques de la classe privées ou publiques
// méthodes statiques de la classe privées ou publiques

public static void main( String [] arg){

methode(); // test des différentes méthodes statiques


}
}

Remarque : Il est possible d’appeler une méthode dans une méthode de la classe, sans pour autant être obligé
de déclarer cette méthode en static. Seule la fonction main() demande cette condition.

Lancer un exécutable
Une fois le fichier source nom.java créé, il faut générer un fichier pseudo-exécutable nom.class (commande
javac nom .java) puis lancer cet exécutable : java nom. Il faut pour cela avoir au préalable télécharger le
jdk1.3 sur le site de http//www.sun.com.
Récapitulons…

Les variables d’instances sont généralement privées.

L’instanciation d’une classe peut se faire de 2 façons :


si la classe ne comporte pas de constructeur : il y aura une initialisation par défaut ,
s’il existe un ou plusieurs constructeurs : on n’a plus le droit de faire appel au constructeur par défaut (sauf
si un constructeur est défini sans arguments).

méthode d’instance : les méthodes sont publiques si elles doivent être appelées de l’extérieur et privées si
elles ne sont appelées que par des méthodes internes à la classe.

L’objet est un paramètre implicite de la méthode, accessible si nécessaire via la notation this. Ainsi
l’accés à une variable (ou à une méthode) se fait par variable ou par this.variable.

Il est possible de créer des variables et méthodes de classe


définies avec le modificateur static
une méthode de classe ne dispose pas de la référence this

Conventions d’écriture
Jusqu’à présent, pour des raisons pédagogiques, nous avons personnifié les messages et donné aux méthodes
des identificateurs dont le libellé illustrait le mécanisme : l’objet reçoit et traite le message :
tonNom, quitteTaSociete, etc.

Dans la pratique professionnelle, on utilise une forme moins personnifiée des notations, en utilisant plutôt
des verbes à l’infinitif
quitterSociete au lieu de quitteTaSociete
afficherEcran au lieu de presenteToi

Conseils
De même, pour les accesseurs, il est conseillé d’adopter les préfixes get et set : getNom au lieu de tonNom,
setSociete au lieu de vaDansSociete ;cette convention est celle attendue par la technologie des JavaBeans,
pour retrouver dynamiquement ces accesseurs
Version finale de la classe Personne
class Personne {
private static final String pasDeSociete = "?";
private String nom;
private String societe;

private String validerSociete(String sNom) {


// méthode-filtre : renvoie sNom s'il représente un nom de société acceptable
if ( sNom.length() > 32 || sNom.equals("?") ) {
System.out.println("classe Personne, société incorrecte : "+ sNom);
sNom=societe ;
}
return sNom; // ici, on est sûr que sNom est valide
}
// deux constructeurs pour instancier
public Personne (String nom) { // construit un objet Personne de société inconnue
this.nom = nom.toUpperCase();
societe = pasDeSociete;
}
public Personne (String nom, String entrep) {// construit un objet Personne de nom fixe et de société
connue
this.nom = nom.toUpperCase();
societe = validerSociete(entrep).toUpperCase();
}
// accesseurs en consultation
public String getNom() {
return nom;
}
public String getSociete() {
if ( societe.equals("?") )
return new String("sans emploi");
else return societe;
}
// accesseurs en modification
public void setSociete(String entreprise) {
societe = valideSociete(entreprise).toUpperCase();
System.out.println("Changement de société") ;
AfficherSurEcran() ;
}

public boolean etreSalarie() {


return !societe.equals(pasDeSociete);
}

public void quitterSociete() {


if ( societe.equals("?") ) {
System.out.println("impossible de quitter la société car sans emploi");
}
else System.out.println("Je quitte la societe :"+societe);
societe = "?"; // car ici, on est sûr qu'il y a une société à quitter
afficherSurEcran();
}
// afficher les caractéristiques d'un objet
public void afficherSurEcran() {
System.out.println("Je m'appelle " + nom);
if (!etreSalarie()) System.out.println("Je ne suis pas employé d'une entreprise");
else System.out.println("Je travaille à " + societe);
}
} // class Personne, dernière version
II - Les classes String et StringBuffer
Jusqu'à présent , nous avons utilisé la classe String pour les entrées/sorties de nos programmes. La classe
String permet de manier les chaînes de caractères. Il existe aussi une autre classe de gestion des chaînes de
caractères : la classe StringBuffer.
StringBuffer
Un objet StringBuffer est un objet non constant qui encapsule un tampon de caractères. Les principales
méthodes de la classe StringBuffer concernent la gestion du contenu et de la taille du tampon.

Instanciation d’un StringBuffer

♦ Un objet StringBuffer peut être défini à partir de sa taille:


StringBuffer tamp = new StringBuffer(40);

♦ où à partir de son contenu (une taille par défaut est allouée)


String str ="Bonjour";
StringBuffer tamp1 = new StringBuffer();
StringBuffer tamp2 = new StringBuffer(str);

Un StringBuffer permet la modification de la chaîne qu’il contient.

♦ On peut modifier le caractère à la position n


public static void main(java.lang.String[] args) {
StringBuffer s= new StringBuffer("toto");
s.setCharAt(1,'i');
s.setCharAt(3,'i');
System.out.println(s); // affiche titi
}

♦ et insérer une sous chaîne dans la chaîne


System.out.println (s.insert(0, " coucou ")); // affiche coucou titi

♦ ou bien effacer un caractère :


s.deleteCharAt(0); // affiche oucou titi

♦ On peut concaténer les chaînes de caractères avec la méthode append :


s.append(12); // affiche oucou titi12
Représentation du tampon
Le tampon s’agrandit dynamiquement en fonction des besoins.
StringBuffer sb = new StringBuffer("aeiouy");

length()
curseur de fin

a e i o u y ? ? ?

espace alloué capacity()


Attention !
ne pas confondre la capacité et la longueur.

♦ Constructeur sans paramètre :


• La capacité par défaut est 16. (place allouée en mémoire)
• La longueur vaut 0 tant que l’on n’a rien mis dedans.

♦ Méthode qui renvoie la capacité : capacity()

♦ Méthode qui renvoie la longueur : length().

Exemple
class TestStringBuffer{
public static void main(java.lang.String[] args) {
StringBuffer s= new StringBuffer("toto");
s.setCharAt(1,'i');
s.setCharAt(3,'i');
System.out.println(s);
s.insert(0,"coucou ");
System.out.println(s);
s.setLength(7);
System.out.println(s);
s.deleteCharAt(0);
System.out.println(s);
s.append(12);
System.out.println(s);
System.out.println("capacité:"+s.capacity()+"longueur:"+ s.length());
s.setLength(s.capacity());
System.out.println(s);
System.out.println("capacité:"+s.capacity()+"longueur:"+ s.length());
}
}

résultat :
titi
coucou titi
coucou
oucou
oucou 12
capacité:20longueur:8
oucou 12

Récapitulatif
Méthodes de Explications
StringBuffer
charAt(int) renvoie le caractère dont l’indice est précisé.
append(String) ajoute un ensemble de caractères à partir de l’indice référencé par le curseur de fin.
append(int) ajoute la chaine de caractère de l'entier (exemple append(12) ajoute la chaine "12")
append(float) ajoute la chaine de caractère du flottant (exemple append(12.5) ajoute la chaine
"12.5")
insert(int, String) insère une chaîne de caractères à l’indice précisé.
setCharAt(int, char) remplace le caractère dont l’indice est précisé.
reverse() inverse le sous-buffer de caractères compris entre l’indice 0 et l’indice référencé par le curseur de fin.
toString() renvoie un objet String.

Différences entre StringBuffer et String

Un objet StringBuffer n’est pas constant


Les méthodes d’instance de la classe StringBuffer qui modifient le contenu du buffer de caractères modifient
l’objet lui-même alors qu'un objet de la classe String est constant. Ainsi dans l'exemple ci-dessous, il y a
construction d'une référence sur la chaine "coucou". Puis cette référence est perdue (elle sera détruite en
mémoire par le garbage Collector ou ramasse miette).Une nouvelle référence est créée sur la chaine
"coucouAu revoir" et s reçoit cette nouvelle référence.

String s;
s= "coucou"; "coucou"
s=s+"Au revoir";
"coucouAu revoir"

Dans l'exemple ci-dessous, il y a construction d'une référence sur la chaine "coucou". La méthode append
(qui n'existe pas pour la classe String) permet la concaténation de la chaine "coucou" avec la chaine
"Aurevoir". La référence sur l'objet s n'a pas changée.

StringBuffer s;
s("coucou"); "coucouAu revoir"
s.append("Au revoir");
Comprendre l’opérateur + de la classe String.

♦ Un objet String est un objet constant : toute méthode d’instance de String qui modifie le contenu d’une
chaîne de caractères renvoie une référence sur un nouvel objet String.

♦ L’opérateur de concaténation + de String peut se comprendre ainsi :

String chaineTVA = "taux de " + "tva = " + 20.6;

String chaineTVA = new StringBuffer("taux de ").append("tva =").append(20.6).toString();

Egalité d’objets String, égalité d’objets StringBuffer


La méthode equals n’a pas été redéfinie dans StringBuffer, donc c’est celle de Object. (comparaison des
références...). Cette méthode ne permet pas de vérifier l'égalité de 2 chaines de caractères.
...
StringBuffer tampTVA1 = new StringBuffer("taux de tva");
StringBuffer tampTVA2 = new StringBuffer("taux de tva");
// tampTVA1.equals(tampTVA2) faux ou bien (tampTVA1==tampTVA2) faux
StringBuffer tampTVA0 = tampTVA2;
// tampTVA0.equals(tampTVA2) ou bien (tampTVA1==tampTVA2) vrai

♦ La méthode equals de StringBuffer compare deux objets StringBuffer...

StringBuffer s1,s2; "coucou" 2 références différentes :


s1("coucou"); donc
s2("coucou");
s1==s2 //est faux
"coucou" s1.equals(s2)//est faux

: avec les StringBuffer, on ne peut pas comparer le contenu des objets mais uniquement les objets, du coup
ils les convertir en String (toString()) afin de pouvoir comparer leur contenu.

♦ ...la méthode equals de String compare le contenu de deux objets String.

String chaineTVA1 = tampTVA1.toString();


String chaineTVA2 = tampTVA2.toString();
// chaineTVA1.equals(chaineTVA2) vrai ou bien (chaineTVA1 ==chaineTVA2) vrai

String s1,s2; "coucou" contenu des 2 chaines


s1("coucou"); équivalent donc :
s2("coucou");
s1==s2 //est vrai
"coucou" s1.equals(s2)//est vrai
Exercices

Analyse

♦ Commenter et tester ce programme.

//ce programme est tiré de Comment Programmer en Java de Deitel & Deitel chapitre 10
import javax.swing.*;

public class StringConstructeurs {


public static void main( String args[] )
{
char charArray[] = { 'a', 'n', 'n', 'i', 'v', 'e',
'r', 's', 'a', 'i', 'r', 'e' };
byte tableauOctets[] = { (byte) 'n', (byte) 'o', (byte) 'u',
(byte) 'v', (byte) 'e', (byte) 'l',
(byte) ' ', (byte) 'a', (byte) 'n' };
StringBuffer buffer;
String s, s1, s2, s3, s4, s5, s6, s7, output;
s = new String( "Bonjour" );
buffer =
new StringBuffer( "Bienvenue à la programmation Java!" );

// utiliser les constructeurs de String.


s1 = new String();
s2 = new String( s );
s3 = new String( charArray );
s4 = new String( charArray, 8, 4 );
s5 = new String( tableauOctets, 7, 2 );
s6 = new String( tableauOctets );
s7 = new String( buffer );

output = "s1 = " + s1 +


"\ns2 = " + s2 +
"\ns3 = " + s3 +
"\ns4 = " + s4 +
"\ns5 = " + s5 +
"\ns6 = " + s6 +
"\ns7 = " + s7;

JOptionPane.showMessageDialog( null, output,


"Démonstration des constructeurs de la classe String",
JOptionPane.INFORMATION_MESSAGE );

System.exit( 0 );
}
}
♦ Commenter et expliquer ce programme

//ce programme est tiré de Comment Programmer en Java de Deitel & Deitel chapitre 10
// Les méthodes charAt, setCharAt, getChars, et reverse de la classe StringBuffer.
import javax.swing.*;

public class StringBufferCar {


public static void main( String args[] )
{
StringBuffer buf = new StringBuffer( "bonjour, vous" );

String sortie = "buf = " + buf.toString() +


"\nLe caractère en 0: " + buf.charAt( 0 ) +
"\nLe caractère en 4: " + buf.charAt( 4 );

char tableauCar[] = new char[ buf.length() ];


buf.getChars( 0, buf.length(), tableauCar, 0 );
sortie += "\n\nVoici les caractères: ";

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


sortie += tableauCar[ i ];

buf.setCharAt( 0, 'B' );
buf.setCharAt( 9, 'V' );
sortie += "\n\nbuf = " + buf.toString();

buf.reverse();
sortie += "\n\nbuf = " + buf.toString();

JOptionPane.showMessageDialog( null, sortie,


"Démonstration des méthodes de caractères de StringBuffer",
JOptionPane.INFORMATION_MESSAGE );

System.exit( 0 );
}
}

Exercices

♦ Ecrire une application qui lit une date sous la forme 25/04/1955 et qui la remplace en 24 Avril 1955, et vice
versa. L'utilisateur aura le choix d'entrer la date sous une forme ou une autre.

♦ Ecrire un programme qui vient lire une suite d'octets séparés par des points et les remplace par leur
équivalent hexadécimaux :
Exemple : 1.255.10 ⇒ 1 FF A
Ce programme sera fait:
En utilisant la méthode toHexString() de la classe Integer
Integer.toHexString(255).toUpperCase() // affiche FF
Ecrire la classe IntegerP
On désire écrire la classe IntegerP. Cette classe doit nous permettre de faire les conversions :

méthodes But Exemple


IntegerP() initialiser value à 0 Integer i=new IntegerP(); // i.value=0
IntegerP(int value) initialiser this.value à value Integer i=new IntegerP(10); // i.value=10
IntegerP(String value) initialiser this.value à value Integer i=new IntegerP("10"); // i.value=10
int compareTo(IntegerP i) comparer this.value à i.value renvoie –1 si this.value est inférieur à
i.value
renvoie 0 si this.value est égal à i.value
renvoie 1 si this.value est supérieurr à
i.value
int intValue() renvoie this.value
int parseInt(String s) initialiser this.value à la valeur int x=Integer.parseInt("10"); // résultat
de s, en base 10 x=10
int parseInt(String s, int initialiser this.value à la valeur int x=Integer.parseInt("10",16); // résultat
radix) de s, en base donnée par radix x=16
String toBinaryString(int i) Renvoie la chaine binaire
équivalente
String toHexString(int i) Renvoie la chaine
hexadécimale équivalente
String toString(int value) convertir le nombre value en String s = IntegerP.toString(10);// s="10"
une String
String toString() convertir this.value en une String s = IntegerP.toString(10);// s="10"
String

Une ébauche de la classe IntegerP est donnée ci-dessous :

public class IntegerP {


private int value;
private static final char [] tabVal =
{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
IntegerP(int value){this.value=value;}
IntegerP (String value){ this.value = parseByte(s, 10);}
public static int parseInt(String s) { return parseInt(s,10);
}
public static int parseInt(String s, int radix){ // à completer
}
public static String toHexString(int i) {// à compléter
}
public static String toBinaryString(int i) {// à compléter
}
public static String toString(byte i) {// à compléter
}
}

♦ Il faudra tester que l'entier est positif.

♦ Une solution est donnée à la page suivante.


/* Creation date: (15/04/2001 18:30:30) * @author: jl Salvat */
/* classe de conversion d'entiers positives */
/* test sur les entiers non faits Donc à finir…
public class IntegerP {
private static int value;
public static final int MAX_VALUE = 0X7FFFFFFF;
public static final int MIN_VALUE = 0;
private static final char [] tabVal = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

public IntegerP() { value=0;}

public IntegerP(int value){this.value=value;}

public IntegerP (String s){ this.value = parseInt(s, 10);}

public static int compareTo(IntegerP i){


if(i.value<value) return +1; else if(i.value>value) return -11 ; else return 0;
}
public static int intValue(){return value; }

public static int parseInt(String s) { return parseInt(s,10); }

public static int parseInt(String s, int radix){ // à completer


int monRadix=radix;
int j,res=0,longChaine=s.length()-1;
if (radix>=0&&radix<=16){
for(int i=longChaine;i>=0;i--){
for (j=0;j<16;j++) if (tabVal[j]==s.charAt(i)) break;
if(i==longChaine) radix=1; else radix*=monRadix;
res+=j*radix;
}
}
else res=-1;
return res;
}

public static String toBinaryString(int i) {


value=i;
String s="";
for (int j=0;j<32;j++){
if (i<0) s+="1"; else s+="0";
if((j%4)==3)s+=".";
i<<=1;
}
return s;
}

public static String toHexString(int i) {


value=i;
int val;
String s="";
for(int j=0;j<8;j++){
val=i&0XF0000000;
val>>>=28;i<<=4;
s+=tabVal[val];
}
return s;
}
public static String toString(int i) {
value=i;
String s= "";
do{ s+=tabVal[i%10];i/=10;}
while(i!=0);
StringBuffer s1= new StringBuffer(s);
s1.reverse();
return s1.toString();
}

public static String toString(){ return toString(value);}


}

La classe StringTokenizer
On désire écrire le code de la classe StringTokenizer qui existe dans le package java.util. Cette classe
permet la découpe d'une chaine (champs str) en jetons en fonctions de caractères de délimitation (champs
delimiters). Ainsi si l'on place les délimiteurs = "\t\n\r .':", la chaine str="Bon jour.c'est:moi toto" sera
découpée en 6 jetons :
"Bon" "jour" "c" "est" "moi" "toto".

Exemple :
import java.util.*;
class Test{
public static void main(String [] arg){
String chaineADecouper = new String( "ceci est une test");
StringTokenizer jetons = new StringTokenizer( chaineADecouper );
System.out.println( "Nombre d'éléments: " + jetons.countTokens() + "\nLes jetons sont:\n" );

while ( jetons.hasMoreTokens() )System.out.println( jetons.nextToken() + "\n" );


}
}

Résultat:

Nombres d'éléments : 4
Les jetons sont :
ceci
est
un
test

La classe StringTokenizer :

class StringTokenizer{
private int currentPosition;
private int maxPosition;
private java.lang.String str;
private java.lang.String delimiters;

public StringTokenizer(String str) {


this(str, " \t\n\r\f"); }

public StringTokenizer(String str, String delim) {


currentPosition = 0;
this.str = str;
maxPosition = str.length();
delimiters = delim;
}

public int countTokens() { // à compléter


}

public boolean hasMoreTokens() { // à compléter


}

public String nextToken() { // à compléter}


}
III - Heritage
La classe Individu

Cahier des charges

Un individu réside en France, sur le continent.


Un individu a un nom, invariable, qui est enregistré en majuscules.

Un individu peut avoir un département de résidence ou de travail, qui sera lui aussi invariable. Ce
département est un entier de 1 à 95.

Si le département d’un individu est inconnu, on le représentera par la valeur 0.

Première version de la classe


class Individu {
private String nom; // nom de l'individu
private byte departement; // département de résidence

private void setDepartement(int unDept) { ... // ici on rejette tout numéro de département incorrect
departement = (byte) unDept;
}
public Individu (String leNom, int leDept) {
nom = leNom.toUpperCase();
setDepartement(leDept);
}
// On définit un constructeur par défaut
public Individu () {
this("nom inconnu",0);//appel au constructeur Individu(String leNom,int leDept)
}
public void afficherSurEcran() {
System.out.println("Je m'appelle " + nom);
if (departement != 0)
System.out.println("j'habite dans le " + departement);
}
} // class Individu

Exercice :

Changer la fonction setDepartement pour ne ranger dans la donnée departement que des numéros
compris entre 0 et 95.
La classe Employe

Cahier des charges


Un employé est un individu qui travaille dans une société.
Son cahier des charges reprend donc celui de la classe Individu.

Un employé a un nom et un département.

Si le département est inconnu, on le représentera par la valeur 0.

Un employé a une société qui peut changer. Le nom de la société doit être en majuscules.

Héritons la classe Employe de Individu


Un employé est un individu qui travaille dans une société

La classe Employe reprend toutes les ..., en les complétant


propriétés de Individu, ...

Première version de la classe Employe


Le mot clé extends (qui veut dire hérite) permet de spécifier le nom de la classe mère. La classe fille
(Employe) hérite des propriétés et méthodes de la classe Individu.

class Employe extends Individu { // extends indique l'héritage


private String societe;
public void setSociete(String uneEntreprise) {
societe = uneEntreprise.toUpperCase(); // toUpperCase() :renvoie une chaine en majuscule
}
public Employe (String laSociete) {
setSociete(laSociete);
}
} // class Employe

La classe Employe hérite de la classe Individu.


La classe Employe hérite des propriétés de la classe Individu. On pourra donc exécuter la séquence suivante
:

Employe e = new Employe("CNAM");


e.afficherSurEcran(); // appel de la méthode définie dans Individu
Héritage

Définition
Une classe représente une famille d’objets ayant les mêmes propriétés et les mêmes méthodes.

L’héritage permet de reprendre les caractéristiques d’une classe existante MMM pour les étendre et définir
ainsi une nouvelle classe FFF qui hérite de MMM. La classe MMM est la classe mère et FFF est la classe
fille.
C la s s e m è r e
classe fille extends mere
h é rita g e {
// code
C la s s e f ille
}

Règles d’héritage en Java

Une classe fille ne peut hériter que d’une seule classe mère. On parle d'héritage simple. (En C++, il existe la
notion d’héritage multiple, c’est à dire une classe fille hérite de plusieurs classes mères).

Plusieurs classes filles peuvent hériter d’une même classe mère.

Une classe fille peut elle-même être la classe mère d’une nouvelle classe. Plus généralement la classe C1 est
une superclasse de C2 (ou C2 est une sous-classe de C1) si C2 descend de C1, par une liaison d’héritage.

Individu

Employe Patron

Cadre Ouvrier Exemple : notion d'héritage

Ici la classe Individu qui possède un nom et un numéro du département (où vit l'individu) est la classe mère (ou
superclasse) des classes Employe et Patron qui possèdent en plus des champs et méthodes de la classe Individu,
les champs societe (String Societe pour l'employé et String[] societe pour le patron qui peut posséder plusieurs
sociétés). La classe Employe peut elle-même être la classe mère des classes Cadre et Ouvrier.

A la définition de la classe, on utilise le mot-clé extends pour désigner la classe-mère. Par défaut, si le mot-
clé extends est omis, la classe hérite de la classe Object.

La classe Object est la superclasse de toutes les classes : en Java, la hiérarchie de classes est une arborescence
de racine unique.
Héritage et constructeurs

Appel implicite au constructeur d'Individu


Quand on exécute la séquence suivante,

Employe e = new Employe("CNAM");


e.afficherSurEcran();

L'objet e se présente comme s'il était un Individu car il hérite de la méthode afficherSurEcran. On obtient
l’affichage :

Je m'appelle nom inconnu

Sans qu'on l'ait précisé dans le constructeur de Employe, la variable d’instance nom a pris la valeur "nom
inconnu". En effet, pour construire l’instance de Employe, le compilateur appelle d’abord le constructeur par
défaut de la classe Individu :

public Individu () {
nom = "nom inconnu";
departement = 0;
}

Si on supprimait ce constructeur de la classe Individu, on aurait alors, pour new Employe("CNAM"), le message
d'erreur suivant :
no constructor matching Individu() found in Individu

Le compilateur ne saurait plus comment construire le sous-objet Individu.

Un constructeur fonctionnel pour la classe Employe


Comment initialiser le nom d’un employé ?
On ne peut pas manipuler directement la variable nom dans une méthode de Employe, car elle est définie
private dans la classe mère.
En revanche, on peut appeler explicitement un constructeur de la classe mère dans le constructeur de
Employe, car celui-ci est public.

public Employe (String leNom, String laSociete) {


super(leNom, 0);
setSociete(laSociete);
}

Remarque : super est le mot-clé pour désigner l'appel au constructeur de la super classe (ou classe mère).
Redéfinir une méthode héritée

La méthode afficherSurEcran pour la classe Employe


Si la classe mère M définit la méthode mmm pour la propriété ppp, une classe fille F de M peut souhaiter
un comportement différent pour ppp :la classe F redéfinira la méthode mmm.

Redéfinissons la méthode afficherSurEcran pour la classe Employe

class Employe extends Individu {


...
public void afficherSurEcran() {
super.afficherSurEcran(); // appelle la méthode afficherSurEcran de Individu
System.out.println(" je travaille chez " + societe);
}
}

Dans la redéfinition d'une méthode mmm, il est conseillé d'appeler la méthode mmm héritée (sauf si l'on veut
supprimer le comportement hérité). Ainsi, le mécanisme de l'héritage facilite la gestion des mises à jour de
mmm.
Mot clé final
Le mot clé final selon qu’il est appliqué à une donnée ou à une méthode n’a pas le même sens :

Le modificateur final empêche la redéfinition d’une méthode dans les classes dérivées.

Le mot clé final appliqué à une donnée déclare cette donnée comme constante.

On peut tout de même trouver une ressemblance entre les 2 sens du mot clé final :
Une fois qu’une variable a été déclarée en final, celle-ci ne peut plus être changée.
Une fois qu’une méthode a été déclarée en final, celle-ci ne peut plus être changée (par une
redéfinition dans sa classe fille.

Exemple :
Class EssaiMere {
Class Essai1 { public final void methfinal(){
public static final char NL = '\n'; System.out.println(“classe mere”);
public static final int AS = 1; }
public static final int VALET = 11; }
public static final int DAME = 12;
// declaration des methods
Class EssaiFille extends EssaiMere{
}
Public final void methfinal(){
// erreur de compilation
Expliquer le sens des mots clés static ajoutés aux }
mots clés final (Cf Chapitre 2.16)
Les références this et super ?

La référence this permet à un objet de se désigner lui-même.

La référence super permet à un objet de référencer les variables et les méthodes de sa classe-mère.

Dans un constructeur, on peut appeler un autre constructeur :


- de la même classe par this(...),
- de la classe-mère par super(...).

Exemple : On désire créer une classe Point et une classe PointCol qui hérite de Point (point coloré)

Class Point { Class PointCol extends Point {


int x,y; String color ;
Point(){ PointCol(){
x=y=0; super();// appel à Point()
} color= »incolore » ;
}
Point(a,y){
x=a;// équivalent à this.x=a PointCol(int x, int y, String s){
this.y=y ; super(x,y) ;// appel à point(x,y)
// Pour lever l’ambiguïté il est obligatoire de mettre this.y=y color =s; // équivalent à this.color=s ;
} }
} }

Remarque:
Il n'y a pas d'héritage pour les variables de la classe fille ayant le même nom que ceux de la classe
mère. Dans ce cas, les variables de la classe fille cache les variables de la classe mère. Dans le cas des
méthodes, les méthodes de la classe fille surchargent celles de la classe mère.

Supposons les classes mères et filles suivantes :

class Mere {
Number aNumber;
}
class Fille extends Mere {
Float aNumber;
}

La variables aNumber cache la variable aNumber de la classe Mere. Cependant il est possible dans la
classe fille (Fille) de lire la variable aNumber de la classe mère. Il suffit d'utiliser le mot clé super. super
permet de préciser la référence de l'objet comme étant celle de la classe mère.

super.aNumber ; // permet d'accéder à la variable Mere.aNumber


Accès aux propriétés héritées

Accessibilité d'une méthode ou d’une variable private


On veut maintenant donner la possibilité de gérer le changement de département.
On définit alors, pour la classe Employe, la méthode :

public void changerDepartement(int unDept) {


setDepartement(unDept); //erreur, car setDepartement est private dans Individu
}

Le compilateur signale une erreur de compilation. En effet, la méthode setDepartement de Individu, parce
qu'elle est private, est inaccessible, même pour les sous-classes de Individu.

De même, pour les propriétés nom et departement de la classe Individu

class Individu {
private String nom; // nom de l'individu
private byte departement; // département de résidence
// Cf CH 3.1 pour le détail des méthodes
}
Ces propriétés ne sont pas accessibles dans les méthodes de la classe Employe (classe fille de la classe
Individu).

Le modificateur d'accès protected


Adaptons la classe Individu pour l'héritage :

class Individu { // 2ème version


...
protected void setDepartement(int unDept) {
...
}
...
} // class Individu

Le modificateur protected permet de conserver une protection semblable à celle assurée par le modificateur
private, tout en assurant l'accès dans les classes héritées et les classes du même package.
Remarque : Une bonne conception objet ne doit jamais modifier une classe mère pour les besoins de la
définition d’une classe fille. Le concepteur de la classe mère doit prévoir les besoins de l’héritage et définir
les accès.
en conséquence. Souvent d’ailleurs, le choix d’un accès protected correspond à une solution de facilité, qui
n’est pas forcément la meilleure.
Il aurait été plus judicieux dans notre exemple d'utiliser directement la fonction
setDepartement héritée de la classe individu . En effet la fonction ChangerDepartement n'apporte rien de
plus.

class Individu { class Employe extends Individu {


private String nom; // nom de l'individu private String societe;
private byte departement; // département
private static final byte MAX_DEPT=95 ; protected void setSociete(String
private static final byte MIN_DEPT=1; uneEntreprise){
societe = uneEntreprise.toUpperCase();
protected void setDepartement(int unDept) { }
if(unDept>=MIN_DEPT&&unDept<=MAX_DEPT) public Employe (String leNom, String
departement=(byte) unDept ; laSociete) {
else System.out.println(« Erreur this(leNom,laSociete,0);
saisie ») ; }
} public Employe(String leNom, String
laSociete, int leDept) {
public Individu (String leNom, int leDept) { super(leNom, leDept);
nom = leNom.toUpperCase(); setSociete(laSociete);
if (leDept != 0) setDepartement(leDept); }
} public void afficherSurEcran() {
super.afficherSurEcran();
// On définit un constructeur par défaut System.out.println("je travaille chez "
public Individu () {
+ societe);
nom = "nom inconnu";
}
departement = 0;
}
public static void main( String []a){
Employe e=new Employe("toto","IUT");
public void afficherSurEcran() {
e.setDepartement(06);
System.out.println("Je m'appelle " + nom);
e.afficherSurEcran();
if (departement != 0)
}
System.out.println("j'habite dans le " +
}
departement);
}
} // class Individu

Le résultat du programme est donné ci-dessous :


Je m'appelle TOTO
j'habite dans le 6
je travaille chez IUT

Remarques : il a fallu déclarer la méthode setDepartement de la classe Individu en protected . En effet si


l’on avait eu la méthode déclaré en private : private void setDepartement(int unDept) on aurait eu
l’erreur de compilation suivante :
The method setDepartement is not visible

la méthode setSociete de la classe Employe est déclaré en protected (au cas ou on aurait à hérité de la
classe Employe ,elle reste visible dans la classe fille).
L’appel au constructeur dans un constructeur ne peut se faire qu’avec le mot clé this :
public Employe (String leNom, String laSociete) {
this(leNom,laSociete,0); // appel au constructeur Employe(leNom,laSociete,0)
}
protected ou private ?

Choisir le modificateur d’accès adéquat


Supposons que la classe CCC doit être implémentée avec les contraintes suivantes :
CCC définit une variable d'instance maVar, qui doit rester inaccessible aux utilisateurs de la
classe,
CCC peut avoir des sous-classes qui doivent avoir accès à maVar.

Etudions deux manières différentes de satisfaire ces contraintes.

Utilisation du modificateur protected : dans ce cas la variable maVar reste visible dans les classes filles.

class CCC {
protected typeVar maVAR;
// MAVar visible uniquement dans les sous classes de CCC
...
} // class CCC

Utilisation d'accesseurs : dans ce cas la variable maVar est invisible. Par contre l’accés à cette variable
peut se faire par l’intermédiaire de ses accesseurs.

class CCC {
private typemaVar maVar; // maVar n’est pas accessible directement hors de CCC
...
private final typeMaVar valideMaVar(typeMaVar v) { // contrôle la validité pour maVar
... // tests de validité : v est-elle une valeur acceptable pour maVar ?
return v;
}
protected typeMaVar getMaVar() { // accesseur en consultation
return maVar;
}
protected void setMaVar(v) { // accesseur en modification
this.maVar = valideMaVar(v);
}
} // class CCC

La deuxième solution est plus contraignante pour le concepteur de la classe. Mais elle permet un contrôle de
validité sur les variables d'instances, et assure ainsi une plus grande sûreté dans l'utilisation de la classe.
Version finale de la classe Employe
class Employe extends Individu {
protected String societe; // nom de la société où l'employé travaille

protected void setDepartement(int unDept) {


if(unDept>=MIN_DEPT&&unDept<=MAX_DEPT)
departement=(byte) unDept ;
else System.out.println(« Erreur saisie ») ;
}
public Employe (String leNom, String laSociete) {
super(leNom, 0);
setSociete(laSociete);
}
public Employe(String leNom, String laSociete, int leDept) {
super(leNom, leDept);
setSociete(laSociete);
}

public void afficherSurEcran() {


super.afficherSurEcran();
System.out.println(" je travaille chez " + societe);
}

public static void main(String args[]){


// exemples de tests unitaires
Employe employe;
employe = new Employe("Meunier", "CNAM");
employe.afficherSurEcran();
// employe.departement = 93;
// erreur de compilation, departement est inaccessible
employe.changeDepartement(93);
employe.setSociete("Java sa");
employe.afficherSurEcran();
} // main
} // class Employe

Exercices

Reprendre la version finale de la classe Personne du chapitre II - 18.


class Personne {
private static final String pasDeSociete = "?";
private String nom;
private String societe;

private String validerSociete(String sNom) {


// méthode-filtre : renvoie sNom s'il représente un nom de société acceptable
if ( sNom.length() > 32 || sNom.equals("?") ) {
System.out.println("classe Personne, société incorrecte : "+ sNom);
sNom=societe ;
}
return sNom; // ici, on est sûr que sNom est valide
}
// deux constructeurs pour instancier
public Personne (String nom) { // construit un objet Personne de société inconnue
this.nom = nom.toUpperCase();
societe = pasDeSociete;
}
public Personne (String nom, String entrep) {// construit un objet Personne de nom fixe et de société
connue
this.nom = nom.toUpperCase();
societe = validerSociete(entrep).toUpperCase();
}
// accesseurs en consultation
public String getNom() {
return nom;
}
public String getSociete() {
if ( societe.equals("?") )
return new String("sans emploi");
else return societe;
}
// accesseurs en modification
public void setSociete(String entreprise) {
societe = valideSociete(entreprise).toUpperCase();
System.out.println("Changement de société") ;
AfficherSurEcran() ;
}

public boolean etreSalarie() {


return !societe.equals(pasDeSociete);
}

public void quitterSociete() {


if ( societe.equals("?") ) {
System.out.println("impossible de quitter la société car sans emploi");
}
else System.out.println("Je quitte la societe :"+societe);
societe = "?"; // car ici, on est sûr qu'il y a une société à quitter
afficherSurEcran();
}
// afficher les caractéristiques d'un objet
public void afficherSurEcran() {
System.out.println("Je m'appelle " + nom);
if (!etreSalarie()) System.out.println("Je ne suis pas employé d'une entreprise");
else System.out.println("Je travaille à " + societe);
}
} // class Personne, dernière version
Personne

private String nom ;


private String societe ;
private String numSecu;
private static final String pasDeSociete = "?";
public Personne (String nom)
public Personne (String nom, String Entreprise)
public Personne (String nom, String Entreprise, String NumSecu);

public int getAge();


public boolean isMasculin();
public String getDateNaissance();
public String getInfo();
public void setNom(String s);
public String getNom();
public void setSociete(String societe);
public String getSociete();
public void quitterSociete()
public void setNumSecu(String numSecu);
public String getNumSecu();
public boolean etreSalarie() {

private String validerSociete(String sNom);


private String valider NumSecu( String numSecu);

On désire rajouter une donnée numSecu qui comporte 13 numéros. Par exemple : "1681046068948"

On s’intéresse aux 5 premiers numéros qui correspondent respectivement


au sexe de la personne :1 :sexe masculin, 2 : sexe féminin
à l’année de naissance : 68 : 1968,
et au mois de naissance 10 : Octobre

Travail à faire :

écrire la méthode validerNumSecu(String num) qui permet de tester la validité du


numéro de sécu entré (13 numéros entrés, premier chiffre égal à 1 ou 2 et mois de
naissance compris entre 1 et 12). Pour cela on utilisera les méthodes de la classe String.

changer les constructeurs pour prendre en compte le numéro de sécu.

écrire les accesseurs boolean isMasculin()et int getAge() et setNumSecu(String num).


Pour la méthode getAge(), vous utiliserez la classe Calendar pour récupérer la date
courante.

ecrire la méthode String getInfo() qui renvoie le le nom, le sexe, l’age, la société

Calendar car = Calendar.getInstance();

int year = car.get(Calendar.YEAR); // lire l’année en cours


int month = car.get(Calendar.MONTH) + 1; // lire le mois en cours
Créer la classe NumSecu
NumSecu
private String numSecu;
public int age;
public String dateNaissance
public boolean Masculin

public numSecu (String numSecu);


public void setNumSecu(String numSecu);
private String validerNumSecu( String numSecu);
public String getNumSecu();

Cette classe met à jour ses champs lors de l'appel au constructeur. Ses champs étant public, il est alors facile
de connaitre l'age, la date de naissance (sous la forme "MM/AAAA")

nouvelle classe Personne qui utilise la classe numSecu

Ecrire la classe personne qui utilise la classe numSecu

Cette nouvelle classe possède les mêmes méthodes. Les méthodes getAge, ismasculin, getDateNaissance, le
constructeur Personne et setNumSecu utilise les propriétés de l'objet numSecu et le constructeur de la classe
NumSecu.

Personne

private String nom ;


private String societe ;
private NumSecu
numSecu;
public Personne (String nom)
public Personne (String nom, String Entreprise)
public Personne (String nom, String Entreprise, String NumSecu);

public int getAge();


public boolean isMasculin();
public String getDateNaissance();
public String getInfo();
public void setNom(String s);
public String getNom();
public void setSociete(String societe);
public String getSociete();
public void quitterSociete()
public void setNumSecu(String numSecu);
public String getNumSecu();
public boolean etreSalarie() {

private String validerSociete(String sNom);


Hériter de la classe Personne

Personne
private String nom ;
private String societe ;
private NumSecu
numSecu;
public Personne (String nom)
public Personne (String nom, String Entreprise)
public Personne (String nom, String Entreprise, String NumSecu);

public int getAge();


public boolean isMasculin();
public String getDateNaissance();
public String getInfo();
public void setNom(String s);
public String getNom();
public void setSociete(String societe);
public String getSociete();
public void quitterSociete()
public void setNumSecu(String numSecu);
public String getNumSecu();
public boolean etreSalarie() {

private String validerSociete(String sNom);

Ouvrier Cadre Patron


private int dateEntre ; private int Indice ; private int cA ;
private static final float SMIG= private byte
6250,90; public Cadre (String nom) pourcentage
public ; (String
Patron nom)
public Ouvrier (String nom) public Cadre (String nom, public Patron (String nom,
public Ouvrier (String nom, String Entreprise) String Entreprise)
String Entreprise) public Cadre (String nom, public Patron (String nom,
public Ouvrier (String nom, String Entreprise, String String Entreprise, String
String Entreprise, String NumSecu); NumSecu);
NumSecu); public float getSalaire(); public float getSalaire();
public float getSalaire(); public String getInfo(); public String getInfo();
public String getInfo();

Créer la classe Ouvrier, la classe Cadre et la classe Patron qui héritent de la classe Personne. et prévoir les
constructeurs (à 3 et à 0 arguments) de chacune des 3 classes,

Le patron a un salaire qui est égal à x% du chiffre d'affaire : salaire = cA*pourcentage/100

Le cadre a un salaire qui dépend de son indice :


E1 : salaire annuel brut 130.000,00 F,
E2 : salaire annuel brut 150.000,00 F,
E3 : salaire annuel brut 170.000,00 F,
E4 : salaire annuel brut 200.000,00 F,
L’ouvrier a un salaire qui est salaire = SMIG + (age-18)*100 + ( dateCourante - dateEntree)*150. De plus, le
salaire ne doit pas dépasser SMIG*2.

Ecrire la méthode float getSalaire() qui permet de calculer le salaire pour chacune des classes.

Ecrire la méthode String getInfo() qui renvoie le nom, le sexe, l’age, la société , le salaire et le poste occupé
(On pourra utiliser la méthode getInfo() de classe mère et rajouter l'information du poste occupé).

Le programme principal devra créer 1 patron et 2 cadres aux indice E1 et E3 et 5 ouvriers. Tester les
différentes fonctions.

Afficher les informations concernant les différents employés. Peut-on utiliser une boucle for ?

Notion de polymorphisme : dans le programme de test taper les informations suivantes :

Personne e [ ] = new Personne[8];


e[0]=new Patron(“boss”,”AutourDuWorld”,1560206789086,1457000,2);
e[1]=new Cadre(“Arthur”, ”AutourDuWorld”,1600206099086,2);
e[2]=new Cadre(“Roger”, ”AutourDuWorld”, 2700206092389,1);
e[3]=new Ouvrier(“bob”, ”AutourDuWorld”, 1501206099086,80);
e[4]=new Ouvrier(“bobe”, ”AutourDuWorld”, 2591206099086,80);
e[5]=new Ouvrier(“bobi”, ”AutourDuWorld”, 1701105609086,90);
e[6]=new Ouvrier(“bobo”, ”AutourDuWorld”, 1551208909086,80);
e[7]=new Ouvrier(“bobob”, ”AutourDuWorld”,1501206099086,99);
for(int i=0;i<8;i++) System.out.println(e[i].getInfo());
// attention e[i].getSalaire() entraine une erreur de compilation puisque cette fonction n’existe pas dans la classe Personne

Si l’on essaie de comprendre ce qui s’est passé : à l’appel de la méthode getInfo() de la classe mère, Java
cherche la référence de l’objet e[i]. Si cette référence correspond à une classe fille (les classes Ouvrier,
Cadre ou Patron) alors il y a appel de la méthode de la classe fille. On dit que l’édition de lien est
dynamique (polymorphisme) puisque le choix de la méthode n’est pas fait lors de la compilation mais
au moment de l’appel de la méthode.

L’intérêt du polymorphisme est évident dans cet exemple puisque au lieu d’appeler 8 fois la méthode
getInfo() pour chaque objet, il suffit de faire l’appel dans une boucle.
Redéfinir une méthode : récapitulons

Si Fille est une classe qui hérite de Mere, une méthode m de Fille qui redéfinit m de Mere doit avoir la même
signature et le même type de renvoi :

class Mere {
public int renvoieCinq() { return 5; }
... // etc.
} // class Mere

class Fille extends Mere {


public String renvoieCinq() { return "cinq"; }
// Erreur de compilation : method redefined with different return type
} // class Fille

Une méthode private est implicitement final. Si on redéfinit les méthodes renvoieCinq comme suit :

class Mere {
private int renvoieCinq() { return 5; }
... // etc.
} // class Mere

class Fille extends Mere {


private String renvoieCinq() { return "cinq"; }
// OK, ce n’est pas une redéfinition
} // class Fille

le compilateur Java ne considère pas la méthode renvoieCinq de Fille comme une redéfinition de
renvoieCinq de Mere, mais comme une nouvelle méthode.
Modificateurs d’accès
Il est possible d'élargir l'accès aux méthodes et aux variables d'instance à travers l'héritage.

Une méthode private n'est jamais accessible par les sous classes et ne peut donc pas être redéfinie (au sens
objet du terme)

Une méthode protected peut être redéfinie protected ou public (élargissement de l'accès)

Une méthode public ne peut être redéfinie que public

Redéfinitions multiples
Quand une méthode mmm est redéfinie plusieurs fois, on ne peut, dans une redéfinition, accéder qu’à la
redéfinition immédiatement supérieure (via super).
En revanche, pour une variable d’instance plusieurs fois redéfinie, on peut avoir accès aux différentes
versions.
Le polymorphisme

La classe Fille qui hérite de la classe Mere prend les caractéristiques de cette dernière tout en ayant des propriétés en
plus
mMere()
Classe Mere
mRedef()
héritage

Classe Fille mRedef()


mFille()

Revenons au cas classique où l'on crée 2 objets objM et objF en utilisant les constructeurs de chacun des 2:

Class Mere { Class Fille extends Mere {


int a ; int b;
public Mere(int a){ this.a=a;} public Fille(int a,int b){ super(a) ;
public void mMere(){} this.b=b ;}
public void mRedef(){ public void mFille(){}
System.out.println("Redef de fille"); } public void mRedef(){
} System.out.println("Redef de mère"); }
}
Mere objM = new Mere(3); // OK
Fille objF = new Fille(2,10); // OK

Tas d'allocation
mRedef() mRedef()
mMere() mFille()
3 2
Table de Table de 10
Pile
Mere (3); fonction de fonction de
objM objF
Mere Fille Fill (2 10)
Pile

Le compilateur crée dans le tas d'allocation les variables de chaque objet et la référence des ses fonctions
membres (sauf si l'on précise le mot-clé final avant les fonctions).

Que se passe-t-il si l'on tape le code suivant ?


Mere objM;
Fille1= new Fille(2,10);
objM=Fille1; // ou bien Mere objM = new Fille(2,10);
Fille objF = new Fille(2,10);

à l'exécution
mRedef() mRedef()
mMere() mFille()
3 2
Pile Table de Table de 10
Mere (3); fonction de fonction de objF
objM
Mere Fille Fill (2 10)
Pile

le compilateur regarde les fonctions définies dans Mere et Fille. Pour ces fonctions, la connexion se fera lors
de l'exécution . Pour le reste, le compilateur place les références des fonctions. Ainsi les fonctions disponibles
sont mMere() et mRedef() pour l'objet objM.

De plus, seule la variable a est initialisé à 3 (la variable b appartient à Fille et est donc perdue).

de Fille vers Mere


L’héritage définit un cast implicite de Fille vers Mere

Donc l’exécution suivante est possible :


Mere objM = new Fille(2,10);
objM.mRedef(); // fonction de Fille
objM.mMere(); // fonction non définie dans Fille donc fonction de mère
System.out.println("valeur de a :"+objM.a) ; // affiche 3

Par contre il y aura erreur de compilation si l'on essai d'accéder au champs b :


System.out.println(" valeur de b :"+objM.b) ;// erreur :the fied name b for objM is not defined
Definition du polymorphisme
Le polymorphisme est une édition de lien dynamique. Ainsi l'appel à une méthode définie dans la
classe mère et dans la classe fille ne sera pas figé à la compilation. C'est seulement à l'exécution que le code
de la méthode désirée sera exécutée.

Mere objM = new Mere(3); Mere objM = new Fille(2,10);


objM.mRedef(); // fonction de Mere objM.mRedef(); // fonction de Fille

De Mere vers Fille


Si l'on veut accéder à la fonction mFille() il faudra utiliser un cast explicite :

Fille fille= new Fille(2,10);


Mere objM = fille;
objM.mFille(); // erreur de compilation
((Fille) objM).mFille(); // OK, le cast « rassure » le compilateur

Peut-on forcer l’exécution de mRedef de la classe Mere ?

Mere objM = new Fille();


((Mere) objM).mRedef(); // OK

Attention :Un objet Fille peut être considéré comme un objet Mere. Mais pas l’inverse

Fille objF = new Mere(2); // erreur de compilation

Exemple :
public class TestMereFille {
public static void main (String agrg[]){
Mere mere = new Mere(3);
Fille fille = new Fille(2,5);
mere= fille;
System.out.println("mere.a= "+mere.a);
mere.mRedef();
// System.out.println("mere.b= "+mere.b); Erreur de compilation
mere = new Mere(3);
// fille= (Fille) mere; //Erreur à l'exécution
mere.mRedef();
System.out.println("fille.a= "+fille.a);
System.out.println("fille.b= "+fille.b);
fille.mRedef();
((Mere)fille).mRedef();
}
}

mere.a= 2
Redef de fille
Redef de mère
fille.a= 2
fille.b= 5
Redef de fille
Redef de fille
Notion de classe abstraite

Reprenons notre classe Personne


Si l’on reprend l’exercice 13.4, il est évident qu’il y a 2 méthodes de la classe Personne qui seront
définis dans les classes filles :ce sont les classes getSalaire() et getInfo(). On pourrait donc préciser que la
classe Personne est une classe abstraite, c’est à dire une classe qui ne place pas de code dans certaines de ses
méthodes. Ce code devra être implémenté dans les classes filles.La classe personne possède une classe
getInfo() non abstraite et une classe getSalaire() abstraite.

abstract
Personne float getSalaire() ;
private String nom ;
private String societe ; fonctions sans code ;
private String numSecu;
String getInfo() { //

Ouvrier Cadre Patron


private int dateEntre ; private int Indice ; private int cA ;
private static final float SMIG= private byte pourcentage ;
6250,90; float getSalaire() { // float getSalaire() { //
code} float getSalaire() { //
code} code}

Exemple : un nouveau cahier des charges

Un complexe agro-alimentaire gère des bovins, porcins et ovins.

Chaque bovin est caractérisé par un nom, un identifiant et un poids,


chaque porcin par un identifiant et un poids,
chaque ovin par un identifiant, un poids, et une date de tonte.

Chaque animal est alimenté par une ration journalière calculée différemment selon qu’il s’agit d’un bovin,
d’un porcin ou d’un ovin.

On veut modéliser chaque population d’animaux en définissant une classe. Comment gérer les similitudes
entre les trois populations ?

Quelles propriétés communes ?


Lors de l’analyse du cahier des charges, il est important de comprendre les propriétés communes à
toutes les classes :

des variables d’instance : identifiant, poids

une méthode descriptive toString, qui variera d’un animal à l’autre, avec une partie commune aux trois classes

une méthode definirRation, qui affiche la description de la ration journalière à attribuer à un animal : les trois
formes de cette méthode sont totalement distinctes les unes des autres.
La classe abstraite Animal : cette classe sera le point de départ de toutes les classes
abstract class Animal {
protected String identifiant;
protected int poids;
public Animal (String identifiant, int poids) {
this.identifiant = identifiant; this.poids = poids;
}
public String toString() {
return Identifiant + poids;
}
public abstract int definirRation();
} // class Animal
Hériter d’une classe abstraite

La classe Animal

La classe Animal n’est pas instanciable

Elle déclare la méthode abstraite definirRation : toute sous-classe de Animal devra définir cette méthode
pour être instanciable.

Les classes Bovin et Porcin

class Bovin extends Animal {


private String nom;
public Bovin (String nom, String identifiant, int poids) {
super(identifiant, poids); // constructeur de la classe abstraite
this.nom = nom;
}
public String toString() {
return nom + super.toString();
}
public int definirRation() { // définition de la méthode abstraite
... // etc. implémentation pour un bovin
}
} // class Bovin

class Porcin extends Animal {


public Porcin(String identifiant, int poids) {
super(identifiant, poids); // constructeur de la classe abstraite
}
// pour cette classe, la méthode toString héritée convient
public int definirRation() { // définition de la méthode abstraite
... // etc. implémentation pour un porcin
}
} // class Porcin
Récapitulatif
Solution 1 : Il aurait été possible de définir 3 classes Bovin , Porcin et Ovin dans lesquelles, on peut voir des
propriétés et méthodes communes. Cette solution n’apparaît pas des plus judicieuses !

Bovin Porcin Ovin


identifiant identifiant identifiant
poids poids poids
nom date tonte
definirRation() definirRation() definirRation()

Solution 2 : On utilise une coquille à moitié pleine qui nous servira de moule pour l fabrication des autres
classes. Cette classe abstraite nous permet de définir des propriétés communes et surtout les méthodes
communes (avec le même code) et les méthodes abstraites (fournis sans le code, à définir dans les classes
filles).
definirRation()
identifiant
toString()
poids
Animal

Bovin Porcin Ovin


nom date tonte
definirRation() definirRation() definirRation()

la classe Animal est une classe abstraite et ne peut donc pas être instanciée,

definirRation() est une méthode abstraite, ce comportement doit être implémenté dans chaque sous-classe,
pour que celle-ci soit instanciable.

toString est une méthode non abstraite, c’est un comportement par défaut hérité par les sous-classes, mais elle
peut être redéfinie par les sous-classes

Remarque : une méthode avec le modificateur abstract ne doit pas être implémentée dans la classe de base
mais doit l’être dans les sous-classes
Exemple d’implémentation de la classe Animal et des classes Ovin, Bovin
abstract class Animal {
protected String identifiant;
protected int poids;
public Animal (String identifiant, int poids) {
this.identifiant = identifiant; this.poids = poids;
}
public String toString() {
return Identifiant + poids;
}
public String getIdentifiant(){ return identifiant;}
public int getPoids(){ return poids;}
pubic void setIdentifiant(String s){identifiant = s;}
public void setPoids(int poids){ this.poids=poids;}

public abstract int definirRation();


} // class Animal

class Bovin extends Animal {


private String nom;
private static final int INDICE=10 ;

public Bovin (String nom, String identifiant, int poids) {


super(identifiant, poids); // constructeur de la classe abstraite
this.nom = nom;
}
public String toString() {
return nom + super.toString();
}
public int definirRation() {
return poids*INDICE/100; // division entière
}
} // class Bovin

class Porcin extends Animal {


private static final int INDICE=6 ;

public Porcin(String identifiant, int poids) {


super(identifiant, poids); // constructeur de la classe abstraite
}
// pour cette classe, la méthode toString héritée convient
public int definirRation() {
return poids*INDICE/100 +5; // division entière
}
} // class Porcin
Classe abstraite : Principes généraux
Une classe abstraite est définie grâce au modificateur abstract. Elle contient deux types de méthodes :

des méthodes abstraites : elles sont déclarées avec le modificateur abstract mais ne sont pas définies : elles
jouent le rôle d’un outil de spécification, en forçant toute sous-classe instanciable à implémenter le
comportement correspondant,

des méthodes non abstraites, complètement définies, qui représentent soit des comportements par défaut pour
l’héritage, soit un comportement commun hérité.

Classes abstraites et méthodes abstraites

Une classe est abstraite si elle est définie avec le modificateur abstract.
Elle peut déclarer 0 ou plusieurs méthodes abstraites.

Une méthode abstraite est déclarée (et non définie) avec le modificateur abstract.

Une classe CCC qui hérite d'une classe abstraite AAA doit implémenter toutes les méthodes déclarées
abstract, sauf si elle est elle-même abstraite. Si CCC n’est pas définie abstract, et si toutes les méthodes
abstraites héritées de AAA ne sont pas définies dans CCC, la compilation de CCC provoque une erreur.
Exercice :

abstract Personne
l
private String nom ;
private String societe ;
private NumSecu
public Personne (String nom)
public Personne (String nom, String Entreprise)
public Personne (String nom, String Entreprise, String NumSecu);
public String getNom();
public String getSociete();
public String getInfo();
public void setNom(String s);
public void setSociete(String societe);
public void setNumSecu(String numSecu);
public boolean etreSalarie() {
private String validerSociete(String sNom);
public abstract float getSalaire();

Ouvrier Cadre Patron


private int dateEntre ; private int Indice ; private int cA ;
private static final float SMIG= private byte
6250,90; public Cadre (String nom) pourcentage
public ; (String
Patron nom)
public Ouvrier (String nom) public Cadre (String nom, public Patron (String nom,
public Ouvrier (String nom, String Entreprise) String Entreprise)
String Entreprise) public Cadre (String nom, public Patron (String nom,
public Ouvrier (String nom, String Entreprise, String String Entreprise, String
String Entreprise, String NumSecu); NumSecu);
NumSecu); public float getSalaire(); public float getSalaire();
public float getSalaire(); public String getInfo(); public String getInfo();
public String getInfo();
Reprendre l’exemple du chapitre 13.4 et changer la classe Personne en une classe abstraite ayant une méthode
abstraite getSalaire() qui devra être redéfinie dans les classes Ouvrier, Cadre, Patron.

Notion de polymorphisme : dans le programme de test taper les informations suivantes :


Personne e [ ] = new Personne[8];
e[0]=new Patron(“boss”,”AutourDuWorld”,1560206789086,1457000,2);
e[1]=new Cadre(“Arthur”, ”AutourDuWorld”,1600206099086,2);
e[2]=new Cadre(“Roger”, ”AutourDuWorld”, 2700206092389,1);
e[3]=new Ouvrier(“bob”, ”AutourDuWorld”, 1501206099086,80);
e[4]=new Ouvrier(“bobe”, ”AutourDuWorld”, 2591206099086,80);
e[5]=new Ouvrier(“bobi”, ”AutourDuWorld”, 1701105609086,90);
e[6]=new Ouvrier(“bobo”, ”AutourDuWorld”, 1551208909086,80);
e[7]=new Ouvrier(“bobob”, ”AutourDuWorld”,1501206099086,99);
for(i=0;i<8;i++) System.out.println(e[i].getInfo());

Que se passe-t-il si l'on essaie de taper le code suivant ?


for(i=0;i<8;i++) System.out.println(""+e[i].getSalaire());

Expliquer.

Conclure sur l'intérêt des classes abstraites.

La classe Object
On a vu précédemment que le mot clé extends permet de spécifier le nom de la classe mère. Si ce mot clé
extends n'est pas utilisé la classe dérive par défaut de la classe Object. Ainsi, les 2 lignes ci-dessous sont
équivalentes :

abstract class Repertoriable extends Object { abstract class Repertoriable {


} }

Les méthodes de la classe Object

méthodes déclarées en final dans la classe mère :


Une méthode déclarée en final dans la classe mère ne peut pas être surchargée dans la classe fille.
• getClass
• notify
• notifyAll
• wait

méthodes pouvant être surchargées :


• clone
• equals/hashCode
• finalize
• toString
La méthode clone :
Posons le problème : Lorsqu'on utilise l'expression p=p1 , p récupère la référence de l'objet p1 et donc à la
suite de cette expression p et p1 pointe vers le même objet. Ceci ne correspond donc pas à une recopie d'un
objet vers un autre.

….
Point p=newPoint(11,2);
Point p1=newPoint(1,11);
p=p1; // p possède maintenant la même référence que p1.
…..

Si l'on veut recopier l'espace mémoire de l'objet p1 vers un autre espace mémoire et donner la
référence à p, il faut utiliser la méthode clone.

….
Point p=newPoint(11,2);
Point p1=newPoint(1,11);
p=p1.clone();// p récupère la référence de l'espace mémoire alloué par clone dans lequel x=1 et y=11

La méthode clone permet de recopier un objet ayant des propriétés de types primitifs. Mais pour les
propriétés de type objet, la méthode clone ne recopiera que la référence de l'objet, ce qui n'est pas suffisant :

class Vecteur {
Point a;
Point b;
Vecteur(int x,int y) {a = new Point(x,y); b=new Point(3,4);}
}


Vecteur monVect = new Vecteur(1,2);
Vecteur monVect1 = new Vecteur(3,4);
monVect=monVect1.clone( );

Avant
Vecteur monVect = new Vecteur(1,2); Vecteur monVect = new Vecteur(3,4);

x=1 monVec Point a x=3


monVec Point a
t t
Point b y=2 Point b y=4

après : monVect=monVect1.clone( );

monVec Point a x=3


monVect Point a
t
Point b
Point b y=4
La méthode equals
La méthode equals permet de comparer les valeurs des propriétés de 2 objets. Si celle-ci sont égales
la méthode retourne true sinon false.

Ainsi dans l'exemple ci-dessous :

Integer one = new Integer(1), anotherOne = new Integer(1);

if (one.equals(anotherOne))
System.out.println("objects are equal");

Résultat true : même si les objets one et anotherOne sont 2 objets différents. Normal puisque equals
compare les valeurs : ici 1=1.

Il faudra donc savoir si les propriétés sont de type primitifs (auquel cas ce sont les valeurs qui seront
comparées) ou des objets (auquel cas ce sont les références qui seront comparés, permet de vérifier que 2
objets ont la même référence). Si vous surdéfinissez equals il faut surdéfinir hashCode.

La méthode finalize :
La méthode finalize est appelé par le système lorsque l'on sort de la classe.

La méthode toString :
Cette méthode est très intéressante pour le déboggage. La plupart des classes redéfinissent cette
méthode.

Par exemple la méthode toString de la classe Point :


class Point {
…..
public String toString() {
return getClass().getName() + "[x=" + x + ",y=" + y + "]";
}
}

La méthode getClass :
La méthode getClass est une méthode final qui permet de récupérer le nom de la classe. Ainsi la méthode
suivante permet d'afficher le nom de la classe de l'objet passé en argument.

void PrintClassName(Object obj) {


System.out.println("The Object's class is " +
obj.getClass().getName());
}

Les méthodes notify, notifyAll et wait


Les méthodes notify, notifyAll et wait sont des classes finales et sont utilisés dans la gestion des Threads.
Héritage, classes abstraites et interfaces

Les interfaces
Une interface est une classe abstraite particulière. Elle ne définit aucune variable d’instance et toutes ses
méthodes doivent être abstraites. Une interface ne fait que représenter, sans les implémenter, un ensemble
de comportements. Elle a uniquement un pouvoir de spécification.

On définit une interface en utilisant le mot clé interface.

Implémentation d’une interface

Une classe peut implémenter une ou plusieurs interfaces.


class Tortue implements Terrestre, Marin { ... }

Une interface peut hériter de plusieurs interfaces. Elle rassemble alors les spécifications des interfaces mères :
interface Amphibie extends Terrestre, Marin

On peut alors définir la classe Tortue comme suit :


class Tortue implements Amphibie

Interfaces ou classes abstraites ?

En Java, il n'y a pas d'héritage multiple :


une classe fille n’hérite que d'une classe mère (abstraite ou non).

Une classe peut hériter d'une seule classe-mère et d'une ou plusieurs interfaces, dont elle fournit une
implémentation.

Utilisation des interfaces

Les interfaces n’ont pas seulement un rôle de spécification, elles permettent d’avoir une « vue » particulière
sur un objet : un objet peut être casté dans le type d’une interface qu’il implémente.

Plusieurs objets implémentant la même interface pourront être considérés de façon équivalente à travers cette
interface.

public interface Marin {


public String vitesseDeNage();
}

class Tortue implements Terrestre, Marin {


public String vitesseDeNage() {
// etc...
return v1 ;
}
}

class Baleine implements Marin, Mammifere {


public String vitesseDeNage() {
// etc...
return v2;
}
}

On pourra alors passer indifféremment un objet Baleine ou un objet Tortue comme paramètre dans la
méthode suivante :
public void afficheAnimalMarin(Marin animal) {
System.out.println("Race : "
+ animal.getClass().getName());
System.out.println("Vitesse de nage : "
+ animal.vitesseDeNage());
}

Exercices :

la classe ColouredPoint

On désire créer la classe ColouredPoint qui hérite de la classe Point de l'exercice 2.24. Cette classe se définit
par
Class ColouredPoint extends Point{
protected java.awt.Color color;
ColouredPoint(int,int,Color){// à définir}
ColouredPoint(Point,Color){// à définir}
getColor(){// à définir}

Sous VisualAge, on pourra dans le WorkBench faire WorkSpace, OpenTypeBrowser et chercher Color :
pour comprendre cette classe.

Tester cette classe


les Packages
Qu’est-ce qu’un package ?
Pour le JDK :Un Package est un ensemble de fichier source dans lesquels se trouve en entête l'instruction
:
Package nomPackage;

Un fichier nomClasse.java aura donc


Package nomPackage; // la classe NomClasse appartient au Package noimPackage
class NomClasse{
}

De plus, il est impératif que la classe NomClasse se trouve dans le répertoire nomPackage.

Exemple :
Package P Compilation ClasseCP.class
ClasseB.class
Fichier 1 s
ClasseC.class
ClasseCP ClasseB
s
ClasseD.class

Fichier 2
ClasseC ClasseD

Dans le package, les classes sont compilées (fichiers *.class).

Règles de visibilité des Packages


Si P est un package, une classe A, qui ne fait pas partie de P, peut utiliser une classe CP de P, à
condition que :
− CP soit publique dans le package P :
public class CP ... { ... }
− A importe la classe CP :
import P.CP; class A {... }
En développement ou en exécution, quand on travaille sous le répertoire RepTravail, l’ensemble des
classes situées directement sous RepTravail constituent un package sans nom, appelé le package
par défaut.
Package PP
utilisation de Package P
Fichier 1 la classe CP
CP.java
dans la classe
Import P.CP ;
ClasseA public class CP{

Packages et modificateurs d’accès


Pour une classe CCC, appelons propriété toute variable d’instance ou méthode. Pour chaque propriété,
les modificateurs d’accès ont la signification suivante :
public accès libre à la propriété, pour toute classe qui a accès à CCC,
private accès réservé aux seules méthodes de CCC,
pas de modificateur d’accès (on parle d’accès package) la propriété est accessible dans CCC et aussi
pour toutes les autres classes du package de CCC.

Le modificateur protected
L’accès à une propriété protected de la classe CCC est limité aux sous-classes de CCC et aux classes du
même package que CCC.
Modificateurs d’accès : récapitulatif
Dans l’exemple ci-dessous :

C1, C2 et C3 sont trois classes publiques du package packageC1C2C3

Dans ce package, la classe C2 hérite de C1

Les classes C4 et C5 importent le package packageC1C2C3 mais n’appartiennent pas à ce package

La classe C4 hérite de C1

Autorisations d'accès suivant la classe :

Accès à : x d’une y d’une w d’une z d’une z d’une


instance de instance de instance de instance de instance de
C1 C1 C1 C1 C4
pour C1 oui oui oui oui oui
pour C2 oui oui oui
pour C3 oui oui oui
pour C4 oui oui
pour C5 oui
protected et l’accès dans les sous-classes
Si la classe Fille hérite de la classe Mere, et si p est une propriété définie protected dans Mere, toute
méthode de Fille n’a accès à p que pour des objets de la classe Fille ou d’une sous-classe de Fille.

Exemple
La classe Compte est définie dans le package PPP :

package PPP;
public class Compte {
protected float solde;
public void enregistreOperation(float versement) {
solde += versement; }
}
}

La classe Pel hérite de Compte. Elle est aussi définie dans le package PPP :

package PPP;
public class Pel extends Compte {
... // etc.
}

La classe Courant hérite aussi de Compte.


Elle n’est pas définie dans le package PPP,
sa méthode mmm prend en argument un objet Compte, un objet Courant et un objet Pel :

import PPP.Compte;
import PPP.Pel;
class Courant extends Compte {
public void mmm(Compte instanceClasseMere,
Courant instanceMemeClasse,
Pel instanceClasseSoeur) {
solde = instanceMemeClasse.solde; // OK
solde = instanceClasseMere.solde; // erreur de compilation
solde = instanceClasseSoeur.solde; // erreur de compilation
}
}
Un exemple de package : java.util
Un package peut contenir des classes, des interfaces ou d’autres packages (sous-packages).
Package java

java.util

java.util.zip java.lang
Vector

Dictionary
Object

Enumeration Hashtable

• Package java : ne contient que de sous-packages


• java.lang : contient la plupart des classes de base (Object, String, System...)
• java.util : contient un sous-package et des classes (représentant des collections)

Hiérarchie de packages

Les packages correspondent à des répertoires (locaux


ou sur le serveur), qui contiennent l'ensemble des
classes ayant des liens fonctionnels.

L’arborescence des packages de la librairie standard Java est compressée dans le fichier classes.zip.
Package et modificateurs d’accès

Deux types d'accès pour une classe ou une interface


public : la classe est accessible à tout autre classe, qu'elle soit du même package ou non.
Pas de modificateur : on parle d'accès package.
La classe n'est accessible qu'aux classes du même package. Elle représente une classe d'implémentation
du package.

Quatre types d'accès pour une propriété

Pas de modificateur : on parle d'accès package.La propriété (variable ou méthode) n'est accessible qu'aux
autres classes du même package,

public : accès non limité,

protected : accès limité aux classes du package et aux classes dérivées,

private : accès limité à la classe.Définition et importation de packages


Le package par défaut
L’instruction package est facultative dans un fichier source. Si elle est omise, les classes définies dans ce
fichier sont associées au package par défaut qui est le répertoire courant.

Un package définit un espace de noms qui permet de résoudre les éventuelles ambiguïtés entre les noms
de classes. Par exemple :
java.util.Date et java.sql.DateExemples d'utilisation de packages
Découpage en packages

Chaque package logique doit correspondre à un répertoire physique.


Par convention, les noms de packages commencent par des minuscules.

Les packages sont souvent utilisés pour matérialiser le découpage d’une application en couches abstraites
distinctes : la couche de gestion de persistance, la couche métier, l’interface graphique....
Les fichiers jar

Des librairies de classes peuvent aussi être utilisées. Elle sont stockées sous forme de fichiers compressés
à extension .jar. Exemple : swingall.jar

Ces fichiers peuvent être générés en utilisant l’utilitaire jar du JDK.


La variable d’environnement CLASSPATH

Pour pouvoir importer les packages des exemples précédents, le chargeur de classes doit trouver les
répertoires correspondants.

Si l’utilisateur place les répertoires imaPackage, interfaceGraphique et objetMetier dans le répertoire


package, il doit affecter à la variable d’environnement CLASSPATH le chemin de ce répertoire.

CLASSPATH = .;C:\Java1.1\Lib\Classes.zip;C:\package\mesComposants.jar;C:\package

. L’interpréteur recherche d’abord une classe dans le


répertoire courant (package par défaut).
C:\Java1.1\Lib\Classes.zip L’interpréteur recherche une classe importée de la librairie
standard de Java.
C:\package\mesComposants.jar L’interpréteur cherche une classe importée d’une librairie
supplémentaire (distincte du JDK) compressée au format jar.
C:\package L’interpréteur recherche une classe importée de la librairie
de l’utilisateur définie dans l’exemple précédent.

Remarque:
On peut définir un CLASSPATH au niveau du système et/ou au niveau de l’environnement de travail
IV - Les structures de données
Introduction
Ce chapitre va nous permettre de comprendre les mécanismes de gestion de données. La gestion de
données , on le verra dans le chapitre suivant, peut se faire avec des classes (Vector, ..) fournis par Java.
Cependant , pour un programmeur débutant, il est important de comprendre les différents éléments mis en
jeu dans la gestion de structures de données.
Ma première base de données (les bases de données statiques)
On désire mettre en place une base de données statique de personnes travaillant dans une société.
Pour cela on reprend les classes définies dans le chapitre III.13. Cette base de données est appelée statique
car elle possède un nombre fixe de Personne.

La classe NumSecu
Cette classe a déjà été écrite dans l'exercice V.13.3. Cette classe permet de rentrer le numéro de
sécurité sociale de la Personne. Ce numéro est confidentiel et donc est déclaré en private. Invisible à
l'extérieur de la classe NumSecu. Ce numéro de sécu permet de connaitre l'age, le sexe et la date de
naissance (sous la forme "MM/AAAA"). Le numéro de sécu doit nous permettre de distinguer 2
personnes. Comme l'on veut que le programmeur n'ait pas accés à cette variable, nous allons coder le
numéro de sécu et le transformer en un entier.Le code associé au numéro de sécu est donné par la
fonction numCode().

NumSecu
private String numSecu;
public int age;
public String dateNaissance
public boolean Masculin

public numSecu (String numSecu);


public void setNumSecu(String numSecu);
private String validerNumSecu( String numSecu);
public String getNumSecu();
La classe Personne
La classe Personne a déjà été étudié au chapitre III.13. Cette classe est abstraite puisque la
méthode de calcul du salaire dépend du salarié. Cette classe possède la fonction getInfo() qui fournit le
nom , la société.
La classe Personne est la classe mère des classes Patron, Cadre et Ouvrier. Ces classes
redéfinissent les méthodes getInfo() et définissent la classe getSalaire().

NumSecu

private String numSecu;


public int age;
public String dateNaissance
public boolean Masculin
abstract Personne
class public numSecu (String numSecu);
private String nom ; public void setNumSecu(String numSecu);
private String valider NumSecu( String numSecu);
private String societe ; public String getNumSecu();
private NumSecu
S
public Personne (String nom)
public Personne (String nom, String Entreprise)
public Personne (String nom, String Entreprise, String NumSecu);
public int getAge();
public boolean isMasculin();
public String getDateNaissance();
public String getInfo();
public void setNom(String s);
public String getNom();
public void setSociete(String societe);
public String getSociete();
public void quitterSociete()
public void setNumSecu(String numSecu);
public String getNumSecu();
public boolean etreSalarie() {
private String validerSociete(String sNom);

public abstract float getSalaire();

Ouvrier Cadre Patron


private int dateEntre ; private int Indice ; private int cA ;
private static final float SMIG= private byte
6250,90; public Cadre (String nom) pourcentage
public ; (String
Patron nom)
public Ouvrier (String nom) public Cadre (String nom, public Patron (String nom,
public Ouvrier (String nom, String Entreprise) String Entreprise)
String Entreprise) public Cadre (String nom, public Patron (String nom,
public Ouvrier (String nom, String Entreprise, String String Entreprise, String
String Entreprise, String NumSecu); NumSecu);
NumSecu); public float getSalaire(); public float getSalaire();
public float getSalaire(); public String getInfo(); public String getInfo();
public String getInfo();
La classe Entreprise

Une entreprise possède des salariés (ici fixé à 5). Ces salariés sont des Personne. Une Personne peut être un
Patron, un ouvrier ou un Cadre. Une Entreprise est donc un tableau de Personne.

Il faut pouvoir ajouter ou retirer des Personnes

Il faut pouvoir afficher les Personnes qui composent l'Entreprise

Il faut savoir si l'Entreprise est vide ou pleine et le nombre de salariés.

Il doit être possible de savoir si une Personne existe dans l'Entreprise

L'Entreprise doit être capable de dire combien il y a de cadres, d'ouvriers et de Patrons dans l'entreprise et
donner leurs références

L'entreprise doit pouvoir licencié tout ses salariés.

Dans un deuxième temps, on devra être capable de faire des recherches dans la base de données:
ainsi il sera possible de rechercher une personne par son nom, son age ou son numéro de sécu.

Remarque : il est possible en utilisant la méthode getClass() héritée de la classe object de récupérer la
classe de l'objet.

Entreprise
private Personne[] tabP;
private static final int
N=5;
public Entreprise();
public void ajouter (Personne p);
public void retirer (Personne p);
public void afficher();
public boolean isExiste(Persone p);
public boolean isPlein();
public boolean isVide();
public int chercher (Personne []p, String
salarie);
public void toutEffacer();
public int getNbPersonne();
Les méthodes de la classe Entreprise null
Le constructeur : null
null
public Entreprise(){ null
tabP = new Personne [N];
} Personne null

Le constructeur permet d'initialiser le tableau de Personne.

Méthode ajouter :

Pour ajouter une personne, il suffit de trouver une case vide. Dés que l'on trouve une cas vide, on ajoute la
personne et on sort de la méthode. Si la base est pleine, on affiche "base pleine" et on sort.

Attention, si la personne existe déjà dans la base, on ne doit pas la saisir de nouveau.

public void ajouter(Personne p){


int i;
if(!isPlein()&&!isExiste(p)){
for(i=0;i<tabP.length;i++) if(tabP[i]==null) break;
tabP[i]=p;
}
else System.out.println("base pleine ou personne existe déja");
}

Méthode retirer

Pour retirer une personne de la base , il faut que cette base soit non vide et que cette personne soit
référencée dans la base. Dés que la personne est trouvée, on met sa référence à null. (effacée).

public void retirer(Personne p){


boolean fin= false;
int i;
if(!isVide()&&isExiste(p)){
for( i=0;i<tabP.length&&!fin;i++)
if(tabP[i].numCode()==p.numCode()){
tabP[i]=null;
fin = true;
}
else System.out.println("Base vide ou Personne existante");

}
}

Methode toutEffacer :

On force toutes les cases du tableau à null.


public void toutEffacer(){
int i;
if(!isVide()){
for( i=0;i<tabP.length;i++)
tabP[i]=null;
}
}

Méthode afficher:

Si la base est non vide on affiche toutes les cases non vides , sinon on affiche ("base vide")

public void afficher(){


int i;
if(!isVide()){
for( i=0;i<tabP.length;i++)
if(tabP[i]!=null) System.out.Println(tabP[i].getInfo());
}
else System.out.println("base vide");
}

Methode isExiste:

Si la base est non vide on teste le code de la personne avec le codes de chaque référence de la base.

public boolean isExiste(Personne p){


int i;
if(!isVide()){
for(i=0;i<tabP.length;i++)
if(tabP[i].getCode()==p.getCode())return true;
}
return false;
}

Méthode isVide :

teste si la base est vide

public boolean isVide(){


for(int i=0;i<tabP.length;i++)
if(tabP[i]!=null) return false;
return true;
}

Méthode isPlein

teste si la base est pleine


public boolean isPlein(){
int i;
for(i=0;i<tabP.length;i++)
if(tabP[i]==null) break;
if(i<tabP.length) return false
else return true;
}
Méthode chercher

Cette méthode se base sur le fait que toutes la classe personne hérite par défaut de la classe Object, et donc
possède la classe getClass(). Si la référence est un objet de la classe Ouvrier, la méthode getClass() renvoie
: class cours.ouvrier (cours étant le nom du package). pour connaître la classe il faut donc analyser la
chaine de caractère renvoyée par la méthode getClass.

La classe StringTokenizer permet de chercher un élément dans une chaine de caractère.

public int chercher (Personne []p,String salarie){


int i,j=0, compteur=0;
String s;

if(!isVide()){
for( i=0;i<tabP.length;i++){
if(tabP[i]!=null){
s=tabP[i].getClass().toString();
StringTokenizer t= new StringTokenizer(s," .");
while(t.hasMoreElements())
if(t.nextToken().equals(Salarie)) p[compteur++]=tabP[i];
}
}
}

else System.out.println("base vide");


return compteur;
}

utilisation de la méthode chercher : on veut afficher tous les ouvriers dans un tableau d'ouvrier

ouvrier [] tabO= new Ouvrier[N];


x=ent.chercher(tab0,"Ouvrier");
if( x>0){ System.out.println("il y a "+x+"Ouvrier");
for (int j=0;j<x;j++) System.out.println(tab0[j].getInfo());

Un exemple récapitulatif

Dans ce qui suit , se trouvent les classes NumSecu, Personne et Entreprise. Ces classes ne sont données
qu'à titre indicatif et ne suivent pas compètement ce qui a été énoncé précédement.

Il est demandé d'écrire soit-même les classes en fonction du cahier des charges et de tester dans une classe
à part la base de données.
package cours;

/* Creation date: (12/04/2001 10:58:35)


* @author: jlSlavat
*/
abstract class Personne {
private String nom; // nom de l'individu
private byte departement; // département de résidence
public NumSecu numSecu;
private long code=0;
private static final byte MAX_DEPT=95 ;
private static final byte MIN_DEPT=1;
private static int compteur=0;
// cette variable permet de donner un numéro même si le numéro de sécu n'est pas donné
// Définition des constructeurs
public Personne () {
this( "nom inconnu",0);
}

public Personne (String nom) {


this(nom,0);
}

public Personne (String leNom, int leDept) {


this(leNom,leDept,null);
code = compteur++;
}

public Personne(String leNom,int leDept,String numSecu) {


nom = leNom.toUpperCase();
if (leDept != 0) setDepartement(leDept);
if(numSecu!=null) this.numSecu= new NumSecu(numSecu);
}

public void afficherSurEcran() {


System.out.println("Je m'appelle " + nom);
if (departement != 0) System.out.println("j'habite dans le " + departement);
}

public long getCode(){


if (numSecu!=null) code= numSecu.numCode();
return code;
}

public String getInfo(){


String s="Je m'appelle " + nom+ "j'habite dans le " + departement;
return s;
}

public abstract float getSalaire();

public void setDepartement(int unDept) {


// ici on rejette tout numéro de département incorrect
if(unDept>=MIN_DEPT&&unDept<=MAX_DEPT) departement=(byte) unDept ;
else System.out.println(" Erreur saisie ") ;
}
}
package cours;

/* Creation date: (16/04/2001 19:08:37)


* @author: jlSlavat
*/
public class NumSecu {
private String numSecu="0";
public long code;

public NumSecu(String param) {


numSecu=param;
}

public long numCode() {


double codeD;
String s1=numSecu.substring(0,7);
codeD = Double.parseDouble(s1);
s1=numSecu.substring(7,12);
codeD *= Double.parseDouble(s1);
codeD/=100;
code=(long)codeD;
return code;
}
}

package cours;

/* Creation date: (16/04/2001 17:39:24)


* @author: jlSalvat */

public class Entreprise {


private Personne[] tabP;
private static final int N=5;

public Entreprise() {
tabP = new Personne [N];
}

public void afficher(){


int i;
if(!isVide()){
for( i=0;i<tabP.length;i++)
if(tabP[i]!=null) System.out.println(tabP[i].getInfo());
}
else System.out.println("base vide");
}

public void ajouter(Personne p){


int i;
if(!isPlein()&&!isExiste(p)){
for(i=0;i<tabP.length;i++)
if(tabP[i]==null) break;
tabP[i]=p;
}
else System.out.println("base pleine ou personne existe déja");
}
public boolean isExiste(Personne p){
int i;
if(!isVide()){
for(i=0;i<tabP.length;i++)
if(tabP[i]!=null) if(tabP[i].getCode()==p.getCode())return true;
}
return false;
}

public boolean isPlein(){


int i;
for( i=0;i<tabP.length;i++) if(tabP[i]==null) break;
if(i<tabP.length) return false;
else return true;
}

public boolean isVide(){


for(int i=0;i<tabP.length;i++)
if(tabP[i]!=null) return false;
return true;
}

public void retirer(Personne p){


boolean fin= false;
int i;
if(!isVide()&&isExiste(p)){
for( i=0;i<tabP.length&&!fin;i++)
if(tabP[i]!=null)
if(tabP[i].getCode()==p.getCode()){
tabP[i]=null;
fin = true;
}
else System.out.println("Base vide ou Personne existante");
}
}

public void toutEffacer(){


int i;
if(!isVide()){
for( i=0;i<tabP.length;i++) tabP[i]=null;
}
}
}
Une nouvelle base de données (bases de données dynamique)
Jusqu'à présent, nous avons utilisé une structure de données statique, c'est à dire une structure de
données qui était figée. Ce type de base de données n'est pas complètement satisfaisant. En effet que
dirait-on d'une structure de données qui prendrait en mémoire 100 ko alors qu'elle ne comporterait que
quelques champs.
Il est évident qu'il faut trouver une structure dont la taille change en fonction des données placées. il faut
donc une structure de données dynamique.

Une classe autoréférentielle


la solution à ce problème est la classe autoréférentielle, c'est à dire qui possède une référence sur
un objet de sa classe.
Noeud
Personne salarie;
Noeud suivant;

public Noeud(Personne p);


public void setPersonne(Personne p);
public Personne getPersonne();
public void setSuivant( Noeud suivant);
public noeud getSuivant();

Allocation de mémoire dynamique


Maintenant à chaque ajout d'une personne, il suffit de créer de façon dynamique un objet de la
classe Noeud et de fournir sa référence au noeud précédent.

L'insertion d'un salarié peut se faire à partir du premier noeud ou à partir du dernier noeud.

De plus , il doit être possible d'effacer dans un premier temps le premier ou le dernier salarié de la liste.

Remarque : dans un deuxième temps, ou pourra se poser la question de l'effacement d'un salarié
quelconque dans la liste.

Noeud Noeud
dernier
Premie salarie1 suivan salarie2 suivan Noeud
rNoeud
La Classe Liste
Reprenons le cahier des charges fixé au chapitre 2.3 pour la classe Entreprise:

Une entreprise possède des salariés (ici fixé à 5). Ces salariés sont des Personne. Une Personne peut être un
Patron, un ouvrier ou un Cadre. Une Entreprise est donc un tableau de Personne.

Il faut pouvoir ajouter ou retirer des Personnes en début et en fin de liste

Il faut pouvoir afficher les Personnes qui composent l'Entreprise

Il faut savoir si l'Entreprise est vide ou pleine et le nombre de salariés.

Il doit être possible de savoir si une Personne existe dans l'Entreprise

L'Entreprise doit être capable de dire combien il y a de cadres, d'ouvriers et de Patrons dans l'entreprise et
donner leurs références

L'entreprise doit pouvoir licencié tout ses salariés.

Dans un deuxième temps, on devra être capable de faire des recherches dans la base de données:
ainsi il sera possible de rechercher une personne par son nom, son age ou son numéro de sécu.

On utilise les classes définies dans le chapitre 2.2, c'est à dire la Classe abstraite Personne et les classes
filles Employe, Cadre et Patron.

Liste
private NoeudListe
premierNoeud;
private NoeudListe
public Liste()
public Liste( String s )
public void afficher()
public boolean isVide()
public void insererEnQueue( Personne personneAIns )
public void insererEnTete( Personne personneAIns )
public Personne retirerDeQueue()
public Personne retirerDeTete()
public boolean isExiste(Persone p);
public int chercher (Personne []p, String salarie);
public void toutEffacer();
public int getNbPersonne();
Le constructeur : public dernier
Liste() Premie null null Noeud
rNoeud

Le constructeur permet d'initialiser les références premierNoeud et dernierNoeud à null

Insertion en Tête : public void insererEnTete( Personne personneAIns )


Noeud
insertion du premier
salarie1 dernier élément noté salarie1
Premie suivan
Noeud
rNoeud

Noeud Noeud
insertion du
dernier deuxième élément
Premie salarie suivan salarie1 suivan Noeud é l i 2
rNoeud 2

Insertion en queue : public Noeud


void insererEnQueue( Personne personneAIns ) insertion du premier
salarie1 dernier élément noté salarie1
Premie suivan
Noeud
rNoeud

Noeud Noeud
insertion du
dernier deuxième élément
Premie salarie1 suivan salarie suivan Noeud é l i 2
rNoeud 2
Effacement en queue : public Personne retirerDeQueue()

Noeud Noeud
Liste avant l'appel à la
dernier méthode
Premie salarie suivan salarie1 suivan Noeud i D Q
rNoeud 2

Noeud Liste aprés l'appel à la


dernier méthode
Premie salarie2 suivan
Noeud retirerDeQueue;
rNoeud Le dernier élément est

Effacement en tête : public Personne retirerDeTete()

Noeud Noeud
Liste avant l'appel à la
dernier méthode retirerDeTete
Premie salarie2 suivan salarie suivan Noeud
rNoeud 1

Noeud Liste aprés l'appel à la


dernier méthode retirerDeTete;
Premie salarie1 suivan
Noeud Le premier élément est
rNoeud effacer
Le code
le code des classes NoeudListe et Liste est donné ci-dessous. Le code des 4 dernières méthodes, de la
classe Liste, données dans la liste précédente n'est pas donné ci-dessous. Il est donc demandé d'écrire le
code des ces 4 fonctions.

package cours;

/* Creation date: (03/05/2001 15:16:03)


* @author: jlSalvat
*/

public class NoeudListe {


Personne salarie;
NoeudListe suivant;

// Constructeur: créer un NoeudListe qui fait référence à la Personne p.


public NoeudListe( Personne p ) { this( p, null ); }

// Constructeur: créer un NoeudListe qui fait référence à la Personne p


// et au NoeudListe suivant de la Liste.
public NoeudListe( Personne p, NoeudListe noeudSuivant ){
salarie = p;
suivant = noeudSuivant;
}

// Retourne une référence à la Personne dans ce noeud.


Personne getPersonne() { return salarie; }

// Retourne le noeud suivant.


NoeudListe getSuivant() { return suivant; }
}

package cours;

/* Creation date: (03/05/2001 15:33:36)


* @author: jlSalvat
*/
// Définition de la classe de Liste.
public class Liste {
private NoeudListe premierNoeud;
private NoeudListe dernierNoeud;
private String nom; // Chaîne contenant "liste" pour affichage.

// Constructeurs
public Liste() {
this("liste");
}

public Liste( String s ){


nom = s;
premierNoeud = dernierNoeud = null;
}

// Afficher le contenu de la Liste.


public void afficher(){
if ( isVide() ){
System.out.println( "La " + nom + " est vide." );
}
System.out.print( "La " + nom + " contient: \n" );
NoeudListe courant = premierNoeud;

while ( courant != null ) {


System.out.print("\t"+ courant.salarie.getInfo() + "\n " );
courant = courant.suivant;
}
System.out.println( "\n" );
}

// Retourner true si la Liste est vide.


public boolean isVide() { return premierNoeud == null; }

// Insérer un salarie en queue de la Liste. Si Liste est vide, premierNoeud et dernierNoeud se réfèrent au
// même objet; sinon, la variable d'instance suivant du dernierNoeud fait référence au nouveau nœud.
public void insererEnQueue( Personne personneAIns ){
if ( isVide() )
premierNoeud = dernierNoeud = new NoeudListe( personneAIns );
else dernierNoeud = dernierNoeud.suivant = new NoeudListe( personneAIns );
}

// Insérer un Object en tête de la Liste. // Si Liste est vide, premierNoeud et dernierNoeud se réfèrent au
// même objet; sinon, le premierNoeud se réfère au nouveau nœud.
public void insererEnTete( Personne personneAIns )
{
if ( isVide() ) premierNoeud = dernierNoeud = new NoeudListe( personneAIns );
else premierNoeud = new NoeudListe( personneAIns, premierNoeud );
}

// Retirer le dernier nœud de la Liste.


public Personne retirerDeQueue()
{
Personne retirerElement = null;
if ( isVide() ) System.out.println("base vide : impossible de retirer la personne");
else retirerElement = dernierNoeud.salarie; // retouver la donnee.

// réinitialiser les références des premierNoeud et dernierNoeud.


if ( premierNoeud.equals( dernierNoeud ) ) premierNoeud = dernierNoeud = null;
else {
NoeudListe courant = premierNoeud;
while ( courant.suivant != dernierNoeud ) // pas le dernier nœud.
courant = courant.suivant; // aller au nœud suivant.
dernierNoeud = courant;
courant.suivant = null;
}
return retirerElement;
}

// Retirer le premier nœud de la Liste.


public Personne retirerDeTete()
{
Personne retirerElement = null;
if ( isVide() ) System.out.println("base vide : impossible de retirer la personne");
else retirerElement = premierNoeud.salarie; // rechercher la donnee.

// réinitialiser les références des premierNoeud et dernierNoeud.


if ( premierNoeud.equals( dernierNoeud ) ) premierNoeud = dernierNoeud = null;
else premierNoeud = premierNoeud.suivant;

return retirerElement;
}
}
Le test de la structure de données dynamique

package cours;

/ * Creation date: (03/05/2001 15:40:27)


* @author: jlSalvat
*/

public class TestListe {


public static void main( String args[] )
{
Liste entrepriseList = new Liste("CNAM"); // créer le conteneur Liste.

// Créer quelques objets à stocker dans la Liste.


Employe e=new Employe("toto","IUT",90,"2681046082597");

System.out.println("le numéro de sécu de toto:"+ e.getCode());


Patron patron= new Patron("patron",90);
System.out.println("le numéro de sécu de patron:"+ patron.getCode());
Employe n=new Employe("titi","CNAM",90,"1701046082597");
System.out.println("le numéro de sécu de titi:"+ n.getCode());
Employe mon=new Employe("tutu","CNAM",90,"1681046082597");
System.out.println("le numéro de sécu :"+ mon.getCode());

// Utiliser les méthodes d’insertion de la Liste.


entrepriseList.insererEnTete( e );
entrepriseList.afficher();
entrepriseList.insererEnTete( patron );
entrepriseList.afficher();
entrepriseList.insererEnQueue( n );
entrepriseList.afficher();
entrepriseList.insererEnQueue( mon );
entrepriseList.afficher();

// Utiliser les méthodes de retrait de la Liste.


Personne objARetirer;

objARetirer = entrepriseList.retirerDeTete();
System.out.println( objARetirer.getCode() + " retirer" );
entrepriseList.afficher();
objARetirer = entrepriseList.retirerDeTete();
System.out.println(objARetirer.getCode() + " retirer" );
entrepriseList.afficher();
objARetirer = entrepriseList.retirerDeQueue();
System.out.println(objARetirer.getCode() + " retirer" );
entrepriseList.afficher();
objARetirer = entrepriseList.retirerDeQueue();
System.out.println(objARetirer.getCode() + " retirer" );
entrepriseList.afficher();

}
}
le numéro de sécu de toto:221427589
le numéro de sécu de patron:0
le numéro de sécu de titi:140489389
le numéro de sécu :138837589
La CNAM contient:
Je m'appelle TOTOj'habite dans le 90 je travaille à IUT

La CNAM contient:
Je m'appelle PATRONj'habite dans le 90
Je m'appelle TOTOj'habite dans le 90 je travaille à IUT

La CNAM contient:
Je m'appelle PATRONj'habite dans le 90
Je m'appelle TOTOj'habite dans le 90 je travaille à IUT
Je m'appelle TITIj'habite dans le 90 je travaille à CNAM

La CNAM contient:
Je m'appelle PATRONj'habite dans le 90
Je m'appelle TOTOj'habite dans le 90 je travaille à IUT
Je m'appelle TITIj'habite dans le 90 je travaille à CNAM
Je m'appelle TUTUj'habite dans le 90 je travaille à CNAM

0 retirer
La CNAM contient:
Je m'appelle TOTOj'habite dans le 90 je travaille à IUT
Je m'appelle TITIj'habite dans le 90 je travaille à CNAM
Je m'appelle TUTUj'habite dans le 90 je travaille à CNAM

221427589 retirer
La CNAM contient:
Je m'appelle TITIj'habite dans le 90 je travaille à CNAM
Je m'appelle TUTUj'habite dans le 90 je travaille à CNAM

138837589 retirer
La CNAM contient:
Je m'appelle TITIj'habite dans le 90 je travaille à CNAM

140489389 retirer
La CNAM est vide.
Une autre structure de données dynamique : la pile
La pile (stack en anglais) est une version de la liste chainée précédente assortie de quelques
contraintes : les noeuds sont obligatoirement ajoutés et retirés de la pile à son sommet. Pour cette raison,
on désigne la pile comme une structure de données du type dernier entré, premier sorti, ou LIFO (Last In,
First out). Le membre de liaison qui porte le lien au membre suivant dans le dernier noeud de la pile est
mis à null pour indiquer qu'il constitue le pied de la pile.
Les principales méthodes qui interviennent dans la manipulation d'une pile sont les méthodes push
(pousser) et pop (enlever). La méthode push ajoute un nouveau noeud au sommet de la pile alors que la
méthode pop retire le noeud du sommet de la pile.

La classe Pile : un cas particulier de la classe Liste


Maintenant à chaque ajout d'une personne, il suffit de créer de façon dynamique un objet de la
classe Noeud et de fournir sa référence au noeud précédent.

L'insertion d'un salarié peut se faire à partir du premier noeud (c'est le sommet de la pile)

De plus , il doit être possible d'effacer dans un premier temps le premier salarié de la liste.

Noeud Noeud

Premie salarie1 suivan salarie2 suivan


rNoeud t t

La Classe Pile
Dans sa forme la plus simple la classe Pile peut être implémentée à partir de la classe mère Liste.

Pile extends Liste

public Pile(){ super("pile");}


public void push(Personne personneAIns){
insererEnTete(personneAIns);}
public Personne pop(){ return retirerDeTete();}
public void afficher(){ super.afficher();}
public boolean isVide(){ return super.isVide();}
Le constructeur : public Liste() Premie null
rNoeud

Le constructeur permet d'initialiser les références premierNoeud à null

push : public void push( Personne personneAIns )


Noeud
insertion du premier
salarie1 élément noté salarie1
Premie suivan
rNoeud

Noeud Noeud
insertion du
Premie salarie salarie1 suivan deuxième élément
suivan
rNoeud 2 é l i 2

pop : public Noeud


Personne pop() Noeud
Liste avant l'appel à la
Premie salarie2 salarie suivan méthode pop
suivan
rNoeud 1

Noeud Liste aprés l'appel à la


méthode pop;
Premie salarie1 suivan
rNoeud
Une autre structure de données dynamique : la FIFO
La queue est semblable à une file d'attente à une caisse de supermarché. La première personne de la file
est servie en premier. Une queue est une structure de données premier entré, premier sorti (FIFO ou First
In , First out). Une application classique est le principe du gestionnaire d'impression. Les travaux
d'impressions se trouvent les uns à la suite des autres. Le premier entré est le premier sortie.

salarie1 insertion salarie1: insererQueue(salarie1)

salarie2 salarie1 insertion salarie2: insererQueue(salarie2)

salarie3 salarie2 salarie1 insertion salarie3: insererQueue(salarie3)

salarie3 salarie2 effacer: effacerQueue()

salarie3 effacer: effacerQueue()

La classe Pile : un cas particulier de la classe Liste

L'insertion d'un salarié se fait à partir du premier noeud (c'est le sommet de la pile)

L'effacement se fait en retirant le dernier noeud.

Dans sa forme la plus simple la classe Queue extends


Queue peut être Liste
implémentée à partir de la classe mère Liste.
public Queue(){ super("queue");}
public void insererQueue(Personne personneAIns){
InsererEnQueue(personneAIns);}
public Personne effacerQueue(){
return retirerDeTete();}
public void afficher(){ super.afficher();}
public boolean isVide(){ return super.isVide();}
Les collections
Introduction
Dans le chapitre précédent, nous avons créé différentes de classes de structure de données, et discuté des
utilisations de ces différentes structures. Dans ce chapitre nous allons analyser les différentes classes
fournies par Java , dans le package java.util, qui permettent de manipuler les structures de données sans se
soucier de leur implémentation. Ces classes sont appelées des collections, car ce sont des collections
d'objets rangés sous différentes formes.
Il existe 2 types de collections :
les collections du JDK 1.1 (les plus anciennes), qui sont les classes Vector (vecteur) , Stack (Pile),
Hashtable (table à découpage), et qui permettent de manipuler des objets
les collections du JDK 1.2 qui sont les interfaces Collection, List, Set et Map et les classes
Collections, ArrayList, LinkedList

Nous nous intéresserons dans un premier temps aux collections les plus anciennes , mais que l'on peut
toujours utiliser.
La classe Vector

Qu’est ce qu’un vecteur ?


Un vecteur est une collection ordonnée d’objets qui s’agrandit dynamiquement en fonction des
besoins.
Les éléments d’un vecteur sont indexés par des entiers à partir de 0.
C’est un tableau d’objets qui s’agrandit en fonction des besoins et dont la longueur n’est pas
constante, il est possible de supprimer des éléments. Comme avec un tableau classique, on accède aux
éléments grâce à un index (avec la méthode elementAt())
Un Vector permet de mémoriser des références sur des Object. Il est ainsi possible de stocker n'importe
quelle objet d'une classe dans un Vector.

Définition d’un vecteur


La classe Vector du package java.util permet de définir un vecteur :
Vector vecteur = new Vector();

Un Vector possède une taille (renvoyée par size()) et une capacité (renvoyée par capacity()). Le
constructeur par défaut créé un tableau de 10 éléments vide (taille=0 et capacité=10).

Ajout et suppression d'éléments


Méthodes de la classe Vector Commentaires
addElement(Object) ajoute l’élément à la fin d'un vecteur.
insertElementAt(Object, int) insère un nouvel élément à l'indice indiqué.
setElementAt(Object, int) remplace l'élément situé à l'indice indiqué par un nouvel élément.
removeElement(Object) supprime la première occurrence de l’élément.
removeElementAt(int) supprime l'élément à l'indice indiqué.

A chaque fois que la taille du tableau, lors de l'ajout d'un élément, excède la capacité de ce tableau, le
système double automatiquement la taille du tableau.
Remarque : setElementAt() ,Il faut que l’index passé en paramètre corresponde à une case qui « existe »...

Vector v = new Vector() ;


v.addElement("Bonjour") ; // case 0 existe
v.addElement(35) ; // case 1 existe
v.setElementAt(Color.blue, 2) ; // case 2 n’existe pas. Une exception ArrayIndexOutOfBoundsException est levée

Pour que ça marche, on peut ajouter avant la ligne critique :


v.addElement(null) ; // case 2 « existe », même si elle contient null

Accès aux éléments d'un vecteur

Méthodes de la classe Vector Commentaires


indexOf(Object) renvoie l'indice d'un élément du vecteur, ou -1si la recherche a été infructueuse
elementAt(int) renvoie l'élément situé à l’index passé en paramètre
size() renvoie le nombre d’objets stockés
capacity() renvoie la capacité totale du tableau
firstElement() renvoie le premier élément
lastElement() renvoie le dernier élément
isEmpty() renvoie true si le Vector est vide
contains(Object) renvoie true si l'Object passé en argument existe dans le Vector

Remarque : indexOf(Object unObjet)


Cette méthode fait une boucle for sur tout le vecteur (de 0 à ElementCount), et teste :
unObjet.equals(elementAt[i]) .

Exercice :
Reprendre la classe Entreprise du chapitre VII 2.3 (structures de données) et utiliser la classe Vector pour
implémenter cette classe. On utilisera pour cela l'objet vectE de la classe Vector qui sera instancié dans le
constructeur Entreprise(). La classe Entreprise n'aura pas la méthode isPlein(), puisque cette fois-ci le
tableau utilisé est dynamique (principe de la classe Vector).

Entreprise
private Vector vectE;

public Entreprise();
Permet de chercher un groupe
public void ajouter (Personne p); de salarié de même type :
public void retirer (Personne p); "employé, cadre ou patron"
public void afficher();
public boolean isExiste(Persone p);
Cette méthode renvoie dans
public boolean isVide(); Personne[] p les salariés
public int chercher (Personne []p, String trouvés
salarie);
public void toutEffacer();
public int getNbPersonne();
Vecteurs et énumérations

L'interface Enumeration
L'interface Enumeration du package java.util définit un type abstrait qui déclare des services de contrôle
d’itération pour parcourir le contenu d’une collection d’objets.

public interface Enumeration {


boolean hasMoreElements(); // renvoi true s'il reste des éléments à parcourir
Object nextElement(); // renvoi le prochain élément de la collection
}

Itération sur le contenu d’un vecteur


La méthode elements de Vector renvoie un objet dont le type implémente l’interface Enumeration.

Un exemple d’utilisation :

import java.util.*; Package dans lequel on trouve la classe Vector et Enumeration


public class Auteur {
Vector livres;
String nom;

public Auteur(String nom, Vector livres) {


this.nom = nom;
this.livres = livres;
}
public String toString() {
// renvoie une chaîne formatée
String chaine = nom ;
// itération sur le vecteur livres
Enumeration enum = livres.elements();
while (enum.hasMoreElements())
chaine += "\n\t" + enum.nextElement();
return chaine;
}
public void ajouterLivre(String unLivre){
livres.addElement(unLivre);
}
}

Avec les vecteurs, il existe deux manières d’itérer :

soit on utilise la manière traditionnelle :

int i;
for (i = 0 ; i < monVecteur.size();i++)
monObjet := monVecteur.elementAt(i) ;

soit on utilise les énumérations

Enumeration monEnum = monVecteur.elements;


while (monEnum.hasMoreElements())
monObjet := monEnum.nextElement() ;
avantage : on n’a pas à se préoccuper de la taille du vecteur, l’énumération le fait pour
nous, il n’y a donc pas de risque que l’on génère une exception alors qu’avec elementAt, si
on passe un index supérieur à la taille du vecteur une exception est levée.

Remarque : ‘la méthode elements de Vector renvoie un objet dont le type implémente l’interface
Enumeration’ => les comportements hasMoreElements et nextElement se retrouvent chez tous les objets
qui implémentent Enumeration

La classe Stack
Au chapitre précédent nous avons appris comment construire des structures de données telles que
les listes chainées, les piles , les queues. Nous avions crées la classe Pile qui héritait de la classe Liste.
La classe Stack est implémentée à partir des méthodes de la classe Vector, et donc la classe Stack
hérite de la classe Vector. La classe Stack permet de réaliser une structure de données en pile. Cette classe
est comme la classe Vector conçue pour stocker des Object.

Les méthodes de la classe Stack


Méthodes de la classe Stack Commentaires
Stack() Constructeur hérite de la classe Vector
pop() retire l'élément du sommet
push() ajoute un élément au sommet
search(Object) renvoie true si l'Object passé en argument existe dans la pile
peek() revoie l'élément au sommet de la pile sans le retirer
Empty() renvoie true si le Stackr est vide

La classe Hashtable

Qu’est ce qu’un dictionnaire ?


Un dictionnaire est une collection d’éléments qui s’agrandit dynamiquement en fonction des besoins. Les
éléments d’un dictionnaire sont des associations entre une clé (de type Object) et une valeur (elle aussi de
type Object).

Information partielle Totalité de l'information


Clé Valeur

Définition d’un dictionnaire


La classe Hashtable du package java.util permet de définir un dictionnaire :

Hashtable dico = new Hashtable();

Ajout et suppression d’éléments


Méthodes de la classe Hashtable Commentaires
put (Object clé, Object valeur) ajoute une association clé-valeur. Si la clé était déjà présente dans le
dictionnaire, l’ancienne valeur est remplacée par la nouvelle.
remove (Object cle) supprime l’association dont la clé est indiquée par le paramètre
Accès aux éléments
Méthodes de la classe Hashtable Commentaires
get(Object clé) renvoie la valeur de l’association dont la clé est indiquée par le paramètre
size() .renvoie le nombre d’associations.
isEmpty() renvoie true si l’objet ne contient aucune association.
containsKey(Object) renvoie true si la clé passée en argument existe dans le Hashtable

Remarque :
Un dictionnaire est une collection d’associations clé-valeur. On accède à une association non pas avec un
index mais via une clé qui doit être du type objet.
Valeur
Clé

On n’a pas accès à l’ordonnancement des associations dans la table de hachage, c’est un algorithme
interne qui s’en charge. Du coup la première association que l’on a créée ne sera pas forcement celle que
l’on verra apparaître lors d’une itération sur la table.

Dictionnaires et énumérations

Itération sur le contenu d’un dictionnaire


Deux méthodes de HashTable renvoient un objet dont le type implémente l’interface Enumeration.

elements () pour itérer sur la collection des valeurs d’un dictionnaire.


keys () pour itérer sur la collection des clés d’un dictionnaire.

Un exemple d’utilisation

// définition de deux objets Auteur


Auteur auteurFlaubert = new Auteur("Flaubert",new Vector());
Auteur auteurBalzac = new Auteur("Balzac",new Vector());

// définition d’un dictionnaire


Hashtable dicoAuteur = new Hashtable();
dicoAuteur.put(auteurFlaubert.nom,auteurFlaubert); // la clé est le nom de l'auteur
dicoAuteur.put(auteurBalzac.nom,auteurBalzac);

// modification d'un objet Auteur


auteurFlaubert.ajouteLivre("Madame Bovary");
auteurFlaubert.ajouteLivre("Salammbô");
auteurFlaubert.ajouteLivre("L'Education Sentimentale");
auteurBalzac.ajouteLivre("Le Père Goriot");

// itération se fait ici sur la clé


Enumeration enum = dicoAuteur.keys();
while (enum.hasMoreElements())
System.out.println(dicoAuteur.get(enum.nextElement()));
}
...

L'autre solution pour l'itération aurait pu se faire sur l'élément :

Enumeration enum = dicoAuteur.elements() ;


while (enum.hasMoreElements())
System.out.println(enum.nextElement()) ;
Des collections d’objets

Collections génériques
Les méthodes d’accès aux éléments d’une collection renvoient des objets qui ne « connaissent » que les
méthodes génériques de Object :
elementAt(int index) de Vector
nextElement() de Enumeration
get(Object clé) de Hashtable

Remarque : on peut stocker n’importe quel type d’objets dans un vecteur et dans un dictionnaire.
En contrepartie, quand on veut travailler sur les éléments de ces collections, il faut préciser au
compilateur de quoi il s’agit.

On est parfois obligé de faire un cast explicite.


Reprenons l’exemple de la classe Auteur :

...
//création de l'auteur Hergé
Vector livresHerge = new Vector();
Auteur auteurHerge = new Auteur("Hergé", livresHerge);
[ ... ] // ici, on enregistre des livres dans le vecteur livresHerge

//compte les titres qui contiennent le mot "Tintin"


int nombre = 0;
Enumeration enum = auteurHerge.livres.elements();
while(enum.hasMoreElements())
if (((String)enum.nextElement()).indexOf("Tintin") != -1)
// cast obligatoire car indexOf n’est pas une méthode d’Object
nombre ++;
System.out.println("nombre de titres contenant \"Tintin\" : "+ nombre);
...

Dans l’exemple :
livresHerge est un vecteur qui contient des chaînes de caractères.
On utilise une énumération de ce vecteur. La méthode nextElement renvoie un Object.
indexOf est une méthode de la classe String. Pour utiliser indexOf, on est obligé de faire un cast.

Remarque : Attention, ceci n’est pas forcément un avantage : on peut stocker des objets de types
différents, mais quand on les récupère, on ne sait pas forcément de quel type est l’objet récupéré (donc pb
pour le cast)...

Les tableaux sont des collections spécialisées


En revanche, si livres a été défini comme un tableau de String, le cast est inutile :

...
for (int i=0; i<tableauLivres.length; i++)
if (tableauLivres[i].indexOf("Tintin") != -1)
nombre ++;
Les collections du JDK 1.2

La nouvelle version du JDK (JDK 1.2) intègre un ensemble de collections beaucoup plus complet que
celui de la version précédente. Ces collections sont classées en trois catégories représentées par les trois
interfaces:

Set : collection d’éléments sans doublons.

List : collection d’éléments ordonnés et accessibles par index.

Map : collection d’associations clé/valeur.

Set et List proposent des méthodes communes héritées de l’interface Collection :

add(Object) : rajoute un élément dans une collection,

isEmpty() : renvoie true si la collection est vide,

size() : renvoie le nombre d’éléments contenus dans la collection,

iterator() : Crée une instance de la classe Iterator, utilisée pour itérer sur la collection avec possibilité de
suppression en cours de l’itération . L’itération est alors réalisée grâce aux méthodes hasNext(), next() et
remove() de la classe Iterator.
L'interface Collection :
public interface Collection {
// Basic Operations
int size();
boolean isEmpty();
boolean contains(Object element);
boolean add(Object element); // Optional
boolean remove(Object element); // Optional
Iterator iterator();

// Bulk Operations
boolean containsAll(Collection c);
boolean addAll(Collection c); // Optional
boolean removeAll(Collection c); // Optional
boolean retainAll(Collection c); // Optional
void clear(); // Optional

// Array Operations
Object[] toArray();
Object[] toArray(Object a[]);
}

La méthode iterator() renvoie une référence sur un objet implémentant l'interface Iterator qui
est équivalente à l'interface Enumeration de la classe Vector

public interface Iterator {


boolean hasNext();
Object next();
void remove(); // Optional
}

Une collection est une structure de données, qui peut contenir d'autres objets. Les Interfaces des
collections définissent les opérations que l'on peut réaliser. Ces interfaces sont implémentés de différentes
manières, en fonction de différents buts recherchés

Classes implémentant l’interface List


La liste (list) est une collection ordonnée qui peut contenir des éléments en double.
L'interface List, en plus des méthodes héritées de l'interface Collection possède des méthodes de
manipulation d'objet en fonction de leur indice, de recherche d'éléments et d'obtention d'un itérateur de
List (ListIterator) pour accéder aux éléments.
public interface List extends Collection {
// Positional Access
Object get(int index);
Object set(int index, Object element); // Optional
void add(int index, Object element); // Optional
Object remove(int index); // Optional
abstract boolean addAll(int index, Collection c); // Optional

// Search
int indexOf(Object o);
int lastIndexOf(Object o);

// Iteration
ListIterator listIterator();
ListIterator listIterator(int index);

// Range-view
List subList(int from, int to);
}
Vector : un tableau redimensionnable synchronisé (cf chp. Thread).
ArrayList : un tableau redimensionnable, non synchronisé dont certaines fonctions
nécessitent un temps linéaire d’exécution (manque de performance pour des collections de
grande taille).
LinkedList : liste chaînée avec méthodes d’accès au premier et au dernier élément et
fournissant des méthodes de base pour l’implémentation des files d’attentes et des piles.

// Ce programme est tiré de l'ouvrage Deitel et Deitel : Comment Programmer en Java


// Utilisation de l’interface Collection.
import java.util.*;
import java.awt.Color;

public class CollectionTest {


private String couleurs[] = { "rouge", "blanc", "bleu" };

public CollectionTest()
{
ArrayList uneListe = new ArrayList();

uneListe.add( Color.magenta ); // ajouter un objet Color.

for ( int k = 0; k < couleurs.length; k++ )


uneListe.add( couleurs[ k ] );

uneListe.add( Color.cyan ); // ajouter un objet Color.

System.out.println( "\nArrayList: " );


for ( int k = 0; k < uneListe.size(); k++ )
System.out.print( uneListe.get( k ) + " " );

retirerChaines( uneListe );

System.out.println( "\n\nArrayList aprés appel de" +


" retirerChaines: " );
for ( int k = 0; k < uneListe.size(); k++ )
System.out.print( uneListe.get( k ) + " " );
}

public void retirerChaines( Collection c )


{
Iterator i = c.iterator(); // obtenir un itérateur.

while ( i.hasNext() ) // boucler tant que


// la collection a des éléments.

if ( i.next() instanceof String )


i.remove(); // retirer objet String.
}

public static void main( String args[] )


{
new CollectionTest();
}
}
Classes implémentant l’interface Map
Hashtable : associe une clé à chaque élément (voir JDK 1.1) .
HashMap : basée sur Hashtable, elle autorise null pour les valeurs mais pas pour les clés.
L’ordre d’entrée des clés n’est pas conservé.
ArrayMap : implémentation avec clés, basée sur ArrayList et performante seulement pour
des petites collections.
TreeMap : ses clés sont triées en ordre ascendant.
Classes implémentant l’interface Set
HashSet : basée sur Hashtable et ne permet pas d’élément null.
import java.util.*;

public class FindDups {


public static void main(String args[]) {
Set s = new HashSet();
for (int i=0; i<args.length; i++)
if (!s.add(args[i]))
System.out.println("Duplicate detected: "+args[i]);

System.out.println(s.size()+" distinct words detected: "+s);


}
}

ArraySet : basée sur ArrayList , permet les éléments null et performante seulement pour
des petits ensembles.

La classe Arrays
Cette classe permet la manipulation de tableaux. La classe Arrays fournit des méthodes de recherche dans
un tableau trié (binarySearch), de comparaison de tableaux (equals), de remplissage de tableau (fill) et de
tri de tableau (sort).

// Ce programme est tiré de l'ouvrage Deitel et Deitel : Comment Programmer en Java


// Utilisation des tableaux en Java.
import java.util.*;

public class UtilisationTableaux {


private int valeursInt[] = { 1, 2, 3, 4, 5, 6 };
private double valeursDouble[] = { 8.4, 9.3, 0.2, 7.9, 3.4 };
private int rempliDInt[], copieValeursInt[];

public UtilisationTableaux()
{
rempliDInt = new int[ 10 ];
copieValeursInt = new int[ valeursInt.length ];
Arrays.fill( rempliDInt, 7 ); // remplir avec des 7.
Arrays.sort( valeursDouble ); // trier valeursDouble.
System.arraycopy( valeursInt, 0, copieValeursInt,
0, valeursInt.length );
}

public void afficheTableaux()


{
System.out.print( "valeursDouble: " );
for ( int k = 0; k < valeursDouble.length; k++ )
System.out.print( valeursDouble[ k ] + " " );

System.out.print("\nvaleursInt: " );
for ( int k = 0; k < valeursInt.length; k++ )
System.out.print( valeursInt[ k ] + " " );

System.out.print("\nrempliDInt: " );
for ( int k = 0; k < rempliDInt.length; k++ )
System.out.print( rempliDInt[ k ] + " " );

System.out.print("\ncopieValeursInt: " );
for ( int k = 0; k < copieValeursInt.length; k++ )
System.out.print( copieValeursInt[ k ] + " " );

System.out.println();
}
public int rechercheUnInt( int valeur )
{
return Arrays.binarySearch( valeursInt, valeur );
}

public void afficheEgalite()


{
boolean b = Arrays.equals( valeursInt, copieValeursInt );

System.out.println( "valeursInt " + ( b ? "==" : "!=" )


+ " copieValeursInt" );

b = Arrays.equals( valeursInt, rempliDInt );


System.out.println( "valeursInt " + ( b ? "==" : "!=" )
+ " rempliDInt" );
}

public static void main( String args[] )


{
UtilisationTableaux u = new UtilisationTableaux();

u.afficheTableaux();
u.afficheEgalite();

int n = u.rechercheUnInt( 5 );
System.out.println( ( n >= 0 ? "Trouv‚ 5 … l'‚l‚ment " + n :
"5 introuvable" ) + " dans valeursInt" );
n = u.rechercheUnInt( 8763 );
System.out.println( ( n >= 0 ? "Trouv‚ 8763 … l'‚l‚ment "
+ n : "8763 introuvable" )
+ " dans valeursInt" );
}
}

valeursDouble: 0.2 3.4 7.9 8.4 9.3


valeursInt: 1 2 3 4 5 6
rempliDInt: 7 7 7 7 7 7 7 7 7 7
copieValeursInt: 1 2 3 4 5 6
valeursInt == copieValeursInt
valeursInt != rempliDInt
Trouvé 5 à l'élément 4 dans valeursInt
8763 introuvable dans valeursInt
La classe Collections
La classe Collections fournit des méthodes statiques qui manipulent des collections (des List, Set ou
Map).

méthode sort
import java.util.*
class NameSort {
public static void main(String args[]) {
String n[] = {"bobo","toto","titi"};
List l = Arrays.asList(n);
Collections.sort(l);
System.out.println(l);
}
}

[bobo, titi, toto]

Méthode shuffle
// Ce programme est tiré de l'ouvrage Deitel et Deitel : Comment Programmer en Java
// Utilisation de l’algorithme shuffle.
import java.util.*;

class Carte {
private String face;
private String coul;

public Carte( String face, String coul )


{
this.face = face;
this.coul = coul;
}

public String getFace() { return face; }

public String getCoul() { return coul; }

public String toString()


{
StringBuffer tamp = new StringBuffer( face + " de " + coul );

tamp.setLength( 20 );
return ( tamp.toString() );
}
}

// Définition de la classe Cartes.


public class Cartes {
private static String couleurs[] = { "Coeur", "TrŠfle",
"Carreau", "Pique" };
private static String faces[] = { "As", "Deux", "Trois",
"Quatre", "Cinq", "Six",
"Sept", "Huit", "Neuf",
"Dix", "Valet", "Dame",
"Roi" };
private List laListe;

public Cartes()
{
Carte jeuDeCartes[] = new Carte[ 52 ];

for ( int k = 0; k < jeuDeCartes.length; k++ )


jeuDeCartes[ k ] = new Carte( faces[ k % 13 ],
couleurs[ k / 13 ] );

laListe = Arrays.asList( jeuDeCartes ); // obtenir liste.


Collections.shuffle( laListe ); // brasser jeuDeCartes.
}

public void printCartes()


{
int moitie = laListe.size() / 2 - 1;

for ( int k = 0, k2 = moitie; k <= moitie; k++, k2++ )


System.out.println( laListe.get( k ).toString() +
laListe.get( k2 ) );
}

public static void main( String args[] )


{
new Cartes().printCartes();
}
}
méthodes reverse, fill, copy, max et min
// Ce programme est tiré de l'ouvrage Deitel et Deitel : Comment Programmer en Java
// Utilisation des algorithmes reverse, fill, copy, min et max.
import java.util.*;

public class Algorithmes1 {


private String lettres[] = { "P", "C", "M" }, copieLettres[];
private List laListe, copieListe;

public Algorithmes1()
{
laListe = Arrays.asList( lettres ); // obtenir liste.
copieLettres = new String[ 3 ];
copieListe = Arrays.asList( copieLettres );

System.out.println( "Affichage statistiques initiales: " );


afficherStatistiques( laListe );

Collections.reverse( laListe ); // ordre inverse.


System.out.println( "\nAffichage statistiques aprŠs " +
"appel de reverse: " );
afficherStatistiques( laListe );

Collections.copy( copieListe, laListe ); // copie liste.


System.out.println( "\nAffichage statistiques aprŠs " +
"copie: " );
afficherStatistiques( copieListe );

System.out.println( "\nAffichage statistiques aprŠs " +


"appel de fill: " );
Collections.fill( laListe, "R" );
afficherStatistiques( laListe );
}

private void afficherStatistiques( List listRef )


{
System.out.print( "La liste contient: " );
for ( int k = 0; k < listRef.size(); k++ )
System.out.print( listRef.get( k ) + " " );

System.out.print( "\nMax: " + Collections.max( listRef ) );


System.out.println( " Min: " +
Collections.min( listRef ) );
}

public static void main( String args[] )


{
new Algorithmes1();
}
}

Affichage statistiques initiales:


La liste contient: P C M
Max: P Min: C

Affichage statistiques aprés appel de reverse:


La liste contient: M C P
Max: P Min: C

Affichage statistiques aprés copie:


La liste contient: M C P
Max: P Min: C

Affichage statistiques aprés appel de fill:


La liste contient: R R R
Max: R Min: R
La méthode binarySearch
// Ce programme est tiré de l'ouvrage Deitel et Deitel : Comment Programmer en Java
// Utilisation de l’algorithme binarySearch.
import java.util.*;

public class BinarySearchTest {


private String couleurs[] = { "rouge", "blanc", "bleu",
"noir", "jaune",
"mauve", "brun_clair", "rose" };
private ArrayList uneListe; // reference à un ArrayList.

public BinarySearchTest()
{
uneListe = new ArrayList( Arrays.asList( couleurs ) );
Collections.sort( uneListe ); // trier l’ArrayList.
System.out.println( "ArrayList trié: " + uneListe );
}

public void afficherResRecherche()


{
afficherResRechAssist( couleurs[ 3 ] ); // premier élément.
afficherResRechAssist( couleurs[ 0 ] ); // élément millieu.
afficherResRechAssist( couleurs[ 7 ] ); // dernier élément.
afficherResRechAssist( "asticot" ); // sous le plus petit.
afficherResRechAssist( "chèvre" ); // n’existe pas.
afficherResRechAssist( "zèbre" ); // n’existe pas.
}

private void afficherResRechAssist( String cle )


{
int resultat = 0;

System.out.println( "\nRecherche de: " + cle );


resultat = Collections.binarySearch( uneListe, cle );
System.out.println( ( resultat >= 0 ? "Trouvé à l'index "+ resultat
: "Introuvable (" + resultat + ")" ) );
}

public static void main( String args[] )


{
new BinarySearchTest().afficherResRecherche();
}
}

ArrayList trié: [blanc, bleu, brun_clair, jaune, mauve, noir, rose, rouge]

Recherche de: noir


Trouvé à l'index 5
Recherche de: rouge
Trouvé à l'index 7

Recherche de: rose


Trouvé à l'index 6

Recherche de: asticot


Introuvable (-1)

Recherche de: chèvre


Introuvable (-4)

Recherche de: zèbre


Introuvable (-9)

Exercices

Utilisation de StreamTokenizer
Ecrire un programme qui demande à l'utilisateur d'écrire une phrase. Cette phrase est analysée par la
classe StreamTokenizer puis placé dans une liste (ArrayList) sous le nom de list1.
Copier cette méthode dans list2.
On demande de classer (méthode sort()) cette liste puis de faire une recherche (méthode binarySearch())
d'un mot entré par l'utilisateur.
Reprendre list1 et remplacer toutes les occurences de "est" par "sont".
Créer un tableau de String de type Arrays et le remplir de "un".
Créer une Liste list3 qui prend la référence du tableau créé précédemment méthode asList().
Puis créer une liste de type HashSet() qui permet de ne créer que de mots différents à partir de list1.
Utilisation des classes ArrayList et HashTable dans la structure de données Entreprise vu au chapitre
VII
On désire reprendre les classes Personnes et les classes Ouvrier, Cadre et patron et reprendre la classe
Entreprise du chapitre VII.
On désire utiliser la classe HashTable qui permet d'entrer dans la structure un Objet suivi de sa clé. On
utilisera comme clé le numéro de sécurité sociale. Ce numéro de sécurité sociale est placé dans une
classe, pour permettre la conformité
La classe Entreprise

Une entreprise possède des salariés .Ces salariés sont des Personne. Une Personne peut être un Patron, un
ouvrier ou un Cadre. Une Personne est définie par sont numéro de sécurité sociale qui constitue sa clé dans
la structure de donnée. Une Personne ne peut pas se trouver 2 fois dans l'entreprise.

Il faut pouvoir ajouter ou retirer des Personnes

Il faut pouvoir afficher les Personnes qui composent l'Entreprise

Il faut savoir si l'Entreprise est vide et le nombre de salariés.

Il doit être possible de savoir si une Personne existe dans l'Entreprise

L'Entreprise doit être capable de dire combien il y a de cadres, d'ouvriers et de Patrons dans l'entreprise et
renvoyer une liste de Personne (du type List).

L'entreprise doit pouvoir licencié tout ses salariés.

on devra être capable de faire des recherches dans la base de données: ainsi il sera possible de rechercher
une personne par son nom, son age ou son numéro de sécu.

Entreprise
private Hashtable ent;

public Entreprise();{ent= new Hashtable();}


public void ajouter (Personne p){
ent.(p.numSecu.toString(),p);};
public void retirer (Personne p);
public void afficher();
public boolean isExiste(Persone p);
public boolean isVide();
public int chercher (Collection c,String salarie);
public void toutEffacer();
public int getNbPersonne();
public int chercheAge(Collection c, int age);
public int chercheNum(Collection c, String num);
public int chercheNom(Collection c, String nom);

remarque :
Les 4 méthodes chercher... renvoient le nombre de personne trouvé et place ces personnes dans une
collection. Cette collection (générique) implémentera la classe Hashtable.

Exercice : Ecrire le code de cette classe Entreprise. On utilisera les classes Personne ,NumSecu, Employe,
Cadre et Patron séjà écrite dans le chapitre VII.
System et Runtime
Les classes System et Runtime fournissent une interface à certaines fonctionnalités du système de façon
indépendante de la plate-forme.

Flux standard d’entrées sorties


in flux standard d’entrée (par défaut le clavier).
out flux standard de sortie (par défaut la console).
err flux standard d’erreur (par défaut la console).

...
char carLu = System.in.read();
System.out.println("caractère lu : " + carLu);

Exécution d’un processus système


La méthode d’instance exec de la classe Runtime permet d’exécuter un processus système.

...
try{
Runtime.getRuntime().exec("notepad.exe") ;
}catch(IOException e){...}
...

Interrompre l’exécution d’un programme


La méthode de classe exit de la classe System.

...
System.exit(1);
...

Copier des tableaux :System.ArrayCopy


Cette méthode a déjà été utilisée dans le chapitre VIII sur les collections.
Créer et manipuler une instance de Date
Une date est représentée en Java par une instance de la classe Date du package java.util.
La classe Date fournit un constructeur par défaut qui permet d’instancier des objets Date
initialisés avec la date courante. Celle-ci est obtenue en tenant compte du fuseau horaire et
des particularités régionales du calendrier (classe Calendar).
Des opérateurs de comparaison : after(Date), before(Date), equals(Object) et
compareTo(Date) permettent de comparer les dates.
Les autres méthodes de la classe Date sont conservées pour la compatibilité avec le JDK
1.0, mais ne doivent plus être utilisées.
La plupart des méthodes de la classe Date sont obsolètes. Celles qui sont utilisées sont les
méthodes de la classe Calendar.

La classe Calendar
Calendar dispose de champs moins précis que date : YEAR, MONTH, DAY, HOUR ect.. .
alors que Date va jusqu’à la milliseconde.
Calendar interprète une date suivant les règles imposées par un calendrier régional précis.
Une implémentation déjà fournie pour notre zone est GregorianCalendar qui est une sous
classe de Calendar.
On obtient une instance de Calendar par la méthode getInstance() :

Calendar maintenant = Calendar.getInstance() ;

La classe TimeZone
représente un fuseau horaire
contient une methode getDefault() qui récupère le fuseau horaire courant
contient une methode getTimeZone() qui permet de récupérer un fuseau horaire donné.
L’exemple du JDK1.2 est :

TimeZone fuseauPacifique = TimeZone.getTimeZone(«PST»);


// PST signifie « Pacific Standard Time », c’est donc le fuseau horaire de la côte pacifique.

Initialiser une date


On peut créer une date différente de la date du jour en utilisant une instance de la classe
GregorianCalendar qui est une classe dérivée de Calendar. Cette instance de GregorianCalendar est
initialisée à la date et au temps voulus grâce à un des nombreux constructeurs de GregorianCalendar.

public GregorianCalendar(int year, int month, int date,


int hour, int minute) ;

On récupère une date par la méthode getTime() de la classe Calendar.

GregorianCalendar monCalendrier = new


GregorianCalendar(1998,Calendar.AUGUST,20,9,45);
Date nouvelleDate = monCalendrier.getTime();
System.out.println(nouvelleDate);

L’affichage obtenu est : « Thu Aug 20 09:45:00 CEST 1998 ».


Une date peut être affichée selon un autre format moyennant un objet de type DateFormat ou
SimpleDateFormat.
Les classes associées au types primitifs
Pour chaque type de données primitif, tel que int ou double, le package java.lang (importé par défaut)
propose une classe correspondante (respectivement Integer ou Double). Ces classes que l'on appelle des
emballages de type, fournissent des méthodes de traitements de valeurs telles que la conversion d'un
String en une valeur ou d'une valeur en une String.

Type primitifs classes associées


byte Byte
short Short
int Integer
float Float
double Double
char Character
"grands nombres entiers" BigInteger
"grands nombres flottants" BigDecimal
Number

Object

Classe
Object Number

Charactre Short Byte Integer Floa Double BigIntege BigDecimal

La classe Integer
Les méthodes de la classe Integer

Méthodes de la classe Integer Commentaires


int parseInt(String) Convertit une chaine de caractère en un entier
Exemple :int x = Integer.parseInt("34");// valeur de x=34
String toBinaryString(int) Convertit un entier en une chaine de caractère image binaire de l'entier
Exemple :System.ou.println(Integer.toBinaryString(34)); affiche
0000.0000.0000.0000.0000.0000.0010.0010.
String toHexString(int) Convertit un entier en une chaine de caractère image hexadécimale de l'entier
Exemple :System.ou.println(Integer.toHexString(34)); affiche 00000022
String toString(int) Convertit un entier en une chaine de caractère
Exemple :String s = Integer.toStringt(34);// valeur de s="34"
String toString() Convertit un entier en une chaine de caractère
Exemple :System.out.println( new Integer(24).toString)); affiche 24
Integer valueOf(String) Convertit une chaine en une référence de la classe Integer
Exemple : Integer x= Integer.valueof("34");
Un exemple

Class EssaiInteger{
public static void main(java.lang.String[] args) {
int j=2; // équivalent à Integer j= new Integer(2);
String s ="15";
System.out.println(""+Integer.parseInt(s)+" "+"
"+Integer.toHexString(Integer.parseInt(s))+" "+ Integer.toBinaryString(Integer.parseInt(s)));
System.out.println(Integer.toString(3290));
// autre solution System.out.println(""+3290);
}
}

15 0000000F 0000.0000.0000.0000.0000.0000.0000.1111.
3290

La classe Double
Les méthodes de la classe Double

Méthodes de la classe Double Commentaires


parseDouble(String) Convertie une chaine de caractère en un double
Exemple :double x = Double.parseDouble("34.5");// valeur de x=34.5
Double valueOf(String) Renvoi une référence sur un objet de type Double
Exemple :Double x = Double.parseDouble("34.5");
toString(double) Convertit un double en une chaine de caractère
Exemple :String s = Double.toStringt(34.5);// valeur de s="34.5"
toString() Convertie un double en une chaine de caractère
Exemple :System.out.println( new Double(24.5).toString)); affiche 24.5

Un exemple

Class EssaiDouble{
public static void main(java.lang.String[] args) {
String s ="15.";
System.out.println(""+Double.parseDouble(s));
double x=Double.toString(34.9);
System.out.println(""+x); // appel à la méthode toString() de Double
x=x/0;
System.out.println("Aprés division par 0"+x); // affiche infinity

}
}

15.0
34.9
infinity
Les exceptions
Introduction
Jusqu'à présent la gestion des erreurs dans un programme se faisait directement dans le code
de la fonction par une gestion avec des if else. Cette gestion des erreurs rajoute des lignes de
code. Ainsi une méthode qui pourrait être codée sur quelques lignes, sans gestion d'erreurs
peut devenir importante quand on lui rajoute la gestion des erreurs. La gestion des erreurs
toujours été un problème pour les programmeurs. Cette gestion d'erreur si elle n'est prise en
compte dés l'analyse peut engendrer des cout de développement important. Cette gestion
d'erreur de plus complique le programme et donc la compréhension et la maintenance du
programme.
Au début des années 1990, C++ est le premier langage à mettre en place un traitement des
exceptions. Java a entièrement repris les mécanismes de gestion des erreurs de C++.
Le but de la gestion des exceptions est la séparation du code avec celui de la gestion des
erreurs.
La gestion des exception permet de créer un programme robuste, c'est à dire exempt le plus
possible d'arrêt brutal du programme.

Mon premier exemple

Sans traitement d'erreurs :


Essayons dans ce premier exemple de voir l'effet de 2 erreurs courantes sur le fonctionnement
du programme :

import entreesortie.*;
class Essai1{
public static void main(String [] arg){
int [] tabInt= new int[5];

// cette instruction provoque l'arrêt brutal du programme


for(int i= 0;i<6;i++) System.out.println("tabInt["+i+"]="+tabInt[i]);

System.out.println("Entrer nombre1 :");


int nombre1=Console.getInt();
System.out.println("Entrer nombre2 :");
int nombre2=Console.getInt();
int res= nombre1/nombre2; // si nombre2=0 : arrêt brutal du
programme
System.out.println("Résultat de la division nombre1/nombre2="+ res);
}
}

Dans le programme ci-dessus, il y a 2 erreurs à l'exécution :

écriture en dehors des limites d'un tableau avec le message : uncaught exception
ArrayIndexoutOfBoundsException

division entière par 0 avec le message uncaught exception ArithmeticException.

Traitement d'erreur classique


Pour ne pas avoir de problème il suffit de tester les valeurs entrées :

import entreesortie.*;
class Essai2{
public static void main(String [] arg){
int [] tabInt= new int[5];

// l'appel à tabInt.length permet d'éviter l'erreur précédente


for(int i= 0;i<tabInt.length;i++)
System.out.println("tabInt["+i+"]="+tabInt[i]);

System.out.println("Entrer nombre1 :");


int nombre1=Console.getInt();
System.out.println("Entrer nombre2 :");
int nombre2=Console.getInt();
if (nombre2==0) System.out.println("Division par zéro");
else{
int res= nombre1/nombre2;
System.out.println("Résultat de la division nombre1/nombre2="+ res);
}
}
}

Le problème posé par cette solution est une augmentation des lignes de code et une
complication du programme.
De plus, si l'on oublie de tester une valeur, il y aura arrêt brutal du programme (bugs de
programmes).

La solution : gestion des exceptions bloc try...catch


Dans l'exemple ci-dessous, on sépare le code du programme du code de gestion des erreurs.
Ainsi le code du programme se trouve dans les blocs try et la gestion des erreurs est faite dans
les blocs catch.
Le système essai d'exécuter le code dans les blocs try et si une exception est levée seul le code
dans les blocs catch est exécuté.

import entreesortie.*;
Class Essai3{
public static void main(String [] arg){
int [] tabInt= new int[5];
try{
for(int i= 0;i<6;i++) System.out.println("tabInt["+i+"]="+tabInt[i]);
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println(e.toString());
}

try{

System.out.println("Entrer nombre1 :");


int nombre1=Console.getInt();
System.out.println("Entrer nombre2 :");
int nombre2=Console.getInt();
int res= nombre1/nombre2;
System.out.println("Résultat de la division nombre1/nombre2="+ res);
}
catch(ArithmeticException e){
System.out.println("Division par 0:"+e.toString());
}
}
}

Le résultat de ce programme est donné ci-dessous :


tabInt[0]=0
tabInt[1]=0
tabInt[2]=0
tabInt[3]=0
tabInt[4]=0
java.lang.ArrayIndexOutOfBoundsException
Entrer nombre1 :
45
Entrer nombre2 :
0
Division par 0:java.lang.ArithmeticException

Revenons à la classe Console donnée dans le chapitre I.13.1


Nous avons jusqu'à présent utilisé les classes de classe Console dans le package entreesortie
pour gérer les entrées/sorties. Ces méthodes permettent d'afficher ou de lire des chaînes de
caractères comme des entiers ou nombre flottants.

Reprenons le code de la méthode statique Console.getInt()


La méthode getInt() utilise la gestion des exceptions. En effet la méthode
Integer.parseInt(String) provoque une exception de type NumberFormatException à chaque
fois que la conversion est impossible (par exemple Integer.parseInt("toto")).
La méthode appelle alors le bloc qui suit l'instruction catch(). On a alors appel de nouveau à la
méthode getInt() : appel récursif.

public static int getInt() {


int val=0;
try {
val= Integer.parseInt(getString());
} catch (NumberFormatException e) {
System.out.println("Vous devez entrer un entier svp :");
val=getInt();
}
return val;
}

Syntaxe try catch


La syntaxe est la suivante :
try{
//code qui peut générer des exception
}
catch( TypeException1 e){
//code exécuté si une Exception1 est lancée
}
catch( TypeException2 e){
//code exécuté si une Exception2 est lancée
}
finally{
//code tout le temps exécuté à la fin
}

Le code à l'intérieur d'un bloc try peut lancer des exceptions. Ces exceptions peuvent être
interceptées dans le bloc qui suit le catch. Il peut y avoir plusieurs catch interceptant
différentes exceptions. En argument du catch on place le type d'exception que l'on veut
intercepter.
Une exception est une classe. et dans le code ci-dessus, le système passe en argument de catch
une référence sur un objet de la classe TypeException. On a donc accés aux méthodes de la
classe Exception. il suffit d'appeler e.methode().
si une exception apparait dans un bloc try, le code qui suit n'est pas exécuté. Seul est exécuté
le code dans le bloc catch.
Si la clause finally existe, quel que soit la configuration du programme, le bloc suivant finally
est exécuté.
Les Exceptions : des classes
Les exceptions sont des classes et donc peuvent bénéficier de l'héritage. Toutes les exceptions
héritent de la classe Throwable.

Extrait de la hiérarchie des classes

Error encapsule des erreurs liées à la machine virtuelle.

Exception définit des erreurs que le programmeur peut gérer lui-même.


RunTimeException et Error sont des exception non controlées.Les exceptions non contrôlées
peuvent être capturées, mais elles n’ont pas à être déclarées dans l’en-tête des méthodes avec
la clause throws.
Extrait des Exceptions dans les packages java.io et java.lang

Quelques méthodes de Throwable

2 constructeurs disponibles : sans paramètre, ou avec une chaîne de caractères contenant un


message d’erreur spécifique.
getMessage() retourne le message d’erreur s’il existe.

toString() retourne la classe à l'origine de l'exception.

printStackTrace() permet d’afficher la pile d’exécution lors de l’interception de l’erreur. Elle


est très utile pour le débogage.

Quelques exemples d’exceptions non contrôlées (qui héritent de RunTimeException) :

ArithmeticException (par exemple quand il y a eu une division par 0)

NullPointerException (quand on essaye d’appliquer une méthode sur un objet null)

SecurityException (quand on essaye d’écrire sur le disque à partir d’une applet par exemple)

ArrayIndexOutOfBoundsException (quand on essaye d’accéder à un élément d’un tableau qui


n’existe pas)
La gestion de toutes les exceptions
jusqu'à présent , nous avons tester les exceptions spécifiques telles que ArithmeticException
et ArrayIndexOutOfBoundsException. Dans ce cas le gestionnaire d'exception lance le code
suivant le catch seulement si ce type d'erreur est apparue.
Grace à l'héritage, il est possible de lancer le code suivant le catch de façon plus générique,
par exemple pour tous les types d'exceptions.
Dans le programme ci-dessous, on intercepte tous les types d'exception catch(Exception e)
sans se préocupper de la source de l'exception.

import entreesortie.*;
Class Essai3{
public static void main(String [] arg){
int [] tabInt= new int[5];
try{
for(int i= 0;i<6;i++) System.out.println("tabInt["+i+"]="+tabInt[i]);
}
catch(Exception e){
System.out.println(e.toString());
}

try{

System.out.println("Entrer nombre1 :");


int nombre1=Console.getInt();
System.out.println("Entrer nombre2 :");
int nombre2=Console.getInt();
int res= nombre1/nombre2;
System.out.println("Résultat de la division nombre1/nombre2="+ res);
}
catch(Exception e){
System.out.println("e.toString());
}
}
}

tabInt[0]=0
tabInt[1]=0
tabInt[2]=0
tabInt[3]=0
tabInt[4]=0
java.lang.ArrayIndexOutOfBoundsException
Entrer nombre1 :
45
Entrer nombre2 :
0
java.lang.ArithmeticException

Remarque :
Il est possible de connaitre l'exception levée grace à l'opérateur insatnceof :
if(e instanceof ArrayIndexOutOfBoundsException)System.out.println("dépasement de
capacité du tableau");

Créer sa propre exception


Jusqu'à présent nous n'avons utilisé que les Exceptions prédéfinies par le sytème. Que se
passe-t-il si nous voulons créer nous-même, nos propres Exceptions.

Code de l'exception ArrayOutOfBoundsException


Le code de cet exception est assez simple. On voit que cette classe hérite de la classe
IndexOutOfBoundsException qui elle-même hérite de la classe RunTimeException. Comme
pour toutes les exceptions, on retrouve le constructeur par défaut et d'autres constructeurs. Ces
constructeurs font appel au constructeur de la classe mère.

package java.lang;
public
class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException {
public ArrayIndexOutOfBoundsException() {
super();
}
public ArrayIndexOutOfBoundsException(int index) {
super("Array index out of range: " + index);
}
public ArrayIndexOutOfBoundsException(String s) {
super(s);
}
}

Un autre exemple la méthode setDepartement de la classe Individu, chapitre III.1


Considérons à nouveau l'exemple de la classe Individu :
Dans le chapitre III.1.2 nous avions étudié la classe Individu qui répondait au cahier des
charges suivant :

un individu possède un nom en majuscule et habite dans un département dont le numéro est
compris entre 1 et 95.

si le numéro de département est inconnu celui-ci prend la valeur 0

le fait d'entrer un numéro inférieur à 0 ou supérieur à 95 est une erreur.

La gestion des erreurs dans la classe setDepartement se faisait grace à un test sur la valeur unDept.

class Individu {
private String nom; // nom de l'individu
private byte departement; // département de résidence

public Individu (String leNom, int leDept) {


nom = leNom.toUpperCase();
setDepartement(leDept);
}
// On définit un constructeur par défaut
public Individu () {
this("nom inconnu",0);
}
public void afficherSurEcran() {
System.out.println("Je m'appelle " + nom);
if (departement != 0)
System.out.println("j'habite dans le " + departement);
}

private byte setDepartement(int unDept) {


if (unDept < 0 || unDept > 95) {
System.out.println ("département incorrect "+unDept);
}
else departement=(byte) unDept; // le cast est indispensable, sans perte
d'information
}

La méthode setDepartement permet la gestion de l'erreur de saisie.

Une autre solution : générer une Exception


Une autre solution, pour la classe individu est de faire de la gestion d'erreur en utilisant les
exceptions. Dans ce cas nous devons créer la classe DepartementException comme ci-dessous
:

public class DepartementException extends Exception {


private int errDep; // valeur de département erronée
public DepartementException (int err) {
errDep = err;
}
public String toString() {
return new String("département erroné : " + errDep);
}
}

La méthode setDepartement lève une exception:


On désire lancer une exception dés que la valeur unDept est en dehors des limites fixées

private byte setDepartement(int unDept)


throws DepartementException {
if (unDept < 0 || unDept > 95)
throw new DepartementException(unDept);
// throw transmet l'exception au gestionnaire d'exceptions, celui-ci termine
// l'exécution de la méthode et retourne à l'appelant pour soumettre
l'exception
// ici, on n'a pas eu d'exception, on est sûr que le n° de département est
valide
departement=(byte)unDept;
}
Avec la technique des exceptions, le code qui constate l'incident n'est plus obligé de le traiter :
il envoie un signal que le gestionnaire d'exceptions va faire remonter dans la pile d'exécution
jusqu'à trouver un candidat pour traiter le signal ou jusqu'à ce que la pile soit vide (arrêt du
programme sur exception non récupérée)
Bien distinguer :
• throws, avec un « s » :
à ajouter dans l’en-tête de la méthode (cette information est destinée au compilateur)
signale que la méthode peut lever ou propager une exception.
• throw, sans « s » :
action de lever une exception.
remarque : on peut lever une exception déjà existante, ou bien l’instancier comme ici
(cas le plus courant)

Une obligation : propager ou capturer


Maintenant, toute méthode susceptible de déclencher l’exception doit :

soit propager elle-même l'exception,

soit récupérer cette exception, et traiter l'incident.

Insuffisance d’un traitement d’erreur classique


Tout traitement, quel qu'il soit n'a aucun degré de liberté vis à vis de l'erreur détectée par
setDepartement.
Le code qui a la responsabilité de la détection d'une erreur ne devrait pas avoir à décider du
traitement de cette erreur.

Exemple :
method1 { (la flèche signifie : appel de )
call method2; methode1
}
method2 { methode2
call method3;
} methode3
method3 {
call readFile;
}

Supposons que la méthode1 appelle la méthode2 qui elle-même appelle la méthode3.


Supposons que la méthode3 consiste à ouvrir une boîte de dialogue dans laquelle l’utilisateur
doit saisir le nom d’un fichier. S’il fait une faute de frappe dans le nom, on ne veut pas
interrompre tout le programme juste pour cette erreur de saisie ! (on perdrait tout ce que
le programme a fait avant...)
On a donc besoin de faire remonter l'erreur de la méthode3 vers la méthode1 qui traitera cette
erreurs

Code classique de gestion d'erreur :

method1 {
errorCodeType error;
error = call method2;
if (error)
doErrorProcessing;
else
proceed;
}
errorCodeType method2 {
errorCodeType error;
error = call method3;
if (error)
return error;
else
proceed;
}
errorCodeType method3 {
errorCodeType error;
error = call readFile;
if (error)
return error;
else
proceed;
}

Il apparaît que la gestion des erreurs en programmation classique complique le code du


programme. La propagation des erreurs ne peut se faire en programmation classique qu'avec
un code d'erreur renvoyé par chacunes des fonctions.
La solution : les exceptions

method1 {
try {
call method2;
} catch (exception) {
doErrorProcessing;
}
}
method2 throws exception {
call method3;
}
method3 throws exception {
call readFile;
}

Dans le code ci-dessus, il y a propagation de l'erreur qui apparaît dans la méthode3 et ceci
grace au mot clé throws jusqu'au traitement de cette erreur dans la méthode1 grace au code try
catch
Propager une exception
Les constructeurs font un appel direct à setDepartement. Une exception peut donc être levée
dans les constructeurs.
Signalons-le au compilateur :

public Individu (String leNom, int leDept) throws DepartementException {


nom = leNom.toUpperCase();
setDepartement(leDept);
}
// On définit un constructeur par défaut
public Individu (){
this("nom inconnu",0);
}

L’exception n’est pas traitée ici, elle est seulement renvoyée au code appelant.
Capturer une exception
La méthode main suivante utilise le constructeur de la classe Individu, qui appelle
setDepartement.
Elle peut donc lever indirectement une exception.

public static void main(String args[]) {


Individu p1;
... // ici, on obtient une valeur pour x
try { // une exception levée dans un bloc try est capturée
p1 = new Individu("Marianne",98);
System.out.println(p1.getDepartement());
}
catch (DepartementException e) { //on indique comment traiter l’exception
System.out.println("n° de département incorrect");
p1 = new Individu("Marianne");
}
p1.afficherSurEcran();
} // main
Plusieurs clauses catch pour un même bloc try
Soit NomException une nouvelle classe d’exception pour gérer les erreurs sur le nom d’un
individu. Une exception de ce type est créée dans la méthode validerNom lorsque le nom est
invalide.
Dans la méthode suivante, on souhaite générer un affichage différent suivant l’exception qui a
été levée, puis relancer l’exception :

public void mmm() throws DepartementException, NomException{


Individu p1; String s; int x;
... // ici, on obtient une valeur pour x et s
try {
p1 = new Individu(s,x);
}
catch (DepartementException d) { //traitement partiel de l’exception
System.out.println("n° département incorrect");
throw d; // on relance l’exception
}
catch (NomException n) { //traitement partiel de l’exception
System.out.println("nom incorrect");
throw n; // on relance l’exception
}
}

Quand le gestionnaire d'exceptions entre dans un bloc catch, les catch suivants ne sont pas
examinés.
A chaque fois qu’on appelle setDepartement() dans une méthode, une exception est
susceptible d’être levée.
Il faut signaler cela au compilateur avec throws..., sinon ça compile pas.

bloc try{ } : On veut soumettre cette partie du code au gestionnaire d’exceptions.

paramètres de catch : On peut passer en paramètre un objet DepartementException, au cas


où on voudrait récupérer des informations sur l’erreur.
catch() { } : On indique entre les accolades ce qu’il y a à faire pour traiter l’erreur.
Dans estDansDepartement, on ne souhaite pas avoir un affichage particulier en cas
d’exception, ni un arrêt de l’exécution, on veut un traitement très léger : la méthode renvoie
faux et c’est tout.

Plusieurs clauses catch


Des exceptions de types variés peuvent être levées dans un bloc try. On indique quoi faire
dans chaque cas.

Remarque : ici, on utilise throw d; pour indiquer que l’exception doit continuer à être
propagée dans la pile. La méthode mmm n’est pas apte à traiter complètement l’exception.
Pour relancer l’exception, la méthode doit être déclarer avec la clause throws.
Une hiérarchie d’exceptions
Les exceptions peuvent être organisées en hiérarchie, à travers un héritage.

IndividuException

DepartementException NomException

Dans ce cas, la clause catch(IndividuException e) capture les exceptions qui sont du type
IndividuException ou d’une sous-classe de IndividuException.
La clause finally
Lorsqu’un traitement doit être exécuté impérativement, qu’une exception soit levée ou non,
on le place dans un bloc introduit par finally.

public static void main(String args[]) {


String nom; int dpt;
[...ouvrir fichier...]
// ouverture d’un fichier contenant des noms et des numéros de département
// obtention d’une valeur pour les variables nom et dpt

try {
Individu p1 = new Individu(nom, dpt);
}
catch (IndividuException e) {
... // traitement de l’exception
}
finally {
[...fermer fichier...]
// Dans tous les cas, fermeture du fichier qui avait été ouvert
}
...
} // main

MereException

Fille1Exception Fille2Exception Fille3Exception

try {...}
catch ( Fille1Exception e ) {...} // ne traite que les exceptions Fille1
catch (MereException e) {...} // traite les Fille2, Fille3 et les meres.

Remarque : Si on inverse les 2 catch, le deuxième est inaccessible.


certaines méthodes ne peuvent être implémentées que dans un bloc try catch :

// Note: This class won't compile by design!


// See ListOfNumbersDeclared.java or
ListOfNumbers.java
// for a version of this class that will compile.
import java.io.*;
import java.util.Vector;

public class ListOfNumbers {


private Vector victor;
private static final int size = 10;

public ListOfNumbers () {
victor = new Vector(size);
for (int i = 0; i < size; i++)
victor.addElement(new Integer(i));
}
public void writeList() {
PrintWriter out = new PrintWriter(new
FileWriter("OutFile.txt"));

for (int i = 0; i < size; i++)


out.println("Value at: " + i + " = " +
victor.elementAt(i));

out.close();
}
}

La solution :

public void writeList() {


PrintWriter out = null;

try {
System.out.println("Entering try
statement");
out = new PrintWriter(
new FileWriter("OutFile.txt"));

for (int i = 0; i < size; i++)


out.println("Value at: " + i + " = " +
victor.elementAt(i));
} catch (ArrayIndexOutOfBoundsException e) {
System.err.println("Caught
ArrayIndexOutOfBoundsException: " +
e.getMessage());
} catch (IOException e) {
System.err.println("Caught IOException: " +
e.getMessage());
} finally {
if (out != null) {
System.out.println("Closing
PrintWriter");
out.close();
} else {
System.out.println("PrintWriter not
open");
}
}
}

Exercices

La classe IntegerP du chapitre V3.3


La classe IntegerP ne faisait pas complètement la gestion des erreurs : en effet que se passe-t-
il dans le cas
IntegerP("toto"); // mauvais format
Integer.parseInt("-89"); // mauvais format
Integer.parseInt("56.3"); // mauvais format
Integer.parseInt("3",0); // mauvais radix
Integer.parseInt("2147483648"); // dépassement de capacité >MAX_VALUE

On désire changer la classe IntegerP (nouvelle version) qui prend en compte la gestion des
erreurs.
Ecrire cette nouvelle classe.
Un correction est donnée à la page suivante :
class NumberFormatException extends Exception {
public NumberFormatException () {
super();
}
public NumberFormatException (String s) {
super (s);
}
}

Class IntegerP{
public IntegerP(int value) {
this.value = value;
}
public IntegerP(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}

public static int parseInt(String s, int radix)throws NumberFormatException{


int monRadix=radix;
int j ,longChaine=s.length()-1;
long res=0;
if (radix>=2&&radix<=16){
for(int i=longChaine;i>=0;i--){
for (j=0;j<16;j++) if (tabVal[j]==s.charAt(i)) break;
if(j>=16)throw new NumberFormatException(s);
if(i==longChaine) radix=1; else radix*=monRadix;
res+=j*radix;
if(res>MAX_VALUE)
throw new NumberFormatException(" dépassement de capacité
: "+s);
}
}
else throw new NumberFormatException("radix "+radix+" en dehors des
limites");
return (int)res;
}
public static void main(java.lang.String[] args) {
int j;
String s="";
try{
s ="1o";
j=IntegerP.parseInt(s);
}
catch(NumberFormatException e){
System.out.println("pb:"+e);
}
try{
s ="2147483648";
j=IntegerP.parseInt(s);
System.out.println(""+j);
}
catch(NumberFormatException e){
System.out.println("pb:"+e);
}
try{
s ="56";
j=IntegerP.parseInt(s,0);
}
catch(NumberFormatException e){
System.out.println("pb:"+e);
}

System.out.println(""+IntegerP.parseInt(s)+" "+"
"+IntegerP.toHexString(IntegerP.parseInt(s))+" "+
Integer.toBinaryString(Integer.parseInt(s)));
System.out.println(IntegerP.toString(3290));

}
}

pb:java.lang.NumberFormatException: 1o
pb:java.lang.NumberFormatException: dépassement de capacité : 2147483648
pb:java.lang.NumberFormatException: radix 0en dehors des limites
56 00000038 111000
3290

La classe Point du chapitre II.24


On rappelle le cahier des charges : Les coordonnées d'un point sont des entiers compris entre
–999 et 999.
On désire créer une classe OutOfRangeException qui hérite de la classe Exception. Cette
exception est créée à chaque fois que les coordonnées du point sont en dehors de limites
MAX et MIN.

Travail à faire :
1/ Faire l'étude des méthodes qui doivent lancer le gestionnaire d'exception.
2/ Implémenter et tester la nouvelle classe Point.

La classe MaString du chapitre II.24


Pour faire cet exercice, il est nécessaire d'avoir fait l'exercice II.24 sue la classe MaString. Le
but de cet exercice est d'améliorer cette classe pour prendre en compte la gestion des
exceptions. On désire faire un certain nombre de changement de cette classe. On pourra donc
créer un nouvelle version sous VisualAge de la classe ou bien appeler cette classe MaString2
La classe MaString a donc les champs suivants :
Class MaString {
private char [] value ; // tableau de caractères
private int count ; // longueur de la chaine de caractères
private int offset ; // offset sur le caractère courant
//methods de la class MaString
}

a/ Ecrire les constructeurs de la classe MaString :


On définira tout d’abord le constructeur générique de la classe MaString
public MaString(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}

this.value = new char[count];


this.count = count;
System.arraycopy(value, offset, this.value, 0, count);
}

Expliquer le fonctionnement du constructeur générique. A partir de ce constructeur générique


écrire le code des constructeurs ci-dessous :

b/ Ecrire le code du constructeur MaString()


Ecrire le code du constructeur MaString(int count)
MaString(char [ ] value); // initialise la chaine avec un tableau de caractère
MaString(MaString s) ; // initialise la chaine avec la classe s
MaString(byte [ ] byte) // initialise la chaine avec un tableau de byte que l’on caste en tableau
de char

c/ On désire écrire la fonction copyValueOf qui permet de copier un tableau de caractères dans la
classe MaString :
public static MaString copyValueOf(char data[]) {
return copyValueOf(data, 0, data.length);
}

Ecrire la fonction MaString copyValueOf(char data[], int offset, int count) qui renvoie une
reference sur MaString. Cette méthode de tester les valeurs de offset et count et générer une
exception du type StringIndexOutOfBounsException

d/ expliquer ce que fait la méthode ci-dessous :


public char charAt(int index) {
if ((index < 0) || (index >= count)) {
System.out.println(“indexe en dehors des limites”);
}
return value[index + offset];
}

Reprendre cette méthode et la changer pour que celle-ci génère une exception du type
StringIndexOutOfBounsException.
Les autres méthodes ne changent pas. Expliquer
Exercices pratiques :
A–

NB : Créer un dossier qui porte votre nom sur le Bureau pour enregistrer tous les travaux de
cette épreuve

Soit un logiciel assurant la gestion des adhérents qui sont inscrits dans une
Médiathèque. Lorsqu'un adhérent est inscrit à la Médiathèque, on lui affecte automatiquement
un numéro et on fixe sa cotisation. La cotisation d’un adhérent peut changer en fonction de la
situation de l’adhérent. L’adhérent qui le souhaite peut ne plus appartenir à la Médiathèque, il
démissionne.

La classe Java ci-dessous permet d'instancier des objets Adhérent identifiés par un nom, un
numéro , (obtenu au fur et à mesure de la création des objets) et une cotisation et un état
(démissionné, ou actif).

public class Adherent


{
private static dernierNuméro = 1000;
private static final boolean DEMIS = false;
private static final boolean ACTIF = true;

private String m_nom;


private int m_numéro;
private double m_cotisation;
private boolean m_etat;

public Adherent ( nom, cot)


{
m_nom = String (nom);
m_cotisation = cot;
m_numero= dernierNuméro++;
m_etat = ACTIF;
}

public void demissionne ()


{
m_ etat = DEMIS;
}

}
Travail demandé :

1) Corriger les erreurs qui se sont glissées dans la classe Adhérent ci-dessus

2) Ajouter à la classe Adhérent les méthodes :

toString() : affichage des attributs de la classe Adhérent sous forme de chaîne


de caractères
modifie(double cotisation) : modification de la cotisation

3) Surcharger le constructeur pour offrir les méthodes de construction suivantes:


un constructeur par défaut qui permet de créer un objet Adhérent dont le nom
est "anonyme"
un constructeur qui permet de créer un objet Adhérent en imposant le numéro

4) Ecrire le code permettant de saisir un adhérent et prévoir les cas d'exception.

5) Créer un vecteur d'adhérent et développer les opérations d'insertion et de


suppression d'un adhérent. Ajouter une méthode de recherche et d'affichage d'un
adhérent.

6) Ajouter une méthode qui permet de trier le vecteur dans l'ordre croissant des noms
des adhérents.

7) Ajouter une méthode d'affichage de tous les éléments du vecteur.

8) On souhaite stocker la liste des adhérents dans un fichier séquentiel. Développer


l'application "Java" qui permet de faire la mise à jour de ce fichier : Ajout,
Suppression, Modification et Consultation d'enregistrements.

"Menu de Mise à jour"


1. Ajoute
r un
Adhér
ent
2. Modifi
er un
Adhér
ent
existan
t
3. Suppri
mer un
Adhér
ent
existan
t
4. Recher
cher
un
Adhér
ent
existan
t
5. Liste
des
Adhér
ents
6. Fin

Ecran de Recherche

Saisir le numéro d'Adhérent à rechercher :


Nom :
Cotisation :
Etat :

Liste de tous les Adhérents


Numéro Nom Cotisation Etat
B–

- L'application de gestion d’un club de sports permet de gérer les adhérents qui s’inscrivent
pour pratiquer diverses disciplines (course, saut, lancé du poids, natation etc.…). Le club
possède des entraîneurs bénévoles.

• Lors de son inscription, l’adhérent fournit les renseignements suivants :


Nom
Prénom
Adresse
Date de naissance
Code d’identification. (attribué automatiquement)
Discipline(s) choisie (s).

• Lors de son inscription, l’adhérent choisit une à trois disciplines proposées.

• Toutes les disciplines sont définies par un nom, une description sommaire et l’âge
à partir duquel la discipline peut être pratiquée.
Travail à réaliser :

1) Ecrire une classe Adhérent et une classe Discipline, avec les constructeurs complets,
les données membres et une méthode d’affichage Affi_adherent() pour l’adhérent et
Affi_discipline() pour la discipline.

2) Ajouter à la classe Adhérent la méthode Saisie_adherent() qui permet de saisir au


clavier les informations d’un adhérent

3) Développer dans la classe test le code permettant de saisir la liste des disciplines dans
un tableau Tdisp.

4) Dans la classe test, Ecrire une méthode Inscription() qui permet d’inscrire un
adhérent et le ranger dans un tableau.

5) Dans la classe test, Ecrire une méthode Afficheliste() qui permet d’afficher ce tableau.

6) Dans la classe test, Ecrire une méthode Trier() qui permet de trier ce tableau sur la
base du nom de l’adhérent.

Gestion club
Adhérent Diagramme de Classe
m_sNom:String
m_sPrenom:String
m_sAdresse :
String TEST
m_sage: Int
m_icode:Int Main()
Trier()
Saisie_adherent()
Inscription()
Affi_adherent()
Afficheliste()
Discipline

m_sNom:String
m_sdescrp:String
m_sageo:Int

Affi_discipline()
C–

Un établissement d’enseignement général souhaite informatiser la gestion des professeurs et des


élèves.

1) créer la classe Personne définit par les propriétés privées suivantes :*


- code : entier
- nom : text(20)
- prénom : text(20)
- sexe : caractère

Ajouter à cette classe un constructeur par défaut et un constructeur qui permet d’initialiser toutes les
propriétés. Créer toutes les méthodes set et get.

2) Un professeur est une personne avec les propriétés privées supplémentaires suivantes :
- Diplôme : text(20)
- Spécialité : text(20)

Développer la classe Professeur qui hérite de la classe personne. Ajouter les constructeurs adéquats.

3) Un élève est une personne avec les informations privées suivantes :


- Section : text(10)
- Nombre de jour d’absence : entier

Ecrire la classe Eleve qui hérite de la classe personne

4) Ecrire la méthode Affichage() qui permet d’afficher les informations d’une personne.
Surcharger cette méthode dans les classes Professeur et Eleve de façon à pouvoir afficher toutes
les informations respectives des deux classes.

5) Ajouter la méthode incAbsence() à la classe Eleve qui permet d’incrémenter le nombre de


jours d’absence

6) Créer la classe de test afin de tester les méthodes d’affichage des classes Professeur et
Eleve

7) Ecrire le code permettant de saisir la liste des élèves dans un tableau (au clavier).

8) Développer le code permettant de trier ce tableau dans l’ordre croissant des noms des
élèves en utilisant une méthode de tri de votre choix. Annoncer le nom de la méthode
sous forme de commentaire.

9) Afficher la liste de tous les élèves.

Vous aimerez peut-être aussi