Programmation Objet en Java et UML
Programmation Objet en Java et UML
Xavier Crégut
<cregut@[Link]>
Septembre 2005
& %
X. Crégut Programmation objet en Java 1
' $
Objectifs du cours
& %
X. Crégut Programmation objet en Java 2
' $
Références
[1] Cay S. Horstmann and Gary Cornell. Au cœur de Java 2, volume 1.
Campus Press, 2001.
[2] Irène Charon. Le langage Java, concepts et pratique. Hermes, 2000.
[3] David Flanagan. Java in a Nutshell. O’Reilly, 2001.
[4] Bruce Eckel. Thinking in Java. Prentice-Hall, 2000. version PDF sur
[Link]
Plan du cours
–
Exemple introductif : approche objet vs approche traditionnelle (C)
–
La plateforme Java : caractéristiques et outils
–
Algorithmique en Java : types, opérateurs, structures de contrôle
–
Paquetages : structuration d’une application
–
Encapsulation : Classes et objet
–
Tableaux, String et autres classes de l’API Java
–
Spécification de comportement : les interfaces
–
Paramétrisation : la généricité
–
Héritage et concepts associés : polymorphisme, liaison tardive, classes
abstraites
– Responsabilité d’une classes : Exceptions et Programmation par contrat
– Quelques API Java
& %
– Interfaces graphiques (autre support)
& %
X. Crégut Programmation objet en Java 5
' $
& %
21
O4
O1
O5
O3
« Les systèmes logiciels sont caractérisés au premier chef par les objets
qu’ils manipulent, non par la fonction qu’ils assurent.
Ne demandez pas CE QUE FAIT LE SYSTÈME.
Demandez À QUI IL LE FAIT ! »
Bertrand Meyer
& %
X. Crégut Programmation objet en Java 7
' $
Les équations : version (plus) objet
1 /** Modélisation d’une équation du second degré et de sa résolution.
2 * @author Xavier Crégut
3 * @version 1.3
4 */
5 class Équation {
6
7 /** Coefficients de l’équation */
8 double coeffA, coeffB, coeffC;
9
10 /** Solutions de l’équation */
11 double x1, x2;
12
13 /** Déterminer les racines de l’équation du second degré. */
14 void résoudre() {
15 double delta = // variable locale à la méthode résoudre
16 [Link] * [Link] - 4 * [Link] * [Link];
17 this.x1 = (- [Link] + [Link](delta)) / 2 / [Link];
18 this.x2 = (- [Link] - [Link](delta)) / 2 / [Link];
19 }
20 }
& %
Attention : Cette classe Équation est simpliste !
& %
Exercice 3 Dessiner l’évolution de la mémoire.
& %
appliquer une méthode.
& %
24 #endif
& %
X. Crégut Programmation objet en Java 14
' $
Le programme principal : test_equation.c
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #include "equation.h"
5
6 int main()
7 {
8 Equation uneEquation; /* notre équation */
9
10 /* Initialiser les coefficients */
11 [Link] = 1;
12 [Link] = 5;
13 [Link] = 6;
14
15 /* Calculer les racines de l’equation */
16 resoudre(&uneEquation);
17
18 /* Afficher les résultats */
19 printf(" Racine 1 : %f\n", uneEquation.x1);
20 printf(" Racine 2 : %f\n", uneEquation.x2);
21
22 return EXIT_SUCCESS;
& %
23 }
De nouvelles constatations
– On peut avoir une approche objet même avec un langage non objet !
Attention : On n’aura pas tous les bénéfices d’une approche objet... sauf
à faire des choses très (trop !) compliquées.
– Dans la version C, il y a séparation entre la spécification (interface) et
l’implantation (corps) du module alors qu’en Java tout est dans une
même construction syntaxique (la classe), dans un seul fichier.
– La fonction resoudre est à l’extérieur de l’enregistrement.
– Le paramètre eq de résoudre a disparu en Java. Il est devenu implicite.
Pour y faire référence, on utilise le mot-clé this.
– Dans la version C, on utilise #include <math.h>. En Java on aurait pu faire
import [Link]; mais ceci est implicite.
& %
X. Crégut Programmation objet en Java 18
' $
& %
20 }
& %
X. Crégut Programmation objet en Java 20
' $
Le compilateur : javac
Le compilateur javac produit du code intermédiaire (byte code).
javac [Link] (==> produit [Link])
& %
[total 1148ms]
Compilation de RésolutionÉ[Link]
licorne> javac -verbose RésolutionÉ[Link]
[parsing started RésolutionÉ[Link]]
[parsing completed 305ms]
[loading /opt/j2sdk1.4.0/jre/lib/[Link](java/lang/[Link])]
[loading /opt/j2sdk1.4.0/jre/lib/[Link](java/lang/[Link])]
[checking RésolutionÉquation]
[loading ./É[Link]]
[parsing started ./É[Link]]
[parsing completed 5ms]
[loading /opt/j2sdk1.4.0/jre/lib/[Link](java/lang/[Link])]
[loading /opt/j2sdk1.4.0/jre/lib/[Link](java/io/[Link])]
[loading /opt/j2sdk1.4.0/jre/lib/[Link](java/io/[Link])]
[loading /opt/j2sdk1.4.0/jre/lib/[Link](java/io/[Link])]
[loading /opt/j2sdk1.4.0/jre/lib/[Link](java/lang/[Link])]
[wrote RésolutionÉ[Link]]
[checking Équation]
[loading /opt/j2sdk1.4.0/jre/lib/[Link](java/lang/[Link])]
[wrote ./É[Link]]
[total 1148ms]
& %
X. Crégut Programmation objet en Java 22
' $
La machine virtuelle Java (JVM) : java
Le code produit par javac étant du code intermédiaire, il ne peut pas être
directement exécuté par la machine.
=⇒ Il faut donc utiliser une machine virtuelle Java : java dans le JDK :
licorne> java Bonjour Xavier
Bonjour Xavier
Bonjour tout le monde !
On donne en paramètre une classe Java (donc sans extension !) qui doit
contenir la méthode principale main, suivi des éventuels arguments de la
ligne de commande.
Attention : Ne pas mettre d’extension derrière le nom de la classe.
licorne> java [Link]
Exception in thread "main"[Link]: Bonjour/class
& %
Le « . » est le séparateur de paquetages (équivalent des répertoires).
& %
X. Crégut Programmation objet en Java 24
' $
Documentation engendrée
& %
X. Crégut Programmation objet en Java 25
' $
Les commentaires
Java propose trois types de commentaires :
– Les commentaires à la C : utiles pour mettre en commentaire plusieurs
lignes de code.
/* Un commentaire qui peut se continuer
sur plusieurs lignes */
/* Exactement comme en C
* et donc ils ne peuvent pas être imbriqués
*/
Attention : Ces commentaires ne peuvent pas être imbriqués.
– Les commentaires à la C++ :
// Ce commentaire se termine avec la fin de la ligne
Les classes sont cherchée dans [Link] puis dans le répertoire courant.
Attention : Ne pas oublier le répertoire courant !
En utilisation CLASSPATH avec (t)csh :
setenv CLASSPATH /usr/local/java/junit/[Link]:.
javac [Link]
& %
java TestMaClasse
& %
X. Crégut Programmation objet en Java 30
' $
& %
X. Crégut Programmation objet en Java 31
' $
& %
X. Crégut Programmation objet en Java 32
' $
& %
X. Crégut Programmation objet en Java 34
' $
& %
X. Crégut Programmation objet en Java 35
' $
& %
X. Crégut Programmation objet en Java 36
' $
Origine et historique
– Projet SunSoft : Langage pour applications embarquées
– 1991 : Projet Green, Produit *7, Langage Oak (futur Java) =⇒ Échec !
– Évolution vers INTERNET : Applications mobiles
– 1994-1995 : navigateur WWW novateur, futur HotJava
– 1995 : intégration de Java dans Netscape
– Les principales versions de Java :
– 1996 : première version de Java publiée (Java 1.02)
– 1997 : Java 1.1 (modèle des événements, internationalisation, Java
Beans...)
– 1998 : Java 1.2 (Swing optimisation de la JVM, ...).
Sun appelle Java 2 les versions de Java.
– 2001 : Java 1.4 (assertions, amélioration JDBC, regexp...)
– 2004 : Java 1.5 (généricité, énumération, foreach) : J2SE 5.0
& %
X. Crégut Programmation objet en Java 37
' $
Algorithmique en Java
& %
X. Crégut Programmation objet en Java 38
' $
La méthode principale
Par définition, lorsqu’on lance la JVM avec une classe, c’est sa « méthode
principale » qui est exécutée. Elle se déclare de la manière suivante :
public static void main (String[] args) {
...
}
& %
X. Crégut Programmation objet en Java 39
' $
& %
X. Crégut Programmation objet en Java 40
' $
Les types primitifs : tableau récapitulatif
Type # bits Défaut Minimum Maximum Exemples
byte 8 0 -128 +127 -10, 0, 10
short 16 0 -32768 32767 -10, 0, 10
int 32 0 −231 231 − 1 -10, 0, 10
long 64 0 −263 263 − 1 -10L, 0L, 10L
float 32 0.0 IEEE 754 IEEE 754 3.1F, 3.1f, 31e-1f
double 64 0.0 IEEE 754 IEEE 754 3.1, 3D, 1e-4
boolean 1 false false true
char 16 ’\u0000’ ’\u0000’ ’\uFFFF’ ’a’, ’\n’, ’\’’
Remarque : Java autorise les conversions entre types à condition qu’il n’y
ait pas de perte d’information (ou précision).
float x1 = 4; // OK : coersion
& %
float x2 = 4.21; // Erreur de compilation !
Identificateurs
– Les identificateurs en Java sont de la forme : lettre (lettre | chiffre) *
Les lettres sont a-z, A-Z, _, ainsi que tout caractère unicode qui
correspond à une lettre dans la langue utilisée (caractères accentués).
– Java comme C et C++ distingue majuscules et minuscules
– Conseil : Toujours choisir un identificateur significatif.
– Mettre en évidence les différents mots d’un identificateur. La convention
Java est de mettre l’initiale des mots suivants en majuscule (exemple :
idEnPlusieursParties).
– Remarque : Le caractères $ fait partie des « chiffres » mais est réservé
pour des identificateurs engendrés automatiquement. Ne pas l’utiliser !
& %
X. Crégut Programmation objet en Java 42
' $
Opérateurs relationnels
== // égalité (conversion si types différents) 3 == 3
!= // différence (non égalité) 4 != 5
> // plus grand que 4 > 3
>= // plus grand que ou égal 4 >= 3
< // plus petit que 3 < 4
<= // plus petit que ou égal 3 <= 4
Opérateurs arithmétiques
+ - // addition et soustraction (op. binaires) 10 + 5 == 15
* / // multiplication et division 10 / 3 == 3
% // modulo : le reste de la division entière 10 % 3 == 1
+ // opérateur unaire +10 == 10
- // opérateur unaire (opposé de l’opérande) -10
++ // pré-incrémentation (++x) ou post-incrémentation (x++)
-- // pré-décrémentation (--x) ou post-décrémentation (x--)
& %
X. Crégut Programmation objet en Java 43
' $
& %
X. Crégut Programmation objet en Java 44
' $
& %
X. Crégut Programmation objet en Java 45
' $
& %
X. Crégut Programmation objet en Java 46
' $
& %
X. Crégut Programmation objet en Java 47
' $
Pré- et post-opérateurs : ++ et --
Incrémenter (++) ou décrémenter (– –) une variable de 1.
& %
X. Crégut Programmation objet en Java 48
' $
& %
X. Crégut Programmation objet en Java 49
' $
Instructions simples : déclaration de variables
La déclaration de variables est une instruction.
=⇒ Les déclarations n’ont plus à être groupées au début d’un bloc.
Déclaration d’une variable :
<type> <nom> [= <valeur_initiale>]; // rôle de la variable
Exemples :
int age, numéro, montant; // éviter les déclarations multiples !
int px = 0, py = 0; // avec initialisation
double x = 55, y = x*x; // utilisation d’une expression calculée
& %
final int MAX = 10;
Attention : En Java, toutes les conditions sont (et doivent être) booléennes !
if (n1 == n2) {
if (n > max) { res = "égaux";
max = n; } else {
} res = "différents";
}
& %
Remarque : Pour représenter un SinonSi, utiliser else if.
& %
Conseil : Mettre un break après chaque groupe d’instructions d’un case.
Répétitions : while
// nb d’années pour atteindre l’objectif
double taux = 0.03;
double capital = 5000;
while (<condition>) { double objectif = 10000;
<instructions>; int nbAnnées = 0;
} while (capital < objectif) {
nbAnnées++;
capital = capital * (1 + taux);
}
& %
X. Crégut Programmation objet en Java 53
' $
& %
X. Crégut Programmation objet en Java 54
' $
Répétitions : for
for (<init>; <cond>; <incr>) { for (int i = 1; i < 10; i++) {
<instructions>; [Link](i);
} }
Sémantique : <init> (initialisation) est exécutée puis, tant que <cond> est
vraie <instructions> et <incr> (incrémentation) sont exécutées.
{ // réécriture du for à l’aide du while
<init>; // initialisation
while (<cond>) { // condition de continuation
<instructions>; // traitement
<incr>; // incrémentation
}
}
Conséquence : Une variable déclarée dans <init> n’est visible que dans le
bloc du for (cas du int i = 1, par exemple).
Conseil : Conserver la sémantique du Pour algorithmique : on sait à
& %
l’avance combien de fois la boucle doit être exécutée.
Répétitions : foreach
public static void main(String[] args) {
for (<type> <var> : <col>) {
for (String nom : args) {
<instructions>;
[Link]("Bonjour " + nom);
}
}
Vocabulaire : On dit « Pour chaque <var> dans <col> » (foreach ... in ...).
Sémantique : <col> est soit un tableau, soit une collection (en fait un
itérable, T. 295).
Les instructions sont exécutées pour <var> prenant chaque valeur de <col>.
Avantage : Écriture simple conservant la sémantique du Pour algorithmique.
Limites : <instructions> ne doit pas modifier le parcours de la collection
(détecté à l’exécution ConcurrentModificationException ). Règle du 80–20 !
& %
X. Crégut Programmation objet en Java 56
' $
Instructions à ne pas utiliser
Les instructions suivantes sont proposées par Java :
– break : arrêter l’exécution d’une boucle ou d’un switch ;
– continue : arrêter l’itération actuelle d’une boucle et passer à la suivante.
Attention : Ces deux instructions ne doivent pas être utilisées car elles
violent les principes de la programmation structurée.
Exception : Bien sûr, le break peut et doit être utilisé dans un switch !
Remarque : En Java, on peut étiqueter les répétitions.
première: for (int i = 0; i < 7; i++) { // for étiquetté « première »
[Link]("i = " + i);
for (int j = 0; j < 4; j++) {
[Link](" j = " + j);
if (j == i) {
continue première; // ==> passer au i suivant !
}
}
}
& %
[Link]("Fin !");
Paquetages
Attention aux conflits si un même nom de classe est utilisé dans deux
paquetages ! Le conflit doit être résolu en utilisant le nom qualifié.
– [Link].* est importé par défaut : il contient les classes de base, les
classes System, Math, String, etc.
& %
X. Crégut Programmation objet en Java 59
' $
Paquetage : intérêts
Intérêt : L’intérêt des paquetages est de :
– structurer l’application en regroupant ses constituants ;
– éviter les conflits de noms : un même nom de classe peut être utilisé dans
deux paquetages différents (un paquetage définit un espace de nommage).
Conseil : Pour éviter toute ambiguïté, il est recommandé de toujours
utiliser la forme complètement qualifiée des classes !
Exemple : [Link] plutôt que Color.
Remarque : Nous parlons maintenant des paquetages car ils sont
nécessaires pour comprendre certains éléments de Java (droit d’accès) ou
certains messages d’erreurs avec les outils du JDK.
Attention : Les paquetages sont très importants pour la structuration d’un
programme, ou d’une bibliothèque. Cependant, nous n’insisterons pas sur
& %
cet aspect dans la suite de ce cours.
– Les objets
– Les poignées
– Les classes
– Les attributs
– Les méthodes
– Les constructeurs
– Les attributs et méthodes de classe
& %
X. Crégut Programmation objet en Java 61
' $
Les objets
Un objet est caractérisé par :
– un état : la valeur des attributs (coeffA, coeffB, etc.) ;
– un comportement : les méthodes qui peuvent lui être appliquées
(résoudre) ;
– une identité qui identifie de manière unique un objet (par exemple son
adresse en mémoire).
Remarque : Un objet n’a de réalité qu’à l’exécution du programme.
Exercice 5 Quel est l’état d’une fraction ? Quel est son comportement ?
& %
X. Crégut Programmation objet en Java 62
' $
Les poignées
Les objets sont alloués dynamiquement (dans le tas) : opérateur new.
new Équation(); // création d’un objet Équation
Il retourne l’identité de l’objet créé. Elle est conservée dans une poignée.
Poignée : Une poignée est une variable dont le type est le nom d’une classe.
Équation eq; // déclarer une poignée eq de type Équation
eq = new Équation(); // créer un objet et l’attacher à la poignée
La valeur par défaut d’une poignée est null. Elle indique qu’aucun objet
n’est attaché à la poignée.
Remarque : On peut regrouper déclaration de la poignée et initialisation :
Équation eq = new Équation(); // en une ligne pour ne pas oublier
// d’initialiser la poignée
& %
l’objet sont faites en même temps.
Et après ?
Nous avons vu ce qu’est un objet, comment le créer et l’attacher à une
poignée.
Mais :
– Où sont définies les caractéristiques des objets ? Dans une classe !
– Comment peut-on accéder aux caractéristiques des objets ? Par l’envoi de
messages (ou appel de méthodes) !
– Comment initialiser un objet (nous ne savons que le créer) ? C’est
l’objectif des constructeurs !
& %
X. Crégut Programmation objet en Java 65
' $
Les classes
Une classe définit :
– un MODULE : elle regroupe la déclaration des attributs et la définition
des méthodes associées dans une même construction syntaxique ;
class NomDeLaClasse {
// Définition de ses caractéristiques
}
& %
les méthodes et attributs définis sur la classe comme MODULE.
& %
X. Crégut Programmation objet en Java 67
' $
Droit d’accès (accessibilité) des caractéristiques
Chaque caractéristique a un droit d’accès. Il existe quatre niveaux de droit
d’accès en Java :
– public : accessible depuis toutes les classes ;
– private : accessible seulement de la classe et d’aucune autre ;
– absence de modifieur de droit d’accès : droit d’accès de paquetage,
accessible depuis toutes les classes du même paquetage ;
– protected : accessible du paquetage et des classes dérivées (cf héritage).
Accessible depuis Droit d’accès/Visibilité
une méthode définie dans public protected défaut private
La même classe oui oui oui oui
Une classe du même paquetage oui oui oui non
Une sous-classe d’un autre paquetage oui oui non non
Une autre classe d’un autre paquetage oui non non non
& %
Intérêt : Les droits d’accès permettent le masquage d’information.
+attributPublic: int
−attributPrivé: double
attributs
#attributProtégé
Équation
RobotType1
+coeffA: double
+x: int +coeffB: double
+y: int +coeffC: double
+direction: int −x1: double
−x2: double
+avancer(nb: int)
+pivoter() +résoudre()
& %
Attention : Si la poignée est null une erreur est signalée à l’exécution.
& %
X. Crégut Programmation objet en Java 72
' $
Méthodes
Définition : Une méthode est une unité de calcul (fonction au sens de C)
qui exploite l’état d’un objet (en accès et/ou en modification).
– Une méthode est identifiée par sa classe, son nom, le nombre et le type de
ses paramètres (surcharge T. 78).
– Elle possède également un type de retour qui est void si elle ne retourne
rien (procédure).
– Elle a un code entre accolades.
Syntaxe
/** Documentation javadoc de la méthode décrivant l’objectif de la méthode.
* Les paramètres et le retour de la méthode sont également documentés :
* @param nomParamètre description de nomParamètre
* @return description de l’information retournée (si non void)
*/
<modifieurs> TypeRetour idMéthode( [Type1 p1[, Type p]*]) {
...
& %
}
Méthodes : exemple
Définir les méthodes set et delta sur la classe Equation.
/** Initialiser une Équation à partir de la valeur de ses coefficients.
* @param a coefficient de x<sup>2</sup>
* @param b coefficient de x
* @param c coefficient
*/
public void set(double a, double b, double c) {
[Link] = a;
[Link] = b;
[Link] = c;
}
Remarque : delta() est private car elle n’a pas à être uilisée de l’extérieur.
& %
X. Crégut Programmation objet en Java 74
' $
Méthodes : utilisation
Une méthode est toujours appliquée sur une poignée (comme un attribut) et
est exécutée sur l’objet associé à cette poignée.
poignée.méthode(p1, ..., pn); // Forme générale (envoi de message)
Remarque : Appliquer une méthode sur une poignée null provoque une
exception (NullPointerException).
Liaison statique : Le compilateur accepte l’appel p.m(a1 , ...,an ) ssi il
existe, dans la classe définissant le type de la poignée p, une méthode m
& %
d’arité n telle que les types de a1 , ..., an sont compatibles avec sa signature.
Exercice 10 Écrire une méthode supérieur (>) si inférieur (<) est définie.
& %
X. Crégut Programmation objet en Java 76
' $
Exercice 11 : Compteur
Un compteur a une valeur (entière) qui peut être incrémentée d’une unité.
Elle peut également être remise à 0. On suppose également qu’il est
possible d’initialiser le compteur à partir d’une valeur entière positive.
11.4 Comment être sûr que la valeur du compteur est toujours positive ?
& %
X. Crégut Programmation objet en Java 77
' $
Surcharge
Définition : En Java, indiquer le nom d’une méthode n’est pas suffisant
pour l’identifier. Il faut préciser :
– la classe à laquelle elle appartient ;
– son nom ;
– son nombre de paramètres ;
– le type de chacun de ses paramètres.
Exemple : Les méthodes suivantes sont toutes différentes :
class A {
void afficher() // afficher sans paramètre
void afficher(int i) // afficher un entier
void afficher(long i) // afficher un entier long
void afficher(String str) // afficher une chaîne
void afficher(String s, int largeur); // sur une certaine largeur
void afficher(String s, int largeur, char mode); // mode == ’c’, ’g’, ’d’
void afficher(int nbFois, String str); // contre-exemple !
}
& %
X. Crégut Programmation objet en Java 78
' $
Surcharge : résolution
Le même nom peut être utilisé pour nommer des méthodes différentes.
=⇒ Pour résoudre un appel de méthode, le compilateur s’appuie également
sur le nombre et le type des paramètres effectifs :
// On suppose que l’on est dans le corps d’une méthode de la classe A
afficher(10); // afficher(int)
afficher(10L); // afficher(long)
afficher("Bonjour", 20, ’c’); // afficher(String, int, char)
afficher("Bonjour"); // afficher(String)
afficher(); // afficher()
afficher(true); // Erreur à la compilation
afficher(20, "Bonjour"); // afficher(int, String)
& %
Exercice 13 Quelles méthodes de Fraction pourraient être surchargées ?
Exemples de surcharge
& %
X. Crégut Programmation objet en Java 80
' $
Surcharge et espace de nommage
Il est possible de définir la même méthode (même nom, même nombre de
paramètres, et mêmes types de paramètres) dans deux classes différentes.
class A { class B {
/** afficher en commençant /** afficher nb fois */
* par un décalage */ void afficher(int nb);
void afficher(int décalage); }
}
Pour savoir quelle méthode choisir, le compilateur s’appuie sur le type du
récepteur (le type de la poignée sur laquelle la méthode est appliquée).
=⇒ La classe définit un espace de nommage.
A x1; // poignée x1 de type A
B x2; // poignée x2 de type B
... // les initalisations de x1 et X2
[Link](5); // afficher(int) de la classe A
[Link](10); // afficher(int) de la classe B
& %
des espaces de nommage différents.
& %
X. Crégut Programmation objet en Java 83
' $
& %
}
& %
}
Constructeurs
Rappel : La création d’un objet nécessite en fait deux étapes :
1. La réservation de la zone mémoire nécessaire. Ceci est entièrement
réalisé par le compilateur (à partir des attributs de la classe).
2. L’initialisation de la zone mémoire. Le compilateur ne peut, a priori,
faire qu’une initialisation par défaut des attributs.
Il y a alors risque d’avoir une initialisation incorrecte, ou non conforme aux
souhaits de l’utilisateur (exemple : Fraction, Date, etc.).
Les constructeurs permettent alors :
– au programmeur d’indiquer comment un objet peut être initialisé ;
– au compilateur de vérifier que tout objet créé est correctement initialisé.
Exemple : Une équation peut (doit !) être initialisée à partir de la donnée
des valeurs de ses coefficients.
& %
X. Crégut Programmation objet en Java 86
' $
& %
X. Crégut Programmation objet en Java 87
' $
Constructeur et surcharge
Même si le nom d’un constructeur est imposé, la surcharge permet de
définir plusieurs constructeurs pour une même classe.
Exemple : On peut souhaiter initialiser une équation à partir de la somme et
du produit de ses racines.
public Équation(double somme, double produit) {
this(1, -somme, produit);
// Appel au constructeur Équation(double, double, double)
// Cet appel est nécessairement la première instruction !
}
& %
X. Crégut Programmation objet en Java 90
' $
& %
X. Crégut Programmation objet en Java 91
' $
& %
X. Crégut Programmation objet en Java 92
' $
Initialisation d’objet : Bilan
Voici ce que fait Java lors de la création d’un objet :
1. Initialisation des attributs avec la valeur par défaut de leur type ;
2. Utilisation des valeurs par défaut fournies par le programmeur ;
3. Exécution des initialiseurs dans leur ordre d’apparition ;
4. Exécution du constructeur :
– si aucun constructeur n’est défini sur la classe, c’est le constructeur
prédéfini qui est utilisé. Le programmeur ne doit pas fournir de
paramètre au constructeur ;
– si au moins un constructeur est défini sur la classe, le programmeur
doit fournir des paramètres (éventuellement aucun !) qui permettent
au compilateur de choisir l’un des constructeurs de la classe.
Conseil : Préférer les constructeurs explicites aux autres initialisations
& %
(valeurs par défaut, initialiseurs, constructeur par défaut synthétisé).
& %
l’appeler ! ! !
& %
X. Crégut Programmation objet en Java 95
' $
sqrt(double) est bien une méthode mais elle n’est pas appliquée à un objet
mais à la classe Math. C’est une méthode de classe.
On constate que sqrt(double) travaille exclusivement sur son paramètre.
Que penser de l’instruction suivante :
[Link]("Que suis-je ?");
out est un attribut (puisqu’il n’est pas suivi de parenthèses) mais il est
appliqué à une classe (System). C’est parce qu’il n’est pas spécifique d’un
objet particulier : la sortie standard est la même pour tout le monde !
C’est en fait un attribut de classe.
& %
X. Crégut Programmation objet en Java 96
' $
& %
}
& %
X. Crégut Programmation objet en Java 99
' $
Méthodes de classe
Définition : Une méthode de classe est une méthode indépendante de toute
instance de la classe. Elle est donc appliquée à une classe et non à un objet.
Conséquence : Une méthode de classe n’a pas de paramètre implicite (this)
et ne peut donc pas utiliser les attributs et méthodes d’instance de sa classe.
public class Date {
private int jour, mois, année;
static private int nbCréées = 0; // nombre de dates créées
& %
Exercice 18 Comment savoir si une méthode doit être de classe ?
& %
classes Math ou [Link].
& %
Java, il est donc nécessaire de définir des méthodes d’accès.
& %
X. Crégut Programmation objet en Java 103
' $
Règles de nommage :
– paquetage : tout en minuscule ([Link]) ;
– classe : les initiales en majuscule (Equation, MaClasse) ;
– méthode : en minuscule sauf l’initial des mots internes en majuscule
(résoudre, setCoeffA, maMéthode) ;
– attribut : comme les méthodes ;
– constante : tout en majuscule avec les mots séparés par des soulignés (_)
(MAX, MA_CONSTANTE).
& %
X. Crégut Programmation objet en Java 104
' $
Conventions de codage : structure d’un fichier
package ...; // définition du paquetage d’appartenance
/**
* Commentaire de documentation de la classe
* @version
* @author Prénom Nom
*/
// constructeurs
& %
}
& %
X. Crégut Programmation objet en Java 106
' $
– Ne pas utiliser directement des constantes littérales (sauf pour -1, 0 et 1).
– Éviter les affectations multiples (a = b = c;) ou dans des expressions
a = (a = b + c) + r;
& %
X. Crégut Programmation objet en Java 107
' $
& %
X. Crégut Programmation objet en Java 108
' $
Les tableaux
& %
X. Crégut Programmation objet en Java 109
' $
& %
X. Crégut Programmation objet en Java 111
' $
Tableaux d’objets
On peut bien sûr créer des tableaux d’objets. Tout fonctionne comme les
tableaux de types élémentaires sauf que le contenu d’une case est une
poignée sur un objet du type précisé.
1 public class TableauÉquations {
2 public static void main (String args []) {
3 Équation[] système = new Équation[3];
4 système[0] = new Équation(1, 5, 6);
5 système[1] = new Équation(4, 4);
6 for (int i = 0; i < systè[Link]; i++) {
7 système[i].résoudre();
8 [Link]("Équation " + (i+1) + " : ");
9 système[i].afficher();
10 [Link]("x1 = " + système[i].x1);
11 [Link]("x2 = " + système[i].x2);
12 }
13 }
14 }
& %
X. Crégut Programmation objet en Java 112
' $
Résultat de l’exécution
Équation 1 : 1.0*x2 + 5.0*x + 6.0 = 0
x1 = -2.0
x2 = -3.0
Équation 2 : 1.0*x2 + -4.0*x + 4.0 = 0
x1 = 2.0
x2 = 2.0
Exception in thread "main" [Link]
at TableauÉ[Link](TableauÉ[Link])
& %
11 }
& %
X. Crégut Programmation objet en Java 114
' $
& %
X. Crégut Programmation objet en Java 115
' $
& %
X. Crégut Programmation objet en Java 116
' $
La classe String
Attention : Les « String » sont des objets. Elles sont donc accessibles au
moyen de poignées.
String s0; // Une poignée non initialisée (éventuellement null)
String s1 = null; // Une poignée initialisée à null
String s2 = ""; // Une chaîne de caractères vide
String s3 = "Bonjour";
String s4 = new String("Bonjour"); // équivalent mais plus long !
String s5 = s3 + "Xavier"; // concaténation
& %
Attention : Les parenthèses changent l’évaluation !
& %
23 // replaceAll et split ont une expression régulière comme paramètre
& %
(voir StringBuffer.)
La classe StringBuffer
Un « StringBuffer » est une chaîne de caractères (comme « String ») qui
peut être modifiée !
En plus de la majorité des méthodes de String, StringBuffer propose
– append : ajouter à la fin de la chaîne ;
– insert : ajouter à une position spécifiée de la chaîne.
Ces deux méthodes sont largement surchargées !
Passage de String à StringBuffer et inversement :
StringBuffer t1 = new StringBuffer("Bonjour");
StringBuffer t2 = new StringBuffer(); // Chaîne de longueur 0 !
String s2 = [Link]();
StringBuffer t3 = new StringBuffer(s2);
StringBuffer t4 = "toto"; // Interdit !
& %
X. Crégut Programmation objet en Java 120
' $
Pourquoi String et StringBuffer ?
– Les « String » sont inaltérables. Ainsi, l’affectation entre chaînes de
caractères peut se faire par affectation des poignées, sans copies
explicites.
=⇒ gain de mémoire et de temps.
– Le caractère inaltérable des « String » fait que toute modification
nécessite la création d’une nouvelle chaîne, donc de l’allocation de
mémoire !
=⇒ StringBuffer permet de faire des modifications sur la même zone
mémoire et évite donc les réallocations.
Remarque : StringBuffer est utilisée par le compilateur pour implanter la
concaténation des chaînes de caractères. L’instruction :
x = "a" + 4 + "c"
& %
x = new StringBuffer().append("a").append(4).append("c").toString()
& %
X. Crégut Programmation objet en Java 122
' $
String ou StringBuffer ?
Temps d’exécution :
> time java ConcatenerString > time java ConcatenerStringBuffer
Longueur de chaîne = 100000 Longueur de chaîne = 100000
real 1335.64 real 0.70
user 1302.43 user 0.58
sys 26.20 sys 0.06
Les concaténations entre String sont plus claires que les opérations sur
StringBuffer, donc :
– Préférer les « StringBuffer » si de nombreuses modifications doivent être
apportées à une chaîne (par exemple dans une boucle).
– Préférer les « String » pour des affections simples de chaînes
(le compilateur fera la transformation en StringBuffer pour vous !).
& %
X. Crégut Programmation objet en Java 123
' $
– Les types primitifs (int, double, boolean...) ne sont pas des objets.
– Cependant, pour chacun des types primitifs, il existe une classe
correspondante, appelée classe enveloppe (wrapper) dans [Link].
– Chaque classe enveloppe permet de construire un objet à partir d’une
valeur du type primitif, et possède une méthode d’accès retournant la
valeur du type primitif.
– Les instances des classes enveloppes sont des objets non altérables !
– Les classe enveloppes fournissent les valeurs minimales (MIN_VALUE) et
maximales (MAX_VALUE) du type.
– Intérêt : Les classes enveloppes n’ont d’intérêt que si l’on a besoin de
considérer les types primitifs comme des objets (T. 155).
– Depuis Java 1.5, la conversion entre types primitifs et classes enveloppes
& %
est automatique (auto boxing/unboxing).
Les constructeurs :
public Integer(int);
public Integer(String);
& %
X. Crégut Programmation objet en Java 125
' $
Exemple d’utilisation de la classe Integer (Java 1.4)
1 Integer n1 = new Integer(15); // construire depuis un int
2 Integer n2 = new Integer("100"); // construire depuis String
3
4 // int i1 = n1; // Erreur : types différents !
5 int i1 = [Link]();
6 double d2 = [Link](); // d2 == 100.0
7
8 int i3 = [Link]("421"); // i3 == 421
9 Integer n4 = [Link]("0xFFF"); // n4 == 4095
10
11 boolean test;
12 // test = n1 == i1; // Types incompatibles
13 // test = i1 == n1; // Types incompatibles
14
15 test = [Link]() == i1; // true
16 test = n1 == new Integer(i1); // false
17
18 // Convertir le premier paramètre de la ligne de commande.
19 if ([Link] > 0) {
20 Integer n5 = [Link](args[0]);
21 int i5 = [Link](args[0]);
22 // Attention à NumberFormatException !
& %
23 }
& %
23 }
Les énumérations
& %
16 }
& %
– On peut ajouter des méthodes et des attributs dans une classe énumérée.
& %
tout
1..* 1..*
Train Wagon Siège
{ordered}
0..1 0..1
passager * conducteur
Personne
...
}
& %
– Pour la composition, plusieurs stratégies sont possibles (voir TD).
Interfaces
& %
X. Crégut Programmation objet en Java 134
' $
Exercice 20 : Liste de réels
On considère une liste de réels (double) offrant les opérations suivantes :
– connaître la taille de la liste (son nombre d’éléments) ;
– obtenir l’élément à la position i de la liste ;
– remplacer le ie élément de la liste ;
– ajouter un élément dans la liste en position i ;
– supprimer l’élément à la position i de la liste.
On doit avoir 0 <= indice < taille sauf pour ajouter car ajouter à taille
signifie ajouter en fin. On ne traitera pas les cas d’erreur.
20.2 Écrire une méthode dans une classe OutilsListe qui calcule la somme
des réels d’une liste.
20.3 On hésite entre stocker les éléments de la liste dans un tableau ou les
& %
chaîner. Ceci remet-il en cause le travail fait ?
& %
=⇒ ListeTab et ListeChaînée sont des réalisations de l’interface Liste.
relation de réalisation
ListeTab
& %
X. Crégut Programmation objet en Java 137
' $
& %
– les utilisateurs savent comment utiliser une interface.
& %
24 }
& %
X. Crégut Programmation objet en Java 140
' $
Instances d’une interface ?
Constatation : Une interface décrit seulement un comportement, une
abstraction (elle contient la spécification et non le code).
=⇒ Une interface décrit une notion abstraite.
Conséquence : Impossible de créer une instance à partir d’une interface.
Justification : Quel sens aurait une telle instance ?
1 /** Une interface ne peut pas être instanciée ! */
2 public class TestListeInstanceErreur {
3 public static void main(String[] args) {
4 Liste une_liste = new Liste();
5 une_liste.ajouter(0, 3.14); // Quel sens ?
6 }
7 }
[Link]: Liste is abstract; cannot be instantiated
Liste une_liste = new Liste();
^
& %
elles, avoir des instances (ListeTab et ListeChaînée).
Attention : Si une classe ne définit pas toutes les méthodes des interfaces
qu’elle réalise, elle est dite abstraite (voir T. 190).
& %
X. Crégut Programmation objet en Java 142
' $
Exemple de réalisation
1 /** Réalisation de la Liste en stockant les éléments dans un tableau. */
2 public class ListeTab implements Liste {
3 private double[] elements; // les éléments de la liste
4 private int nb; // la taille de la liste
5 /** Construire une liste vide.
6 * @param capacite capacité initiale de la liste
7 */
8 public ListeTab(int capacite) {
9 [Link] = new double[capacite];
10 [Link] = 0; // la liste est initialement vide
11 }
12
13 public int taille() {
14 return [Link];
15 }
16
17 public double item(int index) {
18 return [Link][index];
19 }
20
21 public void remplacer(int index, double x) {
22 [Link][index] = x;
& %
23 }
24
25 public void ajouter(int index, double x) {
26 if ([Link] >= [Link]) { // tableau trop petit !
27 // agrandir le tableau : pourrait être plus efficace !
28 double[] nouveau = new double[[Link]+2]; // 2 arbitraire !
29 [Link]([Link], 0, nouveau, 0, [Link]);
30 [Link] = nouveau;
31 }
32 // décaler les éléments à partir de index
33 [Link]([Link], index,
34 [Link], index+1, [Link]-index);
35 // ranger le nouvel élément
36 [Link][index] = x;
37 [Link]++;
38 }
39
40 public void supprimer(int index) {
41 [Link]([Link], index+1,
42 [Link], index, [Link]-index-1);
43 [Link]--;
44 }
45 }
& %
X. Crégut Programmation objet en Java 144
' $
& %
X. Crégut Programmation objet en Java 145
' $
Interfaces et poignées
Question : On ne peut pas créer d’instances d’une interface... Quel intérêt alors ?
Réponse : Une interface permet de déclarer des poignées.
Propriété : Une interface définit un type (par exemple, le type Liste).
Intérêt : Écrire des méthodes (et classes) qui s’appuient sur des interfaces sans en
connaître les réalisations actuelles... ni futures !
1 /** Quelques méthodes utiles sur les listes. */
2 public class OutilsListe {
3 /* Calculer la somme des réels d’une liste.
4 * @param l la liste dont on veut sommer les valeurs
5 * @return la somme des valeurs de l */
6 static public double somme(Liste l) {
7 double resultat = 0;
8 for (int i = 0; i < [Link](); i++) {
9 resultat += [Link](i);
10 }
11 return resultat;
12 }
13 }
& %
Question : Mais que peut bien désigner le parmètre l de type Liste ?
& %
qui désigne soit une liste avec tableau, soit une liste chaînée.
& %
poignée non nulle déclarée de type I, est-on sûr que p.m() a un sens ?
& %
X. Crégut Programmation objet en Java 149
' $
& %
X. Crégut Programmation objet en Java 150
' $
Remarque : Les contrats définis sur une interface (ici Liste) s’appliquent à
toutes ses réalisations (ici ListeTab, ListeChaînée...).
& %
X. Crégut Programmation objet en Java 151
' $
& %
X. Crégut Programmation objet en Java 152
' $
1 /** Spécification d’une liste. @version 1.4 */
2 public interface Liste {
3 //@ public invariant taille() >= 0;
4
5 /** L’itérateur qui permet de réaliser un parcours de la liste. */
6 Iterateur iterateur();
7
8 /* Obtenir la taille de la liste.
9 * @return nombre d’éléments dans la liste */
10 /*@ pure @*/ int taille();
11
12 /* Obtenir un élément de la liste.
13 * @param indice position de l’élément */
14 //@ requires 0 <= indice && indice < taille();
15 /*@ pure @*/ double item(int indice);
16
17 /* Remplacer un élément de la liste.
18 * @param indice indice de l’élément à remplacer
19 * @param x nouvelle valeur */
20 //@ requires 0 <= indice && indice < taille(); // indice valide
21 //@ ensures item(indice) == x; // élément remplacé
22 //@ ensures (\forall int i; // autre éléments inchangés
23 //@ 0 <= i && i < taille();
24 //@ i == indice || item(i) == \old(item(i)));
25 //@ ensures taille() == \old(taille()); // pas d’ajout !
26 void remplacer(int indice, double x);
& %
27
28 /* Ajouter un élément dans la liste.
& %
53
Généricité
& %
X. Crégut Programmation objet en Java 155
' $
& %
X. Crégut Programmation objet en Java 156
' $
Solutions
Principe : On constate que seul le type des éléments de la liste change.
Les algorithmes (donc le code des méthodes) restent identiques.
Solution 1 (mauvaise) : Faire du copier/coller. Il faut :
– Renommer tous les fichiers ([Link], [Link]...) en prenant un
nouveau nom (ListeDouble, ListeTabDouble, ListeFraction... par exemple).
– Remplacer le type double par le nouveau type.
Inconvénients : Même si ces transformations peuvent être automatisées
elles sont fastidieuses !
Solution 2 (mauvaise) : Celle utilisée avant java 1.5. Voir T. 206.
Solution 3 (la bonne) : Utiliser la généricité (à partir de java 1.5 !)
& %
X. Crégut Programmation objet en Java 157
' $
Version générique de l’interface Liste
1 /** Spécification d’une liste. @version 1.4 */
2 public interface Liste<T> {
3 /* Obtenir la taille de la liste.
4 * @return nombre d’éléments dans la liste */
5 int taille();
6
7 /* Obtenir un élément de la liste.
8 * @param indice position de l’élément */
9 T item(int indice);
10
11 /* Remplacer un élément de la liste.
12 * @param indice indice de l’élément à remplacer
13 * @param x nouvelle valeur */
14 void remplacer(int indice, T x);
15
16 /* Ajouter un élément dans la liste.
17 * @param indice indice où doit se trouver le nouvel élément
18 * @param x élément à insérer */
19 void ajouter(int indice, T x);
20
21 /* Supprimer un élément de la liste.
22 * @param indice indice de l’élément à supprimer */
23 void supprimer(int indice);
& %
24 }
Remarque : Le type des éléments des listes est précisé entre < >.
Remarque : On utilise Double et non double car il faut impérativement un
& %
objet. L’auto boxing/unboxing rend ceci transparent.
Explications
Une classe générique est une classe paramétrée par un (ou plusieurs) types.
public class Liste<T> { ... }
public class Paire<A, B> { ... }
Pour obtenir une « classe » (appelée type paramétré), il faut préciser les
paramètres de généricité (les valeurs des variables de type).
Liste<Double>, Liste<Personne>...
Paire<String, Livre>...
& %
14 }
& %
25 }
26 }
& %
25 public void remplacer(int index, T x) {
& %
50 }
Exercice 25 Écrire une méthode qui échange deux éléments d’un tableau à
des indices donnés.
Elle doit par exemple pouvoir être utilisée pour échanger deux éléments
d’un tableau de chaînes de caractères, d’un tableau de points, etc.
& %
X. Crégut Programmation objet en Java 165
' $
Méthodes de classes génériques
1 public class GenericiteSwap {
2 /** Permuter deux éléments d’un tableau.
3 * @param tab le tableau
4 * @param i indice du premier élément
5 * @param j indice du deuxième élément */
6 public static <T> void swap(T[] tab, int i, int j) {
7 T tmp = tab[i];
8 tab[i] = tab[j];
9 tab[j] = tmp;
10 }
11
12 public static void main(String[] args) {
13 String[] ts = { "I", "II", "III", "IV", "V" };
14 swap(ts, 0, 1);
15 assert ts[0].equals("II");
16 assert ts[1].equals("I");
17
18 Integer[] ti = { 1, 2, 3, 4, 5 }; // et pas int !!!
19 swap(ti, 0, 1);
20 assert ti[0] == 2;
21 assert ti[1] == 1;
22 }
& %
23 }
& %
X. Crégut Programmation objet en Java 167
' $
Généricité contrainte : méthode max
1 public class GenericiteMax {
2 /** Déterminer le plus grand élément d’un tableau.
3 * @param tab le tableau des éléments
4 * @return le plus grand élément de tab */
5 public static <T extends Comparable<T>> T max(T[] tab) {
6 T resultat = null;
7 for (T x : tab) {
8 if (resultat == null || [Link](x) < 0) {
9 resultat = x;
10 }
11 }
12 return resultat;
13 }
14
15 /** Programme de test de [Link] */
16 public static void main(String[] args) {
17 Integer[] ti = { 1, 2, 3, 5, 4 };
18 assert max(ti) == 5;
19
20 String[] ts = { "I", "II", "IV", "V" } ;
21 assert max(ts).equals("V");
22 }
& %
23 }
– Dans le code de la méthode max, on peut donc utiliser compareTo sur les
éléments du tableau.
– Integer réalise l’interface Comparable<Integer> et String réalise
Comparable<String>. On peut calculer le max des tableaux ti et ts.
& %
X. Crégut Programmation objet en Java 169
' $
Relation d’héritage
& %
X. Crégut Programmation objet en Java 170
' $
Exemple introductif
& %
X. Crégut Programmation objet en Java 171
' $
& %
X. Crégut Programmation objet en Java 172
' $
& %
X. Crégut Programmation objet en Java 173
' $
& %
X. Crégut Programmation objet en Java 174
' $
Héritage
Buts : plusieurs objectifs (non liés) :
– définir une nouvelle classe :
– comme spécialisation d’une classe existante ;
– comme généralisation de classes existantes (factorisation des
propriétés et comportement communs) ;
– définir une relation de sous-typage entre classes (substitutionalité) ;
– copier virtuellement les caractéristiques de classes existantes dans la
définition d’une nouvelle classe (héritage).
Moyen : La relation d’héritage en Java (appelée relation de
généralisation/spécialisation en UML).
Exemples :
– PointNommé est une spécialisation de Point (un point avec un nom).
– Point est une généralisation de PointNommé, PointPondéré,
& %
PointColoré...
classe fille
classe dérivée
PointNommé
sous−classe
Notation en Java
public class PointNommé
extends Point // relation d’héritage
{ ... }
Vocabulaire : On parle également d’ancêtres et de descendants (transitivité
& %
de la relation d’héritage)
& %
Attention : Ne pas confondre extends et import !
Enrichissement
Dans la classe dérivée, on peut ajouter :
– de nouveaux attributs (conseil : prendre de nouveaux noms !) ;
private String nom;
– de nouvelles méthodes.
public String getNom() { ... }
public void nommer(String nouveauNom) { ... }
& %
X. Crégut Programmation objet en Java 178
' $
Héritage et constructeurs
Principe : Tout constructeur d’une classe dérivée doit appeler un des
constructeurs de la classe de base.
Justification : Hériter d’une classe, c’est récupérer ses caractéristiques qui
doivent donc être initialisées. Ds un PointNommé, il y a un Point... et un nom.
En pratique : On utilise super suivi des paramètres effectifs permettant au
compilateur de sélectionner le constructeur de la classe parente.
public PointNomme(String sonNom, double vx, double vy) {
super(vx, vy); // appel au constructeur de Point (1ère instruction)
nom = sonNom;
}
– L’appel à super doit être la première instruction du constructeur !
– La classe parente est toujours initialisée avant la sous-classe.
– Si aucun appel à super n’est fait, le compilateur appelle automatiquement
le constructeur par défaut de la classe parente super().
& %
=⇒ D’où le danger de définir un constructeur par défaut !
& %
Remarque : afficher() ne donne pas le nom du PointNommé.
& %
Attention : La redéfinition n’est pas de la surcharge !
& %
Remarque : Le principe de substitution est vérifié à la compilation.
& %
le type réel de l’objet attaché à la poignée p (X).
& %
X. Crégut Programmation objet en Java 185
' $
Le compilateur interdit cet appel car il s’appuie sur les types apparents.
Or, un PointNommé est attaché à p. L’affectation aurait donc un sens.
C’est le pb de l’affectation renversée résolu en Java par le « transtypage » :
q = (PointNomme) p; // Autorisé par le compilateur
& %
X. Crégut Programmation objet en Java 186
' $
Liaison tardive : intérêt
Intérêt : Éviter des structures de type choix multiples :
– Les alternatives sont traitées par le compilateur (sûreté : pas d’oubli).
– L’ajout d’une nouvelle alternative est facilité (extensibilité).
En l’absence de liaison tardive, il faudrait tester explicitement le type de
l’objet associé à une poignée pour choisir quelle méthode lui appliquer :
Point q = ...; // Afficher les caractéristiques de q
if (q instanceof PointNomme) // tester le type réel
// afficher q comme un PointNomme
else if (q instanceof PointColoré ) // tester le type réel
// afficher q comme un PointColoré
...
else // Ce n’est donc qu’un point !
// afficher q comme un Point
Remarque : Permet de réaliser le principe du choix unique (B. Meyer) :
« Chaque fois qu’un système logiciel doit prendre en compte un ensemble
d’alternatives, un et un seul module du système doit en connaître la liste
& %
exhaustive ».
Le modifieur final
Définition : Le modifieur final donne le sens d’immuable, de non
modifiable.
Il est utilisé :
– pour une variable locale : c’est une constante (qui doit donc
nécessairement être initialisée lors de sa déclaration) ;
– un attribut d’instance ou de classe (qui doit être nécessairement initialisé
par une valeur par défaut) ;
– une méthode : la méthode ne peut pas être redéfinie par une classe
dérivée. Elle n’est donc pas polymorphe ;
– une classe : la classe ne peut pas être spécialisée. Elle n’aura donc aucun
descendant (aucune de ses méthodes n’est donc polymorphe).
& %
X. Crégut Programmation objet en Java 188
' $
La classe Object
En Java, si une classe n’a pas de classe parente, elle hérite implicitement de
la classe Object. C’est l’ancêtre commun à toutes les classes.
Elle contient en particulier les méthodes :
– public boolean equals(Object obj); Égalité de this et obj (par défaut
égalité des adresses). Elle a cependant le sens d’égalité logique et doit
donc être redéfinie (String, etc.)
– public String toString(); chaîne de caractères décrivant l’objet. Elle est
utilisée dans print, println et l’opérateur de concaténation + par
l’intermédiaire de [Link](Object).
– protected void finalize(); Méthode appelée lorsque le ramasse-miettes
récupère la mémoire d’un objet.
...
Voir le transparent T. 302 pour d’autres caractéristiques de la classe Object.
& %
X. Crégut Programmation objet en Java 189
' $
& %
X. Crégut Programmation objet en Java 190
' $
Méthode retardée
Définition : Une méthode retardée (ou abstraite) est une méthode dont on
ne sait pas écrire le code. Cette méthode est notée abstract (modifieur).
Exemple : Un objet géométrique peut être affiché et déplacé mais si on ne
connaît pas le type d’objet on ne sait pas écrire le code de ces méthodes
/** Afficher les caractéristiques de l’objet géométrique. */
abstract public void afficher();
& %
23 abstract public void translater(double dx, double dy);
} 24
ObjetGéométrique
−couleur: Couleur
+getCouleur(): Couleur
+setCouleur(nc; Couleur)
+afficher()
+translater(dx: double, dy: double)
+«constructeur» ObjetGéométrique(c: Couleur)
Point
−x: double
−y: double Segment
−nom: String
+getNom(): String
+setNom(n: String)
+afficher()
+«constructor» PointNommé(vx: double, vy: double, n: String)
& %
X. Crégut Programmation objet en Java 193
' $
Commentaires sur la notation UML
– Essayer de placer les classes parentes au-dessus des sous-classes.
– Ne pas confondre la relation de généralisation/spécialisation avec une
relation d’association avec contrainte de navigation !
– Le nom des classes abstraites et des méthodes retardées est noté en
italique... Ce qui n’est pas toujours très visible !
Remarque : On peut également utiliser la contrainte {abstract}.
– Dans une sous-classe UML, on ne fait apparaître que les méthodes qui
sont définies ou redéfinies dans la sous-classe.
Exemples : Point définit afficher et translater qui étaient spécifiées dans
ObjetGéométrique .
Dans PointNommé on ne fait apparaître ni translater ni distance car ce sont
celles de Point. En revanche, afficher est redéfinie.
– La relation entre Segment et Point est une relation de composition.
& %
Comment la réaliser en Java ?
Classe abstraite
Définition : Une classe qui possède une méthode retardée (propre ou
héritée) est nécessairement une classe abstraite.
Conséquence : Une classe abstraite ne peut pas être instanciée. Elle devra
donc avoir des descendants qui définissent les méthodes retardées.
Remarque : En Java, on peut déclarer une classe abstraite même si elle ne
possède pas de méthodes retardées.
Constructeurs : Une classe abstraite peut avoir des constructeurs et il est
recommandé d’en définir (s’ils ont un sens).
Exercice 30 Quel est l’intérêt d’utiliser une méthode retardée plutôt que de
définir une méthode avec un code vide (ou affichant un message d’erreur)
qui serait redéfinie dans les sous-classes ?
& %
X. Crégut Programmation objet en Java 195
' $
Intérêt des classes abstraites
– Factoriser, même si on n’est pas capable de le coder, le comportement
de plusieurs classes. Ce comportement peut alors être utilisé.
Exercice 31 Définir une classe Groupe (d’objets géométriques). Groupe
est-elle abstraite ? Peut-on mettre un Groupe dans un Groupe ?
– Classifier les objets, par exemple les objets géométriques généraux,
fermés, ouverts, etc.
– Permettre au compilateur de vérifier que la définition des méthodes
retardées héritées est bien donnée dans les sous-classes.
Remarque : Une sous-classe d’une classe abstraite peut être concrète ou
abstraite suivant qu’elle donne ou non une définition à toutes ses méthodes
(propres et héritées).
MOBILEHOME HYDRAVION
& %
notion d’interface.
Exercice 33
33.1 Comment faire pour écrire une méthode de tri d’un tableau qui soit
générale ?
33.2 Comment faire pour écrire une méthode permettant de dessiner sur
un écran graphique tous les éléments d’un tableau ?
33.3 Comment faire pour qu’un objet, un point par exemple, puisse
apparaître à la fois comme élément du premier tableau et du deuxième
tableau ?
& %
X. Crégut Programmation objet en Java 198
' $
Interfaces et héritage
Héritage multiple : Une interface peut spécialiser plusieurs interfaces.
interface I extends I1, I2 { ... }
& %
}
& %
Attention : La liaison tardive fait que a < b ≡ b > a peut être faux.
& %
de OrdreTotal.
& %
Test : Une classe dérivée doit réussir les tests de ses classes parentes.
& %
X. Crégut Programmation objet en Java 203
' $
Réutilisation
& %
La question est alors : « Quoi choisir entre utilisation et héritage ? »
Généricité vs Héritage
Exercice 35
35.1 Quelles sont les relations entre un ensemble de points, une liste de
points et une liste chaînée de points ?
35.2 Quelles sont les relations entre une liste de points, une liste de
personnes, une liste de livres, etc.
35.4 Comment adapter la pile de points pour avoir une pile de livres ou
une pile de personnes ?
& %
X. Crégut Programmation objet en Java 206
' $
Généricité vs Héritage : schéma
& %
polymorphisme d’héritage ou sous-typage.
& %
X. Crégut Programmation objet en Java 208
' $
while (![Link]()) {
[Link]().afficher();
pile.dépiler();
}
}
}
& %
X. Crégut Programmation objet en Java 209
' $
& %
X. Crégut Programmation objet en Java 210
' $
while (![Link]()) {
((Point)[Link]()).afficher();
pile.dépiler();
}
}
}
& %
X. Crégut Programmation objet en Java 211
' $
& %
X. Crégut Programmation objet en Java 212
' $
while (![Link]()) {
[Link]().afficher();
pile.dépiler();
}
}
}
& %
1.5 (avec l’option -source 1.5) !
Conséquences
– D’un point du vue pratique, il n’existe qu’une seule classe, par exemple
Pile et non Pile<String>, Pile<Point>... (différent de C++).
Si G est une classe Générique et A et B deux types, G<A> et G<B> ont même
classe G. L’information de généricité a disparu.
– Le compilateur engendre donc les transtypages nécessaires.
– Les informations de types sont uniquement connues du compilateur et
non de la JVM. Dans le byte code, les informations de généricité ont
disparu !
– On ne peut pas utiliser instanceof ni faire du transtypage avec type
générique.
& %
X. Crégut Programmation objet en Java 216
' $
1 public class TestErreurTranstypage {
2 public static void main(String[] args) {
3 Paire<String, Integer> pp = new Paire<String, Integer>("I", 1);
4 Paire po = pp;
5 Object o = pp;
6
7 Paire<String, Integer> p2 = null;
8 p2 = po; // unchecked conversion
9 p2 = (Paire) po; // unchecked conversion
10 p2 = (Paire<String, Integer>) po; // unchecked cast
11
12 p2 = o; // incompatible types
13 p2 = (Paire) o; // unchecked conversion
14 p2 = (Paire<String, Integer>) o; // unchecked cast
15
16 Paire<String, Object> ps = (Paire<String, Object>) pp;
17 // inconvertible types
18 // Ce n’est pas vraiment du transtypage
19 // puisqu’il est interdit par le compilateur
20
21 assert po instanceof Paire<String, Integer>;
22 // llegal generic type for instanceof
23 assert po instanceof Paire;
24 }
& %
25 }
Généricité et sous-typage
& %
X. Crégut Programmation objet en Java 218
' $
Généricité et sous-typage
1 public class TestErreurGenericiteSousTypage {
2 public static void main(String[] args) {
3 ListeTab<String> ls = new ListeTab<String> (10);
4 ListeTab<Object> lo = ls;
5 [Link](0, "texte");
6 [Link](0, 15.5); // en fait new Double(15.5);
7 String s = [Link](0);
8 [Link](s);
9 }
10 }
& %
immuable !
& %
37.2 Proposer une nouvelle version de la méthode afficher.
& %
Rq : On peut utiliser des types « wildcard » contraints (<? extends Type>).
Exercice 38 Écrire une méthode de classe qui copie une liste dans une
& %
autre.
& %
X. Crégut Programmation objet en Java 223
' $
& %
X. Crégut Programmation objet en Java 225
' $
& %
X. Crégut Programmation objet en Java 226
' $
Fraction et principe de protection en écriture des attributs
public class Fraction {
private int numérateur;
private int dénominateur;
public Fraction(int num, int dén) { set(num, dén); }
public int getNumérateur() { return numérateur; }
public int getDénominateur() { return dénominateur; }
public void set(int n, int d) { ... }
public void set(int n) { set(n, 1); }
...
}
Remarques :
– Les deux attributs sont private ;
– Chaque attribut possède son accesseur ;
– Les modificateurs individuels des attributs ne sont pas définis. Ils auraient
pu être définies comme protected.
Notation : La convention Java est de préfixer le nom de l’attribut par
& %
« get » et le modifieur par « set ».
Programmation défensive
Principe : Les cas anormaux sont testés dans le sous-programme.
Traitements possibles :
– signaler un message d’erreur et continuer l’exécution ;
– signaler un message d’erreur et arrêter l’exécution ;
– renvoyer un code d’erreur ;
– particulariser une valeur de retour pour indiquer l’erreur ;
– réaliser un traitement par défaut (renvoyer une valeur valide) ;
– appeler une fonction fournie par l’appelant ;
Questions :
– Qui détecte l’erreur (ou le problème) ?
– Qui est capable de le résoudre ?
Meilleure solution : Signaler l’erreur en levant une exception.
& %
X. Crégut Programmation objet en Java 228
' $
Exceptions
– Exemple introductif
– Intérêt des exceptions
– Classification des exceptions en Java
– Mécanisme des exceptions
– Les exceptions utilisateur
– Exemples d’utilisation des exceptions
– Une exception est une classe
– Conseils sur l’utilisation des exceptions
& %
X. Crégut Programmation objet en Java 229
' $
Exceptions : Exemple démonstratif
1 import [Link].*; // io pour input/output
2 public class Moyenne {
3 /** Afficher la moyenne des valeurs réelles du fichier args[0] */
4 public static void main (String args []) {
5 try {
6 BufferedReader in = new BufferedReader(new FileReader(args[0]));
7 int somme = 0, nb = 0; // somme et nb de valeurs lues
8 String ligne; // une ligne du fichier
9 while ((ligne = [Link]()) != null) {
10 somme += [Link](ligne);
11 nb++;
12 }
13 [Link]();
14 [Link]("Moyenne = " + (somme / nb));
15 } catch (IOException e) {
16 [Link]("Problème d’E/S : " + e);
17 } catch (NumberFormatException e) {
18 [Link]("Une donnée non numérique : " + e);
19 }
20 }
& %
21 }
& %
X. Crégut Programmation objet en Java 231
' $
Exceptions : Intérêts
Les exceptions permettent :
– de transférer le flôt de contrôle de l’instruction qui lève l’exception (qui
détecte l’anomalie) vers la partie du programme capable de la traiter ;
– d’éviter de surcharger le code d’une méthode avec de nombreux tests
concernant des cas anormaux ;
– de regrouper le traitement des cas anormaux et erreurs ;
– de différencier les anomalies (différents types d’exception) ;
Remarque : Les exceptions peuvent être considérées comme des « goto »
disciplinés.
Attention : Il ne faut pas abuser des exceptions et les réserver aux cas
réellement anormaux ou d’erreurs.
& %
X. Crégut Programmation objet en Java 232
' $
Exceptions : Principe
Motivation : Mécanisme pour le traitement des erreurs et/ou des cas
anormaux.
Principe : Le mécanisme repose sur trois phases :
– une exception est levée quand une erreur ou anomalie est détectée ;
– l’exception est propagée : l’exécution séquentielle du programme est
interrompue et le flot de contrôle est transféré aux gestionnaires
d’exception ;
– L’exception est (éventuellement) récupérée par un gestionnaire
d’exception. Elle est traitée et l’exécution « normale » reprend avec les
instructions qui suivent le gestionnaire d’exception.
Remarque : Une exception non récupérée provoque l’arrêt du programme
(avec affichage de la trace des appels de méthodes depuis l’instruction qui a
levé l’exception jusqu’à l’instruction appelante de la méthode principale).
& %
X. Crégut Programmation objet en Java 233
' $
Mécanisme de propagation d’une exception
o1.m1(...)
o3.m3(...)
o2.m2(...)
o4.m4(...)
instr
Ex4
Ex1 Ex2
Exception Ex3
& %
X. Crégut Programmation objet en Java 234
' $
Illustration du mécanisme de propagation
1 /** Illustrer le mécanisme de propagation des exceptions */
2 public class ExceptionPropagation {
3 public static void m1() {
4 m2();
5 }
6 public static void m2() {
7 m3();
8 }
9 public static void m3() {
10 int n = 1 / 0; // division par zéro !
11 }
12 public static void main(String[] args) {
13 m1();
14 }
15 }
Exception in thread "main" [Link]: / by zero
at ExceptionPropagation.m3([Link])
at ExceptionPropagation.m2([Link])
at ExceptionPropagation.m1([Link])
at [Link]([Link])
& %
shell returned 1
Throwable
Error Exception
FileNotFoundException IllegalArgumentException
NumberFormatException
& %
X. Crégut Programmation objet en Java 236
' $
& %
Elles correspondent à la notion de robustesse.
& %
– Elles définissent des constructeurs de même signature que Throwable.
& %
+-- [Link]
& %
X. Crégut Programmation objet en Java 240
' $
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
& %
X. Crégut Programmation objet en Java 241
' $
Lever une exception
L’opérateur throw permet de lever une exception. Une exception est une
instance d’une classe descendant de Throwable (souvent de Exception).
Forme générale :
if (<condition anormale>) {
throw new TypeException(<paramètres effectifs>);
}
& %
}
& %
– Terminer le programme
& %
X. Crégut Programmation objet en Java 245
' $
Intérêt : Être sûr de libérer une ressource (faire un dispose sur un élément
graphique, fermer un fichier – à condition que l’ouverture ait réussi...).
& %
X. Crégut Programmation objet en Java 246
' $
Spécification des exceptions
Une exception correspond à un résultat transmis par une méthode. À ce
titre, Java impose de spécifier les exceptions levées ou propagées dans la
spécification d’une méthode.
Remarque : Java n’impose (et le compilateur ne vérifie) que la
spécification des exceptions sous contrôle.
Syntaxe : Toutes les exceptions sous contrôle qui sont levées ou propagées
par une méthode doivent être déclarées en utilisant le mot-clé throws.
<modifieurs> Type maMéthode(Type1 a) throws TypeExc1, TypeExc2 { ... }
& %
– soit déclarées comme étant propagées (clause throws).
& %
X. Crégut Programmation objet en Java 248
' $
Définition d’une exception : exceptions utilisateur
Exception : Une exception est tout objet instance d’une classe qui hérite de
Throwable. Cependant, une exception utilisateur hérite généralement de
Exception ou de l’une de ses descendantes.
Conseil : Choisir soigneusement la classe parente de son exception (en
particulier, l’exception doit-elle être sous contrôle ou hors contrôle ?).
Définition type d’une exception :
public class MathException extends Exception {
public MathException(String s) {
super(s);
}
public MathException() { // utile ?
}
}
Remarque : Cette classe, comme toute autre, peut avoir des attributs et des
& %
méthodes.
& %
X. Crégut Programmation objet en Java 250
' $
Exemples d’exceptions sur la classe Fraction
public class DivisionParZeroException extends Exception {
public DivisionParZeroException(String message) {
super(message);
}
}
public class Fraction {
private int numérateur;
private int dénominateur;
public Fraction(int num, int dén) throws DivisionParZeroException {
set(num, dén);
}
public void set(int n, int d) throws DivisionParZeroException {
if (d == 0) {
throw new DivisionParZeroException("Dénominateur nul");
}
...
}
public Fraction inverse() throws DivisionParZeroException {
return new Fraction(dénominateur, numérateur);
}
...
& %
}
& %
du bloc try qui est à l’origine de l’exception (cf commentaire) ?
42.1 Écrire le programme qui réalise cette somme en signalant les erreurs
éventuelles.
42.2 Comment faire pour indiquer le numéro de l’argument incorrect ?
42.3 Comment faire pour indiquer tous les arguments incorrects ?
& %
X. Crégut Programmation objet en Java 253
' $
L’exception FormatEntierException
/** Exception indiquant une représentation erronée d’un entier en base 10 */
public class FormatEntierException extends Exception {
private String entier; // l’entier sous forme de chaîne de caractères
private int indice; // indice de l’erreur dans entier
& %
}
& %
X. Crégut Programmation objet en Java 255
' $
& %
X. Crégut Programmation objet en Java 256
' $
& %
X. Crégut Programmation objet en Java 257
' $
& %
Programmation : Une méthode est le fournisseur, l’appelant est le client.
& %
Remarque : Utiliser l’égalité sur des réels est une erreur !
Exercice 43 : Pgcd
Donner les contrats de la méthode pgcd.
& %
X. Crégut Programmation objet en Java 261
' $
& %
(\product int i; i > 0 && i < 5; i); // 24, 1 * 2 * 3 * 4 (= 4!)
& %
}
& %
X. Crégut Programmation objet en Java 265
' $
Programmation par contrat : la classe Fraction (1/4)
/** La classe Fraction représente les fractions rationnelles.
* @author Xavier Crégut */
public class Fraction {
//@public invariant [Link](getNumerateur(),
//@ getDenominateur()) == 1;
//@public invariant getNumerateur() == 0 ==> getDenominateur() == 1;
& %
public Fraction(int n) { this(n, 1); }
& %
X. Crégut Programmation objet en Java 267
' $
Programmation par contrat : Fraction (3/4)
/** Changer le signe de la fraction. */
//@ ensures getNumerateur() == - \old(getNumerateur());
//@ ensures getDenominateur() == \old(getDenominateur());
public void opposer() { num = - num; }
& %
public Fraction inverse() { return new Fraction(den, num); }
& %
}
& %
X. Crégut Programmation objet en Java 270
' $
& %
X. Crégut Programmation objet en Java 271
' $
& %
22 }
Par défaut, les assertions ne sont pas vérifiées. Elles sont activées avec
l’option -ea et désactivées avec -da au niveau de la classe ou du paquetage
(-esa et -dsa pour les classes système).
licorne> java -ea RacineCarree # toutes les classes sauf système
Donnez un réel : 4
RC(4.0) = 2.0
RC(0) = 0.0
...
RC(81) = 9.0
Exception in thread "main"[Link]: x négatif -4.0
at [Link](RC_assert.java:4)
& %
at [Link](RC_assert.java:20)
& %
– mécanisme supporté par l’environnement de développement !
Classes internes...
& %
X. Crégut Programmation objet en Java 276
' $
Exercice 44 : Améliorer [Link]
Dans l’exercice 20, nous avons défini une interface Liste et deux
réalisations ListeTab (les éléments sont stockés dans un tableau) et
ListeChaînée (les éléments sont chaînés). Nous avons également écrit une
classe OutilsListe qui définit la méthode somme.
/** Quelques méthodes utiles sur les listes. */
public class OutilsListe {
/* Calculer la somme des réels d’une liste.
* @param l la liste dont on veut sommer les valeurs
* @return la somme des valeurs de l */
static public double somme(Liste l) {
double resultat = 0;
for (int i = 0; i < [Link](); i++) {
resultat += [Link](i);
}
return resultat;
}
}
& %
mécanisme plus efficace... et général.
& %
et définir une réalisation pour ListeTab, ListeChaînée...
& %
14 }
& %
X. Crégut Programmation objet en Java 280
' $
& %
X. Crégut Programmation objet en Java 281
' $
Discussion
De manière générale, l’itérateur (ici IterateurTab) devrait avoir accès à la
structure de données (ici ListeTab). Voir le diagramme de classe !
– IterateurTab est une classe extérieure à ListeTab
– IterateurTab doit accéder à la représentation interne de ListeTab
=⇒ deux solutions :
1. ListeTab ajoute des méthodes de manipulation de sa représentation
interne MAIS violation du principe d’encapsulation !
2. ListeTabfournit en paramètre du constructeur de IterateurTab ses
données internes (solution choisie).
Comment faire pour garder une solution plus proche du diagramme de
classe : l’itérateur a accès à la structure de données ?
=⇒ On utilise une classe interne !
& %
X. Crégut Programmation objet en Java 282
' $
1 public class ListeTab<T> implements Liste<T> {
2 private T[] elements; // les éléments de la liste
3 private int nb; // la taille de la liste
4
5 public Iterateur<T> iterateur() {
6 return new IterateurTab<T>(this);
7 }
8
9 static private class IterateurTab<T> implements Iterateur<T> {
10 private ListeTab<T> liste; // liste à parcourir
11 private int curseur; // élément à parcourir
12
13 public IterateurTab(ListeTab<T> l) {
14 [Link] = l;
15 [Link] = 0;
16 }
17
18 public boolean encore() {
19 return [Link] < [Link];
20 }
21
22 public T suivant() {
23 return [Link][[Link]++];
24 }
25 }
& %
26 }
& %
24 }
Classe interne
Définition : Une classe interne est une classe définie à l’intérieur d’une
autre classe.
Avantages : La classe interne est dans l’espace de nom de l’autre classe et a
donc accès à toutes ses informations (y compris celles déclarées private).
Classe interne membre : (T. 285) Elle contient un accès sur l’objet qui a
permis sa création (et récupère les paramètre de généricité).
Liste<Double> ll = ...;
Iterateur<Double> it = [Link]();
// it est construit à partir de ll
Classe interne « statique » : (T. 283) Elle n’est associée à aucun objet de la
classe englobante.
& %
X. Crégut Programmation objet en Java 286
' $
Classe anonyme
1 public class ListeTab<T> implements Liste<T> {
2 private T[] elements; // les éléments de la liste
3 private int nb; // la taille de la liste
4
5 public Iterateur<T> iterateur() {
6 return new Iterateur<T>() {
7 private int curseur = 0; // élément à parcourir
8
9 public boolean encore() {
10 return [Link] < nb;
11 }
12
13 public T suivant() {
14 return elements[[Link]++];
15 }
16 }; // Ne pas oublier le point-virgule
17 }
18 }
– Une classe anonyme est une classe qui n’a qu’une seule instance.
– Peut avoir accès aux variables locales du sous-programme à condition
& %
qu’elles soient déclarées final ;
& %
X. Crégut Programmation objet en Java 288
' $
values
Collection Iterator
entrySet
Map Set MultiSet List Bag
keySet
Remarque : Vector est une « ancienne » classe qui a été modifiée pour faire
partie de la hiérarchie Collection. C’est la seule classe « synchronisée ».
& %
X. Crégut Programmation objet en Java 289
' $
L’interface [Link]
L’interface Collection est la racine de l’arbre d’héritage des structures de
données de Java.
Définition : Une collection représente un groupe d’éléments, de type E
(généricité).
Propriétés : Voici quelques propriétés des collections :
– Avant Java 1.5, les éléments d’une collection était Object (T. 206).
– Une collection peut autoriser ou non plusieurs occurrences d’un même
élément.
– Les éléments peuvent être ordonnés ou non.
– Toute classe dérivée de Collection devrait définir un constructeur par
défaut (qui crée une collection vide) et un constructeur qui prend en
paramètre une collection.
– Certaines méthodes peuvent ne pas être implantées dans les sous-classes
& %
qui devraient alors lever UnsupportedOperationException .
L’interface [Link]<E>
C’est la racine de la hirérachie définissant les collections (groupe
d’éléments).
boolean add(E o) // ajouter l’élément (false si doubles interdits)
boolean addAll(Collection<? extends E> c) // ajouter les éléments de c
void clear() // supprimer tous les éléments de la collection
boolean contains(T o) // est-ce que la collection contient o ?
boolean containsAll(Collection<?> c) // ... tous les éléments de c ?
boolean isEmpty() // aucun élément ?
Iterator<E> iterator() // un itérateur sur la collection
boolean remove(Object o) // supprimer l’objet o (collection changée ?)
boolean removeAll(Collection<?> c) // supprimer tous les élts de c
boolean retainAll(Collection<?> c) // conserver les éléments de c
int size() // le nombre d’éléments dans la collection
Object[] toArray() // un tableau contenant les éléments de la collection
& %
X. Crégut Programmation objet en Java 291
' $
La classe [Link]<E>
Objectif : Disposer de tableaux automatiquement redimensionnables.
Remarque : La classe Vector est définie dans le paquetage [Link].
Constructeurs : Sauf contre-indication, ils créent des vecteurs vides.
Vector(int capacitéInitiale, int incrémentCapactité)
Vector(int capacitéInitiale) // Vector(capacitéInitiale, 4)
Vector() // Vector(10);
Vector(Collection<? extends E> c) // vecteur contenant les éléments de c
Méthodes élémentaires : (équivalentes à celles des tableaux)
int size() // le nombre d’éléments du vecteur (taille effective)
int capacity() // capacité du vecteur
& %
set(int index, E o) // vecteur[index] = o et 0 <= index < size()
L’interface [Link]<E>
Objectif : Un itérateur est un objet qui permet de parcourrir tous les
éléments d’une collection.
boolean hasNext() // reste-t-il des éléments dans la collection ?
E next() // l’élément suivant (et avancer)
void remove() // supprimer l’élément (peut ne pas être définie)
Exemple d’utilisation :
Collection<Double> c = ...
Iterator<Double> it = [Link]();
while ([Link]()) {
Double élément = [Link](); // retourne l’élément courant et avance
// manipuler élément...
}
& %
X. Crégut Programmation objet en Java 294
' $
L’interface [Link]<E>
public interface Iterable<E> {
Iterator<E> iterator();
}
Intérêt : Il est possible d’utiliser foreach sur toute classe qui réalise
Iterable.
Iterable<E> collection = ...
for (E o : collection) {
faire(o);
}
& %
X. Crégut Programmation objet en Java 295
' $
Les entrées/sorties
& %
X. Crégut Programmation objet en Java 296
' $
La classe InputStream
But : Lire des octets (bytes) depuis un flux d’entrée.
public abstract class InputStream {
public abstract int read() throws IOException;
// Lire un octet renvoyé sous la forme d’un entier entre 0 et 255.
// Retourne -1 si le flux d’entrée est terminé. Bloquante.
public int read(byte[] buf, int off, int len) throws IOException;
// Lire au plus len octets et les stocker dans buf à partir de off.
// Retourne le nombre d’octets effectivement lus (-1 si fin de flux).
// @throws IndexOutOfBoundsException, NullPointerException...
public int read(byte[] b) throws IOException;
// Idem read(b, 0, [Link])
public long skip(long n) throws IOException
// Sauter (et supprimer) n octets du flux.
// Retourne le nombre d’octets effectivement sautés.
public void close() throws IOException;
// Fermer le flux et libérer les ressources système associées.
public int available() throws IOException;
// nombre d’octets disponibles (sans bloquer)
}
& %
X. Crégut Programmation objet en Java 297
' $
La classe OutputStream
But : Écrire des octets (bytes) dand un flux de sortie.
public abstract class OutputStream {
public void write(byte[] buf, int off, int len) throws IOException;
// Écrire len octets de buf[off] à buf[off+len-1] dans ce flux.
// @throws IndexOutOfBoundsException, NullPointerException...
& %
X. Crégut Programmation objet en Java 298
' $
Les classe Reader et Writer
Principe : Équivalentes à InputStream et OutputStream mais avec des
caractères et non des octets.
Remarque : int pour stocker 16 bis d’un caractères ou -1 pour fin du flux.
public abstract class Reader {
public int read() throws IOException;
public abstract int read(char[] buf, int off, int len) throws IOException;
public int read(char[] b) throws IOException;
public long skip(long n) throws IOException
public abstract void close() throws IOException;
public boolean ready() throws IOException; // prêt à être lu ?
}
public abstract class Writer {
public void write(int b) throws IOException;
public abstract void write(char[] buf, int off, int len) throws IOException;
public void write(char[] b) throws IOException;
public void write(String str) throws IOException;
public void write(String str, int off, int len) throws IOException;
public void flush() throws IOException
public abstract void close() throws IOException;
& %
}
& %
types primitifs.
& %
= new BufferedReader(new InputStreamReader([Link]));
& %
– ...
& %
interface Cloneable { } // Cloneable ne déclare rien !!!
& %
X. Crégut Programmation objet en Java 305
' $
Conclusions
Idées clés
– Techniques de décomposition et d’architecture.
– Architectures logicielles flexibles et décentralisées (ni centre, ni sommet).
– Construction de logiciels par combinaison ascendante (bottom-up)
d’éléments logiciels réutilisables.
– Éléments partiellement affinés décrivant des comportements communs.
– Spécification précise de chaque composant (contrat logiciel).
– Ouverture sur le monde extérieur : routines externes, empaquetage
d’outils existants.
– BIBLIOTHÈQUES
& %
X. Crégut Programmation objet en Java 306
' $
& %
X. Crégut Programmation objet en Java 307
' $
Liste des exercices
Exercice 1 : Équation du second degré . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Exercice 2 : Lister les erreurs de la classe Équation . . . . . . . . . . . . . . . . . . . . 9
Exercice 3 : Évolution de la mémoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Exercice 4 : Comparaison de résoudre en C et Java . . . . . . . . . . . . . . . . . . . 14
Exercice 5 : Caractéristiques des objets fractions . . . . . . . . . . . . . . . . . . . . . 62
Exercice 6 : Obtenir la valeur d’attributs privés . . . . . . . . . . . . . . . . . . . . . . . 70
Exercice 7 : Description UML de la classe Fraction . . . . . . . . . . . . . . . . . . . 70
Exercice 8 : Accéder à un attribut privé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Exercice 9 : Définir une date avec jour, mois, année et quantième . . . . . . 72
Exercice 10 : Supérieur et inférieur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
& %
Exercice 11 : Compteur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
&
Exercice 24 : Généraliser les listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 %
X. Crégut Programmation objet en Java 309
' $
Exercice 25 : Échanger deux élément d’un tableau . . . . . . . . . . . . . . . . . . 165
Exercice 26 : Plus grand élément d’un tableau . . . . . . . . . . . . . . . . . . . . . . 167
Exercice 27 : Définition d’une point nommé . . . . . . . . . . . . . . . . . . . . . . . . 171
Exercice 28 : Déplier la classe PointNommé . . . . . . . . . . . . . . . . . . . . . . . . 180
Exercice 29 : Schémas mathématiques : les classes abstraites . . . . . . . . . 190
Exercice 30 : Méthode retardée vs méthode redéfinie . . . . . . . . . . . . . . . . 195
Exercice 31 : Définir un groupe d’objets géométriques . . . . . . . . . . . . . . . 196
Exercice 32 : Définir un menu textuel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
Exercice 33 : Pourquoi les interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
Exercice 34 : Modélisation d’une équipe de football . . . . . . . . . . . . . . . . . 205
Exercice 35 : Généricité vs Héritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
& %
Exercice 36 : Généricité et sous-typage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
& %
X. Crégut Programmation objet en Java 311