134
OFPPT ROYAUME DU MAROC 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 Office de la Formation Professionnelle et de la Promotion du Travail DIRECTION RECHERCHE ET INGENIERIE DE FORMATION

TSDI - module 4 - Programmation orientée objet

Embed Size (px)

Citation preview

Page 1: TSDI - module 4 - Programmation orientée objet

OFPPT

ROYAUME DU MAROC

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

Office de la Formation Professionnelle et de la Promotion du Travail

DIRECTION RECHERCHE ET INGENIERIE DE FORMATION

Page 2: TSDI - module 4 - Programmation orientée objet

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 etl’amélioration de ce programme.

Said Slaoui DRIF

Page 3: TSDI - module 4 - Programmation orientée objet

OBJECTIF OPERATIONNELS DE PREMIER NIVEAU

DE COMPORTEMENT PRECISIONS SUR LE COMPORTEMENT ATTENDU A. Programmer des classes (métier ou service) dans un langage orienté objet (JAVA) B. Exploiter les classes de collections C. Programmer les exceptions D. Gérer les flux (entrée/sortie) E. Réaliser les tests unitaires F. Documenter le programme

CRITERES PARTICULIERS DE PERFORMANCE • Création et instanciation judicieuse

des classes • 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 • Utilisation adéquate des classes de

collections(listes,piles,tableaux,dictionnaire...)

• 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 d’objet contenu dans une collection

• Identification correcte des

événements d’exception associés 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 du langage utilisé.

• Constitution judicieuse d’un jeu

d’essai contenant tous les cas 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

Page 4: TSDI - module 4 - Programmation orientée objet

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

Page 5: TSDI - module 4 - Programmation orientée objet

RESUME THEORIQUE ET TRAVAUX PRATIQUES

Cours & Exemples pratiques

Page 6: TSDI - module 4 - Programmation orientée objet

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

La programmation classique : 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. 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

Algorithmes

Structures de données

Programme

Données (notées attributs ou champs)

Fonctions de traitements des données ou méthodes

Objet de la classe xxx

Page 7: TSDI - module 4 - Programmation orientée objet

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); }

Page 8: TSDI - module 4 - Programmation orientée objet

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.

deux instanciationsnom societe

presenteToi()

System.out.println( "Je m'appelle " + nom); System.out.println( "Je travaille à " + societe);

nomDupond

societeima

p1, un objet Personne

nomDurand

societeOutils Java

p2, un autre objet Personne

la classe Personne

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.

Page 9: TSDI - module 4 - Programmation orientée objet

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.

Page 10: TSDI - module 4 - Programmation orientée objet

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;

Page 11: TSDI - module 4 - Programmation orientée objet

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";

Page 12: TSDI - module 4 - Programmation orientée objet

- 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

virgule flottante : Caractères : Valeurs logiques :

byte (8 bits), short (16 bits), int (32 bits), long (64 bits).

float (32 bits), double (64 bits)

char (16 bits, Unicode) boolean, deux valeurs true et false.

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;// on définit une variable de type int

a = 245;// on lui affecte la valeur 245

?a

245a

L’emplacement mémoire de la variable contient la valeur associée à la variable.

Page 13: TSDI - module 4 - Programmation orientée objet

Une variable de type objet désigne sa valeur

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

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

?ch

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();

Page 14: TSDI - module 4 - Programmation orientée objet

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.

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; { // bloc B Personne p2 = new Personne("Meunier"); p2.presenteToi(); p1 = p2; } // fin bloc B ... // etc. } // fin bloc A

nom

societe

Meunier

Outils Java SA

P1 = P2

P1 P2

pile

tas

P1 54

P2

Meunier ?

Bloc A

Bloc B

Page 15: TSDI - module 4 - Programmation orientée objet

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.

Page 16: TSDI - module 4 - Programmation orientée objet

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(); }

Page 17: TSDI - module 4 - Programmation orientée objet

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;

Page 18: TSDI - module 4 - Programmation orientée objet

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);

Page 19: TSDI - module 4 - Programmation orientée objet

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.

Page 20: TSDI - module 4 - Programmation orientée objet

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

Page 21: TSDI - module 4 - Programmation orientée objet

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

Page 22: TSDI - module 4 - Programmation orientée objet

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

Page 23: TSDI - module 4 - Programmation orientée objet

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

a e i o u ? ?y

curseur de fin

espace alloué

?

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

capacity()

length()

Page 24: TSDI - module 4 - Programmation orientée objet

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. 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.

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

"coucouAu revoir" StringBuffer s; s("coucou"); s.append("Au revoir");

Page 25: TSDI - module 4 - Programmation orientée objet

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... : 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

"coucou" StringBuffer s1,s2; s1("coucou"); s2("coucou");

"coucou"

2 références différentes : donc s1==s2 //est faux s1.equals(s2)//est faux

"coucou" String s1,s2; s1("coucou"); s2("coucou");

"coucou"

contenu des 2 chaines équivalent donc : s1==s2 //est vrai s1.equals(s2)//est vrai

Page 26: TSDI - module 4 - Programmation orientée objet

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 ); } }

Page 27: TSDI - module 4 - Programmation orientée objet

♦ 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

Page 28: TSDI - module 4 - Programmation orientée objet

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=10int 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

de s, en base 10 int x=Integer.parseInt("10"); // résultat x=10

int parseInt(String s, int radix)

initialiser this.value à la valeur de s, en base donnée par radix

int x=Integer.parseInt("10",16); // résultat 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 une String

String s = IntegerP.toString(10);// s="10"

String toString() convertir this.value en une String

String s = IntegerP.toString(10);// s="10"

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.

Page 29: TSDI - module 4 - Programmation orientée objet

/* 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 :

Page 30: TSDI - module 4 - Programmation orientée objet

"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} }

Page 31: TSDI - module 4 - Programmation orientée objet

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.

Page 32: TSDI - module 4 - Programmation orientée objet

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é

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

La classe Employe reprend toutes les propriétés de Individu, ...

..., en les complétant

Page 33: TSDI - module 4 - Programmation orientée objet

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.

h é rita g e

C la sse m è re

C la sse 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.

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.

classe fille extends mere { // code }

Individu

Employe

Ouvrier

Patron

Cadre Exemple : notion d'héritage

Page 34: TSDI - module 4 - Programmation orientée objet

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).

Page 35: TSDI - module 4 - Programmation orientée objet

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 Essai1 { public static final char NL = '\n'; public static final int AS = 1; public static final int VALET = 11; public static final int DAME = 12; // declaration des methods }

Expliquer le sens des mots clés static ajoutés aux mots clés final (Cf Chapitre 2.16)

Class EssaiMere { public final void methfinal(){ System.out.println(“classe mere”); } }

Class EssaiFille extends EssaiMere{ Public final void methfinal(){ // erreur de compilation }

Page 36: TSDI - module 4 - Programmation orientée objet

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 { int x,y; Point(){ x=y=0; } Point(a,y){ x=a;// équivalent à this.x=a this.y=y ; // Pour lever l’ambiguïté il est obligatoire de mettre this.y=y } }

Class PointCol extends Point { String color ; PointCol(){ super();// appel à Point() color= »incolore » ; } PointCol(int x, int y, String s){ super(x,y) ;// appel à point(x,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

Page 37: TSDI - module 4 - Programmation orientée objet

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.

Page 38: TSDI - module 4 - Programmation orientée objet

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 { private String nom; // nom de l'individu private byte departement; // département private static final byte MAX_DEPT=95 ; private static final byte MIN_DEPT=1; protected void setDepartement(int unDept) { if(unDept>=MIN_DEPT&&unDept<=MAX_DEPT) departement=(byte) unDept ; else System.out.println(« Erreur saisie ») ; } public Individu (String leNom, int leDept) { nom = leNom.toUpperCase(); if (leDept != 0) setDepartement(leDept); } // On définit un constructeur par défaut public Individu () { nom = "nom inconnu"; departement = 0; } public void afficherSurEcran() { System.out.println("Je m'appelle " + nom); if (departement != 0) System.out.println("j'habite dans le " + departement); } } // class Individu

class Employe extends Individu { private String societe; protected void setSociete(String uneEntreprise){ societe = uneEntreprise.toUpperCase(); } public Employe (String leNom, String laSociete) { this(leNom,laSociete,0); } 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 []a){ Employe e=new Employe("toto","IUT"); e.setDepartement(06); e.afficherSurEcran(); } }

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) }

Page 39: TSDI - module 4 - Programmation orientée objet

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.

Page 40: TSDI - module 4 - Programmation orientée objet

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.

Page 41: TSDI - module 4 - Programmation orientée objet

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

Page 42: TSDI - module 4 - Programmation orientée objet

� 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

private String nom ; private String societe ; private String numSecu; private static final String pasDeSociete = "?";

Personne

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);

Page 43: TSDI - module 4 - Programmation orientée objet

Créer la classe NumSecu

� 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.

private String numSecu; public int age; public String dateNaissancepublic boolean Masculin

NumSecu

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

private String nom ; private String societe ; private NumSecu numSecu;

Personne

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);

Page 44: TSDI - module 4 - Programmation orientée objet

Hériter de la classe Personne

� 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,

public Patron (String nom) public Patron (String nom, String Entreprise) public Patron (String nom, String Entreprise, String NumSecu); public float getSalaire(); public String getInfo();

public Cadre (String nom) public Cadre (String nom, String Entreprise) public Cadre (String nom, String Entreprise, String NumSecu); public float getSalaire(); public String getInfo();

public Ouvrier (String nom) public Ouvrier (String nom, String Entreprise) public Ouvrier (String nom, String Entreprise, String NumSecu); public float getSalaire(); public String getInfo();

private int dateEntre ; private static final float SMIG= 6250,90;

Ouvrier private int Indice ;

Cadre

private int cA ; private byte pourcentage ;

Patron

private String nom ; private String societe ; private NumSecu numSecu;

Personne

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);

Page 45: TSDI - module 4 - Programmation orientée objet

� 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.

Page 46: TSDI - module 4 - Programmation orientée objet

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.

Page 47: TSDI - module 4 - Programmation orientée objet

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

héritage

Classe Mere

Classe Fille

mMere()mRedef()

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 { int a ; public Mere(int a){ this.a=a;} public void mMere(){} public void mRedef(){ System.out.println("Redef de fille"); } }

Class Fille extends Mere { int b; public Fille(int a,int b){ super(a) ; this.b=b ;} public void mFille(){} public void mRedef(){ System.out.println("Redef de mère"); } }

Page 48: TSDI - module 4 - Programmation orientée objet

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

� 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);

� 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

3 Mere (3);

2 10

Fill (2 10)

mRedef() mFille()

Table de fonction de Fille

mRedef() mMere()

Table de fonction de MereobjM

PileobjF

Pile

Tas d'allocation

3 Mere (3);

2 10

Fill (2 10)

mRedef() mFille()

Table de fonction de Fille

mRedef() mMere()

Table de fonction de MereobjM

PileobjF

Pile

à l'exécution

Page 49: TSDI - module 4 - Programmation orientée objet

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); objM.mRedef(); // fonction de Mere

Mere objM = new Fille(2,10); 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

Page 50: TSDI - module 4 - Programmation orientée objet

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.

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.

private String nom ; private String societe ; private String numSecu;

Personne

private int dateEntre ; private static final float SMIG= 6250,90;

Ouvrier private int Indice ;

Cadreprivate int cA ; private byte pourcentage ;

Patron

float getSalaire() ;

abstract

fonctions sans code ;

float getSalaire() { // code}

float getSalaire() { // code}

float getSalaire() { // code}

String getInfo() { //

Page 51: TSDI - module 4 - Programmation orientée objet

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

Page 52: TSDI - module 4 - Programmation orientée objet

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 ! 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). 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

Bovin Porcin Ovin

identifiant poids nom

identifiantpoids

identifiant poids date tonte

definirRation() definirRation() definirRation()

Bovin Porcin Ovin

Animalidentifiantpoids

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

definirRation() toString()

Page 53: TSDI - module 4 - Programmation orientée objet

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

Page 54: TSDI - module 4 - Programmation orientée objet

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 :

public Patron (String nom) public Patron (String nom, String Entreprise) public Patron (String nom, String Entreprise, String NumSecu); public float getSalaire(); public String getInfo();

public Cadre (String nom) public Cadre (String nom, String Entreprise) public Cadre (String nom, String Entreprise, String NumSecu); public float getSalaire(); public String getInfo();

public Ouvrier (String nom) public Ouvrier (String nom, String Entreprise) public Ouvrier (String nom, String Entreprise, String NumSecu); public float getSalaire(); public String getInfo();

private int dateEntre ; private static final float SMIG= 6250,90;

Ouvrier private int Indice ;

Cadre

private int cA ; private byte pourcentage ;

Patron

private String nom ; private String societe ; private NumSecu

Personne

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();

abstract l

Page 55: TSDI - module 4 - Programmation orientée objet

� 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

Page 56: TSDI - module 4 - Programmation orientée objet

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 après : monVect=monVect1.clone( );

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

monVect

Point a

Point b

x=1

y=2

monVect

Point a

Point b

x=3

y=4

monVect Point a

Point b

x=3

y=4

monVect

Point a

Point b

Page 57: TSDI - module 4 - Programmation orientée objet

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.

Page 58: TSDI - module 4 - Programmation orientée objet

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 :

Page 59: TSDI - module 4 - Programmation orientée objet

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

Page 60: TSDI - module 4 - Programmation orientée objet

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 :

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 P

Fichier 1

Fichier 2

ClasseCP ClasseB

ClasseC ClasseD

Compilation ClasseCP.class

ClasseB.classsClasseC.classsClasseD.class

Page 61: TSDI - module 4 - Programmation orientée objet

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.

Package PP

Fichier 1

ClasseA Import P.CP ;

Package P

CP.java

public class CP{ }

utilisation de la classe CP dans la classe

Page 62: TSDI - module 4 - Programmation orientée objet

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 instance de

C1

y d’une instance de

C1

w d’une instance de

C1

z d’une instance de

C1

z d’une instance de

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

Page 63: TSDI - module 4 - Programmation orientée objet

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 } }

Page 64: TSDI - module 4 - Programmation orientée objet

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

Vector

Enumeration

Dictionary

Hashtable

java.lang

Object

• 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.

Page 65: TSDI - module 4 - Programmation orientée objet

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....

Page 66: TSDI - module 4 - Programmation orientée objet

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

Page 67: TSDI - module 4 - Programmation orientée objet

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().

private String numSecu; public int age; public String dateNaissancepublic boolean Masculin

NumSecu

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

Page 68: TSDI - module 4 - Programmation orientée objet

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().

public Patron (String nom) public Patron (String nom, String Entreprise) public Patron (String nom, String Entreprise, String NumSecu); public float getSalaire(); public String getInfo();

public Cadre (String nom) public Cadre (String nom, String Entreprise) public Cadre (String nom, String Entreprise, String NumSecu); public float getSalaire(); public String getInfo();

public Ouvrier (String nom) public Ouvrier (String nom, String Entreprise) public Ouvrier (String nom, String Entreprise, String NumSecu); public float getSalaire(); public String getInfo();

private int dateEntre ; private static final float SMIG= 6250,90;

Ouvrier private int Indice ;

Cadre

private int cA ; private byte pourcentage ;

Patron

private String nom ; private String societe ; private NumSecu

S

Personne

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();

abstract class

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

NumSecu

public numSecu (String numSecu); public void setNumSecu(String numSecu); private String valider NumSecu( String numSecu); public String getNumSecu();

Page 69: TSDI - module 4 - Programmation orientée objet

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.

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

Entreprise

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();

Page 70: TSDI - module 4 - Programmation orientée objet

Les méthodes de la classe Entreprise Le constructeur :

� 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++)

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

null null null null nullPersonne

Page 71: TSDI - module 4 - Programmation orientée objet

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; }

Page 72: TSDI - module 4 - Programmation orientée objet

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.

Page 73: TSDI - module 4 - Programmation orientée objet

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 ") ; } }

Page 74: TSDI - module 4 - Programmation orientée objet

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"); }

Page 75: TSDI - module 4 - Programmation orientée objet

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; } } }

Page 76: TSDI - module 4 - Programmation orientée objet

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.

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.

Personne salarie; Noeud suivant;

Noeud

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

Noeud

suivansalarie1

Noeud

suivansalarie2 PremierNoeud

dernier Noeud

Page 77: TSDI - module 4 - Programmation orientée objet

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.

private NoeudListe premierNoeud;

private NoeudListe

Liste

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();

Page 78: TSDI - module 4 - Programmation orientée objet

Le constructeur : public Liste() Le constructeur permet d'initialiser les références premierNoeud et dernierNoeud à null Insertion en Tête : public void insererEnTete( Personne personneAIns ) Insertion en queue : public void insererEnQueue( Personne personneAIns )

dernier Noeud

Noeud

suivansalarie1 PremierNoeud

dernier Noeud

PremierNoeud

null null

insertion du premier élément noté salarie1

Noeud

suivansalarie1PremierNoeud

dernier Noeud

insertion du deuxième élément

é l i 2

Noeud

suivansalarie2

Noeud

suivansalarie1 PremierNoeud

dernier Noeud

insertion du premier élément noté salarie1

Noeud

suivansalarie2

PremierNoeud

dernier Noeud

insertion du deuxième élément

é l i 2

Noeud

suivansalarie1

Page 79: TSDI - module 4 - Programmation orientée objet

Effacement en queue : public Personne retirerDeQueue() Effacement en tête : public Personne retirerDeTete()

Noeud

suivansalarie1PremierNoeud

dernier Noeud

Liste avant l'appel à la méthode

i D Q

Noeud

suivansalarie2

Noeud

suivansalarie2 PremierNoeud

dernier Noeud

Liste aprés l'appel à la méthode retirerDeQueue; Le dernier élément est

Noeud

suivansalarie1

PremierNoeud

dernier Noeud

Liste avant l'appel à la méthode retirerDeTete

Noeud

suivansalarie2

Noeud

suivansalarie1 PremierNoeud

dernier Noeud

Liste aprés l'appel à la méthode retirerDeTete;Le premier élément est effacer

Page 80: TSDI - module 4 - Programmation orientée objet

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() ){

Page 81: TSDI - module 4 - Programmation orientée objet

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;

Page 82: TSDI - module 4 - Programmation orientée objet

} }

Page 83: TSDI - module 4 - Programmation orientée objet

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(); } }

Page 84: TSDI - module 4 - Programmation orientée objet

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.

Page 85: TSDI - module 4 - Programmation orientée objet

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.

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

Noeud

suivant

salarie1

Noeud

suivant

salarie2 PremierNoeud

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();}

Page 86: TSDI - module 4 - Programmation orientée objet
Page 87: TSDI - module 4 - Programmation orientée objet

Le constructeur : public Liste() Le constructeur permet d'initialiser les références premierNoeud à null push : public void push( Personne personneAIns ) pop : public Personne pop()

Noeud

suivansalarie1 PremierNoeud

PremierNoeud

null

insertion du premier élément noté salarie1

Noeud

suivansalarie1PremierNoeud

insertion du deuxième élément

é l i 2

Noeud

suivansalarie2

Noeud

suivansalarie1

PremierNoeud

Liste avant l'appel à la méthode pop

Noeud

suivansalarie2

Noeud

suivansalarie1 PremierNoeud

Liste aprés l'appel à la méthode pop;

Page 88: TSDI - module 4 - Programmation orientée objet

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.

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 peut être implémentée à partir de la classe mère Liste.

Queue extends 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();}

salarie1

salarie1 salarie2

salarie1 salarie2 salarie3

salarie2 salarie3

salarie3

insertion salarie1: insererQueue(salarie1)

insertion salarie2: insererQueue(salarie2)

insertion salarie3: insererQueue(salarie3)

effacer: effacerQueue()

effacer: effacerQueue()

Page 89: TSDI - module 4 - Programmation orientée objet

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.

Page 90: TSDI - module 4 - Programmation orientée objet

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é infructueuseelementAt(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).

private Vector vectE; Entreprise

public Entreprise(); public void ajouter (Personne p); public void retirer (Personne p); public void afficher(); public boolean isExiste(Persone p); public boolean isVide(); public int chercher (Personne []p, String salarie); public void toutEffacer(); public int getNbPersonne();

Permet de chercher un groupe de salarié de même type : "employé, cadre ou patron" Cette méthode renvoie dans Personne[] p les salariés trouvés

Page 91: TSDI - module 4 - Programmation orientée objet

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() ;

Page 92: TSDI - module 4 - Programmation orientée objet

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

Page 93: TSDI - module 4 - Programmation orientée objet

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. 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())

Valeur Clé

Page 94: TSDI - module 4 - Programmation orientée objet

System.out.println(enum.nextElement()) ;

Page 95: TSDI - module 4 - Programmation orientée objet

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 ++;

Page 96: TSDI - module 4 - Programmation orientée objet

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.

Page 97: TSDI - module 4 - Programmation orientée objet

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); }

Page 98: TSDI - module 4 - Programmation orientée objet

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.

Page 99: TSDI - module 4 - Programmation orientée objet

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(); }

Page 100: TSDI - module 4 - Programmation orientée objet

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

Page 101: TSDI - module 4 - Programmation orientée objet

// 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(); } }

Page 102: TSDI - module 4 - Programmation orientée objet

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();

Page 103: TSDI - module 4 - Programmation orientée objet

} }

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

Page 104: TSDI - module 4 - Programmation orientée objet

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é

Page 105: TSDI - module 4 - Programmation orientée objet

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.

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.

private Hashtable ent;

Entreprise

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);

Page 106: TSDI - module 4 - Programmation orientée objet

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.

Page 107: TSDI - module 4 - Programmation orientée objet

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 ».

Page 108: TSDI - module 4 - Programmation orientée objet

Une date peut être affichée selon un autre format moyennant un objet de type DateFormat ou SimpleDateFormat.

Page 109: TSDI - module 4 - Programmation orientée objet

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

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");

NumberClasse

Short Byte IntegerCharactre DoubleFloa

Object

Object

BigIntege BigDecimal

Page 110: TSDI - module 4 - Programmation orientée objet

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

Page 111: TSDI - module 4 - Programmation orientée objet

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.*;

Page 112: TSDI - module 4 - Programmation orientée objet

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 :

Page 113: TSDI - module 4 - Programmation orientée objet

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().

Page 114: TSDI - module 4 - Programmation orientée objet

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.

Page 115: TSDI - module 4 - Programmation orientée objet

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.

Page 116: TSDI - module 4 - Programmation orientée objet

� 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

Page 117: TSDI - module 4 - Programmation orientée objet

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);

Page 118: TSDI - module 4 - Programmation orientée objet

} // 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.

Page 119: TSDI - module 4 - Programmation orientée objet

• 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 { call method2; } method2 { call method3; } 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; }

methode1

methode2

methode3

(la flèche signifie : appel de )

Page 120: TSDI - module 4 - Programmation orientée objet

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.

Page 121: TSDI - module 4 - Programmation orientée objet

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

Page 122: TSDI - module 4 - Programmation orientée objet

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.

Page 123: TSDI - module 4 - Programmation orientée objet

IndividuException

NomException DepartementException

Une hiérarchie d’exceptions Les exceptions peuvent être organisées en hiérarchie, à travers un héritage. 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.

Page 124: TSDI - module 4 - Programmation orientée objet

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"); }

Page 125: TSDI - module 4 - Programmation orientée objet

} } 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="";

Page 126: TSDI - module 4 - Programmation orientée objet

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

Page 127: TSDI - module 4 - Programmation orientée objet

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.

Page 128: TSDI - module 4 - Programmation orientée objet

Les autres méthodes ne changent pas. Expliquer

Page 129: TSDI - module 4 - Programmation orientée objet

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; } }

Page 130: TSDI - module 4 - Programmation orientée objet

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érent

2. Modifier un Adhérent existant

3. Supprimer un Adhérent existan

Page 131: TSDI - module 4 - Programmation orientée objet

t 4. Recher

cher un Adhérent existant

5. Liste des Adhérents

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

Page 132: TSDI - module 4 - Programmation orientée objet

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.

Page 133: TSDI - module 4 - Programmation orientée objet

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 clubDiagramme de Classe

TEST

Main()Trier()

Inscription()Afficheliste()

Adhérent

m_sNom:Stringm_sPrenom:Stringm_sAdresse : String m_sage: Int

m_icode:IntSaisie_adherent()

Affi_adherent()

Discipline

m_sNom:Stringm_sdescrp:String

m_sageo:Int

Affi_discipline()

Page 134: TSDI - module 4 - Programmation orientée objet

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.