Académique Documents
Professionnel Documents
Culture Documents
Amaury Habrard
Université de Saint-Etienne
amaury.habrard@univ-st-etienne.fr
Mis à jour par François-Xavier Dupé
Université d’Aix-Marseille
francois-xavier.dupe@lif.univ-mrs.fr
2
Table des matières
1 Avant propos 7
3
4 TABLE DES MATIÈRES
5 La gestion d’exceptions 89
5.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
5.2 Gestion de plusieurs exceptions et transmission d’informations . . . . . . . . . . . 90
5.3 Dérivation et redéclenchement d’exceptions . . . . . . . . . . . . . . . . . . . . . . 93
5.4 Le bloc finally . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
5.5 Les exceptions standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.6 Exercices de Cours . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
5.6.1 Vrai ou Faux ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
5.6.2 Mots croisés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Avant propos
Ce document en est à sa troisième version, il existe de manière certaine des coquilles, des fautes
de frappes, des oublis de mot, des erreurs de code involontaire, des copier/coller non pertinents, . . .
Dans le but d’améliorer ce document, merci de signaler toutes ces erreurs. Si vous avez quelques
suggestions pour améliorer certaines parties, corriger certaines affirmations, elles sont les bienve-
nues.
7
8 CHAPITRE 1. AVANT PROPOS
Chapitre 2
2.1 Présentation
Java est une technologie composée d’un langage de programmation orienté objet et d’un en-
vironnement d’exécution. Préalablement nommé Oak, il a été créé par James Gosling et Patrick
Naughton chez Sun Microsystems avec le soutien de Bill Joy.
Le langage Java fut officiellement présenté le 23 mai 1995 au SunWorld.
Java est à la fois un langage de programmation et une plateforme d’exécution. Le langage Java
a la particularité principale d’être portable sur plusieurs systèmes d’exploitation tels que Windows,
MacOS ou Linux. C’est la plateforme qui garantit la portabilité des applications développées en
Java.
Le langage reprend en grande partie la syntaxe du langage C++, très utilisé par les infor-
maticiens. Néanmoins, Java a été épuré des concepts les plus subtils du C++ et à la fois les
plus déroutants, tels que les pointeurs. Les concepteurs ont privilégié l’approche orientée objet de
sorte qu’en Java, tout est objet à l’exception des primitives (nombres entiers, nombres à virgule
flottante, etc.).
Java permet de développer des applications autonomes mais aussi, et surtout, des applications
client-serveur. Côté client, les applets sont à l’origine de la notoriété du langage. C’est surtout côté
serveur que Java s’est imposé dans le milieu de l’entreprise grâce aux servlets, le pendant serveur
des applets, et plus récemment les JSP (Java Server Pages) qui peuvent se substituer à PHP et
ASP.
Les applications Java peuvent être exécutées sur tous les systèmes d’exploitation pour lesquels a
été développée une plateforme Java, dont le nom technique est JRE (Java Runtime Environment -
Environnement d’exécution Java). Cette dernière est constituée d’une JVM (Java Virtual Machine
- Machine Virtuelle Java), le programme qui interprète le code Java et le convertit en code natif.
Mais le JRE est surtout constitué d’une bibliothèque standard à partir de laquelle doivent être
développés tous les programmes en Java. C’est la garantie de portabilité qui a fait la réussite de
Java dans les architectures client-serveur en facilitant la migration entre serveurs, très difficile
pour les gros systèmes.
Dans le cadre de ce cours notre objectif sera d’étudier les concepts fondamentaux de la pro-
grammation objet à l’aide du langage java. Le but est d’acquérir les bases permettant ensuite de
développer des applications plus consistantes.
9
10 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA
est dit formé de byte code et est compact et portable sur n’importe quelle machine : il suffit
qu’elle dispose d’un programme permettant d’interpréter le langage, on parle de machine
virtuelle. Ce projet s’appelait Oak.
— Eté 1992 : première présentation interne des possibilités de Oak. Un appareil appelé ”Star
Seven” permet de visualiser une animation montrant Duke, l’actuelle mascotte de Java.
— 1994 : développement de HotJava, un navigateur internet entièrement écrit en Java capable
d’exécuter des applets écrites en byte code.
— 1995 : lancement officiel de Java 1.0
— 1996 : lancement du JDK 1.0 et des versions 1.01 et 1.02 du langage JAVA (250 classes
dans la bibliothèque).
— 1998 : version 1.1 du JDK (500 classes).
— 1999 : version 1.2 JDK que l’on appelle Java2 (1500 classes).
— 2000 : version de J2SE (Java 2 Standard Edition) 1.3 (1800 classes).
— 2002 : version de J2SE (Java 2 Standard Edition) 1.4 (2700 classes), applications web et
entreprises.
— 2003 : version de J2EE (Java 2 Entreprise Edition) 1.4.
— 2004 : version du J2SE 1.5 également appelé J2SE 5.0 ou Java 5 (≈ 3500 classes).
— 2006 : version du J2SE 1.6 également appelé J2SE 6.0 ou Java 6 (≈ 3800 classes).
— 2011 : version du J2SE 1.7 également appelé J2SE 7.0 ou Java 7 (≈ 3900 classes).
— 2014 : version du J2SE 1.8 également appelé J2SE 8.0 ou Java 8 (≈ 4000 classes).
L’encapsulation de données : il n’est pas possible d’agir directement sur les données d’un objet,
il est nécessaire de passer par des méthodes associées à l’objet. Ces méthodes jouent le rôle d’in-
terface obligatoire. L’appel d’une méthode peut être vu comme l’envoi d’un message à un objet.
Vu de l’extérieur, un objet se caractérise uniquement par ses spécification (données attributs) et
ses méthodes.
La notion de classe :
— elle généralise la notion de type de donnée
— elle permet de décrire un ensemble d’objets ayant une structure de données commune et
disposant de mêmes méthodes.
— Les objets apparaissent comme des variables d’un type de classe donnée, on parle d’instances
de classe.
La notion d’héritage. Elle permet de définir une nouvelle classe à partir d’une autre. On réutilise
cette dernière en bloc et on lui ajoute de nouvelles fonctionnalités. La conception d’une nouvelle
classe, qui hérite (récupère) toutes les propriétés et aptitudes de l’ancienne. Il est ainsi possible
de s’appuyer sur des réalisations antérieures parfaitement au point et les spécifier à volonté. Ceci
facilite donc la réutilisation de code ou de logiciel déjà existant.
}
12 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA
Une classe contient une ou plusieurs méthodes. Par exemple dans la classe Chien, la méthode
aboyer() va contenir des instructions spécifiant la façon dont un chien aboie. Les méthodes sont
obligatoirement déclarées dans une classe.
public class Chien{
void aboyer()
{
// code de la methode aboyer où l’on indique comment
// la méthode doit ^
etre exécutée
}
Exemple.
public class Addition2Entiers{
public static void main(String [] args)
{
int entier1, entier2, resultat;
entier1=Integer.parseInt(args[0]);
entier2=Integer.parseInt(args[1]);
somme=Float.parseFloat(args[0]);
resultat = somme / taux;
Pour ceux qui souhaitent essayer leurs programmes sous windows, la procédure d’installation est
la même. Il peut cependant être nécessaire de créer une variable d’environnement supplémentaire.
Voici la procédure à suivre.
— Vérifier que la variable d’environnement JAVA HOME est spécifiée.
— Si ce n’est pas le cas allez dans Menu Démarrer → Settings → Control Panel → System
→ Advanced, menu variables d’environnement. Entrez le nom de JAVA HOME puis sa valeur
(par exemple C:2sdk1.8.0 10/).
— Pour vérifier tapez echo %JAVA HOME dans une console MS-DOS.
Vous pouvez utiliser la même méthode pour modifier le contenu de la variable PATH ou confi-
gurer CLASSPATH. En guise de terminal vous utiliserez alors une console MS-DOS.
2.6. TYPES, EXPRESSIONS ET STRUCTURES DE CONTRÔLE FONDAMENTALES 15
La notion de type
La mémoire centrale est un ensemble de ” positions binaires ” appelées. Ces bits sont regroupés
en octets, un octet correspondant à 8 bits. Les ordinateurs (actuels) ne savent représenter et traiter
que des informations représentées sous forme binaire. Il est donc nécessaire de savoir comment une
information a été codée pour pouvoir attribuer une signification à une suite de bits d’un emplace-
ment de la mémoire.
Il sert à représenter les nombres entiers relatifs. Il existe quatre sortes d’entier permettant de
représenter des valeurs différentes. Le tableau suivant décrit chacun de ces types avec le nom du
type, la taille occupée en mémoire en octets, la valeur minimale et la valeur maximale possible
avec la constante correspondante dans le langage.
Les constantes peuvent s’écrire sous forme décimale (548, -78, +5), ou sous forme hexadécimale
en précisant la valeur de 0x ou 0x (0x1A 0X12), ou octale en faisant précéder la valeur de 0 (032,
05).
Une constante entière est forcément de l’un des types int ou long. On peut forcer une constante
à être du type long en faisant suivre sa valeur de la lettre l ou L, exemple 25L. Le compilateur
rejettera toute constante ayant une valeur supérieure la capacité du type long.
16 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA
Il en existe deux sortes. Le tableau suivant décrit chacun d’eux avec leur nom, la taille occupée
en mémoire en octets, la précision correspondant au nombre de chiffres significatifs pris en compte
dans la partie décimale, la valeur minimale et la valeur maximale possible avec la constante
correspondante dans le langage.
Les caractères, désignés par le type char, sont codés sur 2 octets, notation d’une constante
caractère : ’a’, ’B’, ’é’, ’+’. Exemple
char lettre=’a’;
Certains caractères avec notation spéciale :
— ’\b’ pour le retour arrière (backspace)
— ’\t’ pour une tabulation
— ’\n’ pour un saut de ligne
— ’\f’ pour un saut de page
— ’\r’ pour un retour chariot
— ’\"’ pour un guillemet
— ’\’’ pour une apostrophe
— ’\\’ pour \.
Il sert à représenter une valeur logique de type vrai ou faux, il existe deux valeurs possibles pour
un booléen en java : true et false (pour vrai et faux). Exemple boolean est ordonne=false;.
Attention, en JAVA, les valeurs logiques ne sont pas représentées par des valeurs mais par un
type spécifique : le type boolean, toute utilisation de valeur numérique dans des tests provoquera
une erreur !
Initialisations et constantes
Constantes : le mot clé final Pour indiquer qu’une variable ne peut pas être modifiée pendant
l’exécution d’un programme, on peut utiliser le mot clé final :
final int nombre de mois=12;
Toute modification de la variable nombre de mois, de valeur initiale 12, sera rejetée par le compi-
lateur.
float
*
float + float
float
* float * float
int float
float +
float
Opérateurs de comparaison
Ils servent à faire des comparaisons relatives et sont principalement utilisés dans les tests
conditionnels.
— < : inférieur strictement à
— <= : inférieur ou égal à
— > : supérieur à
— >= : supérieur ou égal à
— == : égal à
— ! = : différent de.
Ces opérateurs peuvent s’utiliser avec des expressions : 2 ∗ a > b + 5, x + y < (a + 2) ∗ 5, . . .
Note : ordre sur les caractères 0 00 <0 10 < . . . <0 90 <0 A0 <0 B 0 < . . . <0 Z 0 <0 a0 < . . . <0 z 0 .
Opérateurs logiques
Ces opérateurs permettent de manipuler des valeurs logiques.
— ! : négation
— & : ”ET”
— ∧ : ”OU” exclusif
— | : ”OU” inclusif
— && : ”ET” avec court-circuit
— || : ”OU” inclusif avec court-circuit
Exemples :
— (a < b) && (c < d), (a < b) & (c < d) : ces deux expressions prennent la valeur true (vrai)
si les deux expressions a < b et c < d sont toutes les deux vraies, la valeur false (faux) dans
le cas contraire.
— (a < b) || (c < d), (a < b) | (c < d) : prend la valeur true si l’une au moins des deux
conditions a < b ou c < d est vraie, la valeur false dans le cas contraire.
— (a < b) ∧ (c < d) prend la valeur true si une et une seule des deux conditions a < b et
c < d est vraie, la valeur false dans le cas contraire.
— !(a < b) prend la valeur true si la condition a < b est fausse, la valeur false dans le cas
contraire. Cette expression possède en fait la même valeur que a >= b.
— Les deux opérateurs && et || possèdent une propriété intéressante : leur second opérande
(celui qui figure à droite de l’opérateur) n’est évalué que si la connaissance de sa valeur
est indispensable pour décider si l’expression est vraie ou fausse. Par exemple, si on a
l’expression (a < b)&&(c < d), on commence par évaluer (a < b), si le résultat est faux
on n’évalue pas c < d puisque le résultat est déjà connu. Les opérateurs ∧ et | évaluent
toujours les deux opérandes, il est donc plutôt conseillé d’utiliser les opérateurs && et ||.
Opérateurs d’affectation
— = : exemples c=b+3; c=i;. L’opérateur possède une associativité de gauche à droite, ainsi
dans l’expression i = j = 5; on évalue j=5 d’abord, puis i=j, i et j ont à la fin la même
valeur 5.
Attention aux problèmes de conversion, supposons que nous ayons une variable de type
int n et une variable de type float x, l’expression n=x+5; est rejetée par le compilateur.
Il faut en fait écrire n=(int) x + 5;. Il est nécessaire de faire une conversion explicite.
Les conversions pour lesquelles il n’y a pas besoin de faire une conversion explicite sont les
suivantes :
— byte → short → int → long → float → double
Il existe cependant un différence suivant où est placé l’opérateur lors de l’évaluation d’une
expression, s’il est placé avant(on parle d’opérateur préfixé) la variable l’opération est ef-
fectuée avant l’évaluation de l’expression, s’il est placé après (on parle d’opérateur postfixé)
l’opération est effectuée après.
— n= ++i -5; : on affecte d’abord la valeur 6 à i puis la valeur 1 à n.
— n= i++ - 5; : on affecte d’abord la valeur 0 à n puis la valeur 6 à i.
— Affectation élargie, les instructions suivantes sont équivalentes :
— i = i + k; et i+=k;
Il existe la même chose avec les opérateurs *, / et -.
Conversion de types
Lorsque l’on désire convertir un type vers autre qui a une taille de représentation inférieure,
les règles suivantes s’appliquent :
— entier vers entier (long vers int, short vert byte, . . .) les octets les moins significatifs sont
conservés.
— double vers float : arrondi au plus proche.
— flottant vers entier : il y a d’abord un arrondi au plus proche dans le type long ou int, puis
on effectue la conversion en conservant les octets les moins significatifs.
Opérateur conditionnel
condition ? etape1 : etape2; : si condition est vraie alors etape1 sinon etape2. Exemple :
max = a<b ? b :a;
Si a < b, alors la variable max reçoit la valeur de la variable b, sinon elle reçoit la valeur de la
variable a.
if
L’instruction if (si) est une instruction de choix.
if(condition)
[{]
instruction\_1
[instruction\_2
...
instruction\_n
}]
[else [{]
instruction\_1
[instruction\_2
...
instruction\_n
}]
Exemple :
public classTva {
public static void main(String [] args)
{
double taux_tva=21.6;
double ht, ttc, net, taux_remise, remise;
ht=200.5;
ttc=ht * (1.0 + taux_tva/100.0);
if(ttc < 1000.0)
taux_remise=0.;
else if(ttc < 2000)
taux_remise=1.;
else if(ttc < 5000){
taux_remise=2.;
System.out.println("Message: Prix ttc entre 2000 et 5000");
}else{
taux_remise=5.;
System.out.println("Message: Prix ttc superieur a 5000");
}
switch
L’instruction switch (branchement) est une instruction de choix, permettant de tester plusieurs
valeurs d’une expression. L’expression peut être de type byte, short, char ou int.
Syntaxe :
2.6. TYPES, EXPRESSIONS ET STRUCTURES DE CONTRÔLE FONDAMENTALES 21
switch(expression)
{
case constante_1: [suite d’instructions1]
case constante_2: [suite d’instructions2]
n=Integer.parseInt(args[0]);
switch(n)
{
case 0: System.out.println("zero");
break;
case 1: System.out.println("un");
break;
case 3: System.out.println("trois");
default: System.out.println("Rien");
System.out.println("Fin");
}
System.out.println("Fin du programme");
}
}
while
Il s’agit d’une boucle tant que qui exécute un ensemble d’instructions tant qu’une condition
est vraie.
Syntaxe :
while(condition)
[{]
instruction_1
[instruction_2
...
instruction_n
}]
Exemple
public class Boucle{
public static void main(String [] args)
{
int x=1;
System.out.println("Avant la boucle");
22 CHAPITRE 2. INTRODUCTION : LE LANGAGE JAVA
while(x<4)
{
System.out.println("Dans la boucle, la valeur de x est "+x);
x=x+1;
}
System.out.println("Après la boucle");
}
}
do-while
Il s’agit d’une boucle faire-tant que similaire à la boucle while sauf que la condition est évaluée
après chaque parcours de boucle. La boucle do-while est exécutée au moins une fois, alors que la
boucle while peut ne jamais être exécutée.
Syntaxe :
do [{]
instruction_1
[instruction_2
...
instruction_n }] while(condition);
N.B. Il y a un point virgule à la fin de l’instruction !
Exemple :
public class Boucle2{
public static void main (String [] args){
int x=1;
System.out.println("Avant la boucle");
do{
System.out.println("Dans la boule, la valeur de x est "+x);
x=x+1;
}while(x<4);
System.out.println("Après la boucle");
}
}
for
L’instruction for est une boucle (pour) dont la syntaxe est divisée en trois expressions.
Syntaxe :
for([initialisation] ; [condition] ; [incrémentation])
[{]
instruction_1
[instruction_2
...
instruction_n
}]
— initialisation est une déclaration ou une suite d’expressions quelconques séparées par
des virgules, cette partie est évaluée une seule fois avant d’entrer dans la boucle.
— condition est une expression booléenne (logique) quelconque, cette partie conditionne la
poursuite de la boucle et est évaluée avant chaque parcours.
— incrémentation est une suite d’expressions quelconques séparées par des virgules, cette
partie est évaluée à la fin de chaque parcours.
2.6. TYPES, EXPRESSIONS ET STRUCTURES DE CONTRÔLE FONDAMENTALES 23
Exemple classique :
i=1;
while(i<=5)
{
System.out.println("bonjour");
System.out.println(i+"fois");
i++;
}
}
}
Autre exemple :
}
}
}
if(i==3) break;
System.out.println("fin tour"+i);
}
System.out.println("apres ma boucle")
}
}
Le résultat du programme précédent est le suivant :
debut tour 1
bonjour
fin tour 1
debut tour
bonjour
fin tour 2
debut tour 3
bonjour
apres la boucle
En cas de boucles imbriquées, l’instruction break fait sortir de la boucle la plus interne.
L’instruction continue permet de passer directement au tour de boucle suivant. Exemple.
public class ExContinue
{
public static void main (String args[])
{
int i;
for(i=1; i<=5; i++)
{
System.out.println("debut tour"+i);
if (i<4) continue;
System.out.println("fin tour"+i);
}
System.out.println("apres la boucle");
2.7. UNE PREMIÈRE INTRODUCTION AU TYPE STRING 25
}
}
Exemple d’exécution :
debut tour 1
debut tour 2
debut tour 3
debut tour 4
fin tour 4
debut tour 5
fin tour 5
apres la boucle
Déclaration
Une chaı̂ne de caractère constante se déclare toujours entre guillemets "... ".
int l = chaineSalutation.length();
Accès à un caractère
Concaténation : l’opérateur +
System.out.println(chaineSalutation);
System.out.println(ch1+ch2);
System.out.println(ch);
Comparaison de chaı̂nes La méthode equals qui teste l’égalité de deux chaı̂nes de caractères :
ch1.equals(ch2) ou ch1.equals("Bonjour").
La méthode compareTo pour comparer deux chaı̂nes de caractères dans l’ordre lexicographique
(alphabétique) : ch1.compareTo(ch2)
— renvoie un entier strictement négatif si ch1 est située avant ch2 dans l’ordre lexicographique
— renvoie un entier strictement positif si ch1 est située après ch2 dans l’ordre lexicographique
— 0 si ch1 contient la même chaı̂ne que ch2.
2.8.2 Commentaires
Il existe trois types de commentaires en JAVA.
— les commentaires commençant par /∗ et se terminant par ∗/, exemple : /* Ceci est un
commentaire usuel*/
— les commentaires de fin de ligne //, exemple :
int valeur; // valeur avec commentaire de fin de ligne
— les commentaires de documentation commençant par / ∗ ∗ et finissant par ∗/. C’est un cas
particulier des commentaires usuels puisqu’ils commencent de manière légèrement différente
par rapport à ces derniers. Leur intérêt est de pouvoir être extrait automatiquement pour
faire de la documentation avec, par exemple, le programme javadoc.
projets java, la documentation technique peut idéalement être complétée par une java-
doc (important pour continuer le développement plus tard).
— une architecture correcte : idéalement à la racine on a le README et que des sous-
répertoires contenant la documentation, le code exécutable, le code source, les drivers,
plus d’autres choses tout le temps dans un sous-répertoire spécifique et documenté dans
le README. Les fichiers sources sont séparés des exécutables, soit dans 2 répertoires
différents, soit - idéalement - dans un répertoire contenance uniquement les sources et les
exécutables sont stockés dans une archive .jar On mettra dans des répertoires spécifiques
des fichiers de log, de sauvegarde, les images, ...
— Un code de bonne qualité. Le code doit respecter les conventions de nommage java, avoir
des noms de variables explicites, être indenté correctement, et bien commenté. On peut
distinguer des commentaires généraux décrivant le contenu d’une classe et son objectif,
et des commentaires décrivant une méthode et ce qu’elle fait (on les retrouve dans la
javadoc). Ensuite des commentaires internes qui se situent à l’intérieur des méthodes pour
expliquer des des attributs d’une classe, des variables de méthode , des algorithmes, des
choses techniques .... Le code importé d’ailleurs doit être signalé dans la documention
technique par exemple et la provenance doit être indiquée. On peut même signaler pourquoi
on a eu besoin de ce code.
— L’archive DOIT être nettoyée et ne pas posséder de fichiers inutiles pour l’utilisateur
(fichiers de log, de sauvegardes, de tests, inutilisés, ...)
Ces remarques sont d’ordre générales mais il est pertinent d’essayer de les respecter. Le nom
de l’archive doit correspondre au nom du projet, lorsque c’est un travail à rendre à un enseignant
il est de bon ton d’ajouter le nom de la personne envoyant le projet. En général, lorsque que l’on
décompresse l’archive, celle-ci doit créer un dossier contenant tous les éléments.
Lorsque que vous envoyez une archive par courrier électronique, il est parfois conseillé d’indi-
quer dans le message le nom de l’archive envoyée (sa taille notamment si elle est volumineuse).
Il est également judicieux de commencer votre message par une formule de salutation (du genre
bonjour) et de terminer par une formule de politesse (Bonne réception, cordialement, ...) et veillez
à ne pas oublier de mettre votre nom à la fin du message ! Pensez également à mettre un sujet
pertinent au message et à vous mettre en copie (cc) notamment lorsque ce sont des projets notés
à rendre.
Je vous conseille aussi pour votre activité professionnelle, dans la mesure du possible, d’utiliser
une adresse électronique qui ne soit pas trop fantaisiste.
2.8.7 Bibliographie
Voici quelques références bibliographiques, ce cours est inspiré de certaines d’entre elles, ce
sont des choix non exhaustifs et subjectifs.
— Programmer en Java. Claude Delannoy. Editions Eyrolles.
— Java Tête la première. Kathy Sierra et Bert Bates. Editions O’Reilly.
— Java in a nutshell - manuel de référence. David Flanagan. Editions O’Reilly.
— The Java Programming Language Second Edition. Ken Arnold, James Gosling. Addison
Wesley, 1998.
— Java par la Pratique. Patrick Niemeyer, Joshua Peck. O’Reilly.
1. Ce fichier compile et s’exécute, mais il faut ajouter une ligne au programme dans la boucle
while sinon il boucle sans fin. On peut, par exemple, ajouter x = x + 1; juste après le
while et avant le if.
2. Ce fichier ne compile pas, il manque une déclaration de classe pour mettre la méthode main
dedans, par exemple public class Test{ et il faudra rajouter une accolade } à la fin.
3. Ce fichier ne compile pas, une classe doit avoir au moins une méthode (par forcément une
méthode main).
4 5
6 7
8 9
10
11 12 13
14
15 16
17 18
19
HORIZONTALEMENT VERTICALEMENT
4 Commande pour executer un programme 1 Patrons de classes (-)
java (-) 2 Pas entier (-)
8 Entier (-) 3 Tout le monde peut y accéder (-)
9 Préfixe (-) 5 Tant que (-)
10 Acronyme d’un fabricant de puces (-) 6 Transformer en bytecode (-)
11 Pour afficher quelque chose (-) 7 Inconstance (-)
15 A beaucoup de caractères (-) 9 Vaut zéro ou un (-)
16 Annoncer une nouvelle classe ou une 12 Modificateur de main (-)
méthode (-)
13 Ma méthode principale (-)
17 Elément de programme (-)
14 Définit un comportement (-)
19 Elle a une ligne (-)
18 Machine fonctionnant au bytecode (-)
Chapitre 3
3 formes doivent s’afficher sur une interface graphique : un cercle, un carré et un triangle.
Lorsque l’utilisateur cliquera sur un bouton, la forme pivotera de 360 degrés dans le sens des
aiguilles d’une montre (tour complet sur elle-même), et le système jouera un son d’un fichier
WAV (format de son) spécifique à cette forme.
2 prototypes sont proposés pour réaliser le programme : une en programmation impérative et une
en programmation objets avec une classe pour chaque forme.
Programmation impérative
Programmation Objets
tourner(typeForme){ Carre Cercle Triangle
//faire tourner la forme tourner(){ tourner(){ tourner(){
//de 360 degrés //code pour //code pour //code pour
} //faire tourner //faire tourner //faire tourner
//un carré //un cercle //un triangle
jouerSon(typeForme){ } } }
//utiliser typeForme
//pour chercher jouerSon(){ jouerSon(){ jouerSon(){
//quel son WAV jouer //code pour //code pour //code pour
//puis le jouer //jouer le son //jouer le son //jouer le son
} //d’un carré //d’un cercle //d’un triangle
} } }
En programmation objet, on va créer ”un objet” par type de forme demandée, et on associera à
chaque forme ses fonctionnalités propres (tourner, jouer un son). En programmation impérative,
on utilisera des procédures génériques et les choix des opérations à effectuer se feront à l’aide de
tests (par exemples des if). Les 2 solutions répondent au problème, mais la modélisation objet
est plus rigoureuse et offre de meilleures garanties en cas de modifications futures.
Justement, supposons que les spécifications soint ensuite modifiées par le commanditaire du projet :
31
32 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION
Il y aura en plus une forme d’amibe à l’écran, au milieu des autres. Lorsque l’utilisateur cliquera
sur l’amibe elle tournera comme les autres formes, mais jouera un son .AIF
Programmation impérative
tourner(typeForme){
//faire tourner la forme Programmation Objets
//de 360 degrés
} Amibe
tourner(){
//code pour
jouerSon(typeForme){
//faire tourner
//Si la forme n’est pas
//l’amibe
//une amibe
}
//utiliser typeForme
//pour chercher
jouerSon(){
//quel son WAV jouer
//code pour
//puis le jouer
//jouer le son
//sinon
//de l’amibe
//jouer le son AIF
}
//de l’amibe
}
En programmation impérative on va être obligé de modifier le code, ce qui peut provoquer l’ajout
d’erreurs (bugs). En programmation objet, on va rajouter un nouvel objet de forme amibe et lui
associer ses comportements propres. On n’a pas à modifier le code existant déjà validé.
(a) Une amibe. (b) Les centres de rotation ne sont pas les mêmes.
Programmation impérative
tourner(typeForme, centreX, centreY){
//si la forme n’est pas
//une amibe
//calculer le centre
//sur la base d’un
//rectangle puis Programmation Objets
//faire tourner la
//forme de 360 degrés Amibe
//sinon int centreX;
//utiliser centreX et int centreY;
tourner(){
//centreY comme centre
//faire tourner
//de rotation puis faire
//l’amibe en utilisant
//tourner la forme
//centreX et centreY
}
}
jouerSon(typeForme){
jouerSon(){
//Si la forme n’est pas
//code pour
//une amibe
//jouer le son
//utiliser typeForme
//de l’amibe
//pour chercher
}
//quel son WAV jouer
//puis le jouer
//sinon
//jouer le son AIF
//de l’amibe
}
En programmation objets on n’a pas besoin de modifier le code des autres formes (carré, triangle,
cercle). On peut même aller plus loin et créer une classe contenant les parties communes, puis des
sous-classes contenant les parties spécifiques. Si la classe Forme contient une fonctionnalité donnée
toutes ses sous-classes en héritent automatiquement. La classe Triangle, par exemple, suit les
directives de la classe forme, pour la classe Amibe on a modifié les parties de code nécessaires.
Forme Super−classe
tourner(){.
//code
}
jouerSon(){
//code
}
Héritage
tourner(){
//code de
//l’amibe
}
jouerSon(){
//code de
// l’amibe
}
Analogie : un objet peut être comparé à des fiches d’un carnet d’adresses, la classe correspond
à la structure d’une fiche (nom, téléphone, adresse, . . .), les objets correspondent aux personnes
stockées dans les fiches.
De part la définition de cette classe Point, nous pouvons noter deux choses :
— 2 variables d’instances sont définies x et y. Le mot private indique que l’on ne peut avoir
accès à ces variables qu’à l’intérieur de la classe. (On appelle parfois ces variables champs
ou attributs)
— 3 méthodes sont définies initialise, deplace et affiche. Le mot public indique que ces
méthodes sont utilisables depuis un programme quelconque. Ces 3 méthodes ne possèdent
pas de valeur de retour, dans ce cas on utilise le mot clé void dans la déclaration. Notons,
de plus, que la méthode initialise – par exemple – possède 2 arguments abs et ord, tous
deux de type entier. (Note : il est possible d’avoir des méthodes private dans une classe,
dans ce cas elles ne pourront être utilisées que dans la classe)
Point a;
ceci implique la création d’une référence de type Point nommée a, à ce stade aucun objet n’a été
créé.
Pour créer un objet, il faut utiliser l’opérateur new :
Point a = new Point();
? x
a ? y
Un objet point a été créé, mais x et y n’ont pas reçu de valeur, par défaut ces variables sont initia-
lisées à zéro. Ensuite on peut initialiser les valeurs de l’objet en utilisant la méthode initialise :
a.initialise(3,5);
3 x
a 5 y
a = new Point();
b = new Point();
a.initialise(3,5);
a.affiche();
a.deplace(2,0);
a.affiche();
b.initialise(6,8);
b.affiche();
}
}
Remarques : Pour utiliser ce programme, il faut créer 2 classes, on enregistre la classe Point
dans un fichier Point.java et la classe TestPoint dans un fichier TestPoint.java.
En règle générale, on sépare la fonction d’utilisation (c’est-à-dire le main) dans une classe à
part de manière à faciliter la réutilisation des classes déjà construites.
Pour utiliser notre programme il faut :
1. compiler le fichier Point.java ;
2. compiler le fichier TestPoint.java, il ne sera possible d’utiliser le programme que si le
fichier Point.class existe ;
3. lancer le programme TestPoint.
36 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION
Exemple
Point a;
Point b = new Point(6,8);
a = new Point(3,5);
a.affiche();
a.deplace(2,0);
a.affiche();
b.affiche();
3.3. LA NOTION DE CONSTRUCTEUR 37
Attention : dans notre nouvelle classe, l’utilisation de a = new Point(); n’est plus possible
et provoque une erreur. Si l’on désire conserver un constructeur sans arguments, il faut écrire 2
constructeurs.
class A
{
private np; //initialisation de type 1
private int n=20, p=10; //initialisation de type 2
public A()
{
np = np * p; //ou np=n*n: initialisation de type 3
n = 5;
}
n=5;
}
3 x
a 5 y
Point a,b; 2 x
b 0 y
a = new Point(3,5);
b = new Point(2,0);
3 x
a 5 y
2 x
b 0 y
a = b;
Second exemple :
Point a,b,c;
a = new Point(1,10);
b = new Point(2,20);
c = a;
a = b;
b = c;
Il y a 2 objets et 3 variables de type Point. Le schéma suivant représente le résultat final du code
ci-dessous.
1 x
a 10 y
b
2 x
c 20 y
null
if(p!=null)
{
p.affiche();
}
3.5 Le ramasse-miettes
Nous avons vu que grâce à l’opérateur new, la JVM alloue un emplacement mémoire à l’objet et
l’initialise. Il n’existe pas de mécanisme de destruction d’objets. Il existe par contre un mécanisme
de gestion automatique de la mémoire connu sous le nom de ramasse-miettes (en anglais garbage
collector). Son principe est le suivant :
— A tout instant, la JVM connaı̂t le nombre de références à un objet donné (tout est géré par
références en Java).
— Lorsqu’il n’existe plus aucune référence sur un objet, on est certain que le programme ne
pourra plus y accéder. Il est donc possible de libérer l’emplacement correspondant. Cepen-
dant, pour des raisons d’efficacité ( ? ?), Java n’impose pas que ce soit fait immédiatement.
On dit que l’objet est candidat au ramasse-miettes.
Note : La zone mémoire utilisée pour allouer de l’espace à de nouveaux objets s’appelle le tas.
Tous les objets résident sur le tas. Avant qu’un objet ne soit candidat au ramasse-miettes, Java
appelle la méthode finalize, qui est une méthode permettant d’effectuer des opérations lors de
la destruction de l’objet.
En général, une bonne programmation objet protège les données relatives à un objet : les
variables définies dans une classe. Pour cela, on déclare ces champs private, et on utilise des
méthodes déclarées public pour y accéder.
Par exemple, pour la classe Point, on rajoutera les méthodes suivantes
y = ord;
}
Suivant les cas, il peut-être préférable d’avoir une seule méthode de changement de variables
lorsque que la modification d’une variable peut en affecter une autre.
Autre exemple :
public BonChien()
{
taille = 20;
}
un.setTaille(70);
deux.setTaille(8);
System.out.println("Taille de un : "+un.getTaille());
un.aboyer();
System.out.println("Taille de deux : "+deux.getTaille());
deux.aboyer();
}
}
3.7. CHAMPS ET MÉTHODES STATIQUES 41
class A
{
public int n;
public float y;
public A()
{
n = 3;
y = 5.5;
}
}
3 a1.n
a1 5.5 a1.y
class B
{
public static int n = 2;
public float y;
public B()
{
y = 5.5;
}
}
b1.n et b2.n désignent le même champ, il est cependant préférable de s’y référer par B.n. n
est une variable statique de la classe B.
Exemple :
public class ExObjet
{
private static long nb = 0;
public ExObjet()
{
System.out.print("++ creation de l’objet ExObjet numero "+nb+" ");
nb++;
System.out.println("il y a maintenant "+nb+" objet(s)");
}
}
Remarque lorsque l’on compte le nombre d’objets, on ne prend pas en compte les éventuels
objets détruits par le ramasse-miettes (il faudrait utiliser la méthode statique finalize).
Les champs statiques peuvent être initialisés avant l’appel à un constructeur. Il peut même
ne pas y avoir de constructeur, dans ce cas la seule initialisation possible est une initialisation
explicite lors de la déclaration.
Exemple
public class B2
{
private float x;
private static int n;
Exemple
class ExObjet2
{
private static long nb = 0;
public ExObjet2()
{
System.out.print("++ creation de l’objet ExObjet2 numero "+nb+" ");
nb++;
System.out.println("il y a maintenant "+nb+" objet"+pluriel());
}
ExObjet2.pluriel() + ":"+ExObjet2.getNb());
c = new ExObjet2();
System.out.println("Après la creation du dernier objet c, NB objet" +
ExObjet2.pluriel() + ":"+ExObjet2.getNb());
}
}
Résultat du programme
{
x += dz;
}
Remarques importantes :
— Le type de la valeur de retour n’intervient pas dans le choix de la méthode surchargée.
— On peut surcharger des méthodes statiques.
— L’attribut final n’a aucune influence sur le choix d’une méthode surchargée.
public void deplace (int dx) { ... }
public void deplace (final int dx){ ... } //erreur de compilation !!!
public Point()
{
x = 0;
y = 0;
}
public Point(int a)
{
x = y = a;
}
x = abs;
y = ord;
}
public Point(Point a)
{
x = a.getX();
y = a.getY();
}
return p;
}
}
Utilisation
Point a = new Point(1,2);
Point b = a.copie();
Cette démarche est utilisable tant que la classe concernée ne comporte pas des variables qui
sont de type objet (par exemple lorsque dans une classe Point on a un champ qui est aussi une
variable de type Point). Il faut alors décider si la copie soit porter sur les objets référencés ou
juste sur les références.
3.10. AUTORÉFÉRENCEMENT : LE MOT CLÉ THIS 47
— Copie superficielle d’un objet : on copie la valeur de tous les champs, y compris ceux qui
sont d’un type classe.
— Copie profonde : on recopie la valeur des champs d’un type primitif, mais pour les champs
de type classe on crée une nouvelle référence à un nouvel objet de même type et de même
valeur.
a.setX(x);
a.setY(y);
x = c.getX();
y = c.getY();
}
}
3 x
a 5 y
Point a = new Point(3,5);
Point b = new Point(0,2);
2 x
b 0 y
2 x 2 x
a 0 y 0 y
a.permute(b) 3 x
b 5 y
Il est également possible de s’en servir pour appeler un autre constructeur au sein de la même
classe.
public class Point
{
private int x,y;
public Point()
{
this(0,0); //appel du constructeur precedent Point(0,0)
System.out.println("Constructeur sans argument");
}
}
ATTENTION : l’appel de this(0,0) doit obligatoirement être la première instruction
du constructeur.
Les instructions :
3.11. REMARQUES SUR LA DÉFINITION DE MÉTHODES 49
Si une méthode fournit un résultat, elle porte alors le nom de fonction. Le type de sa valeur
de retour doit être indiqué dans son en-tête, l’instruction return doit également être utilisée pour
retourner le résultat.
Exemple : une méthode distance qui calcule la distance du point par rapport à l’origine (0, 0)
que l’on pourrait ajouter à la classe Point.
r = a.distance();
System.out.println("La distance par rapport à l’origine est de "+r);
Il est également possible de ne pas utiliser la valeur de retour, dans ce cas on appelle juste la
méthode :
a.distance();
On peut théoriquement changer la valeur des arguments, mais c’est assez déconseillé.
void g()
{
double x; //variable locale à g, indépendante de la variable de f
...
}
L’emplacement mémoire d’une variable locale est alloué au moment ou l’on entre dans la
méthode, il est libéré lorsque l’on en sort.
Il est possible de créer des variables locales à un bloc (par exemple dans une boucle for) qui ne
seront valides que dans ce bloc.
3.11.4 La récursivité
La récursivité de méthodes peut prendre 2 formes :
— directe : une méthode comporte dans sa définition au moins un appel à elle-même,
— croisée : l’appel d’une méthode entraı̂ne l’appel d’une autre méthode qui a son tour appelle
la méthode initiale.
La récursivité peut s’appliquer aussi bien aux méthodes statiques qu’aux autres méthodes.
Exemple : Calcul de la factorielle d’un nombre par une méthode statique (pour n = 8 le résultat
est 40320).
}
}
Note : A chaque appel de fact, il y a une allocation mémoire pour les variables locales, la valeur
de retour et l’argument. Chaque nouvel appel de fact entraı̂ne donc une telle allocation sans que
les emplacements précédents n’aient été libérés (empilement d’espace mémoire). Ce n’est qu’à la
première instruction return qu’il y a un dépilement des arguments.
MesClasses
Utilitaires.Mathematiques
Utilitaires.Tris
L’utilisation du point . implique une hiérarchie logique de package. Ainsi, dans l’exemple
précédent, les packages Mathématiques et Tris sont en dessous du package Utilitaires.
52 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION
Utilitaires
Mathematiques Tris
L’attribution à un package se fait au niveau du fichier source. Toutes les classes d’un même
fichier appartiendront toujours à un même package. On place en début de fichier une instruction
de la forme :
package nompaquetage;
La plupart des environnements imposent des contraintes quant à la localisation des fichiers
correspondant à un package. En particulier un paquetage de nom X.Y.Z se trouvera toujours
dans un sous-répertoire de nom X.Y.Z (les répertoires de niveau hiérarchique supérieur peuvent
être quelconques). Deux paquetages X.Y.Z et X.Y.U n’ont pas l’obligation d’être dans le même
répertoire.
Avec le JDK (ou SDK) de Sun la recherche d’un package se fait dans les répertoires indiqués
dans la variable d’environnement CLASSPATH.
S’il n’y a pas de paquetage indiqué dans un fichier source, le compilateur considère que ceux-ci
appartiennent au paquetage par défaut.
Si l’on ne souhaite pas utiliser la variable d’environnement CLASSPATH, il est possible de spécifier
les chemins où se trouvent les paquetages à l’aide de l’option -classpath des commandes java et
javac. Par exemple si une classe Toto utilise des packages ou des classes situées dans les répertoires
~/MesPackages et /usr/local/packages standard, on utilisera les commandes suivantes :
Ceci implique si le package affaires de toto est utilisé, le dossier contenant les éléments de
ce package doit être situé soit dans ~/MesPackages/affaires_de_toto ou dans
/usr/local/packages standard/affaires de toto ; évidemment une seule des 2 solutions est
valable.
Avec une citation de nom de classe. Si vous avez attribué la classe Point au package
MesClasses, vous pouvez l’utiliser en la nommant MesClasses.Point, exemple :
Il est ensuite possible d’utiliser les classes Point et Cercle sans avoir à mentionner leurs noms.
3.13. LES TABLEAUX 53
import MesClasses.*;
Toutes les classes du package MesClasses et les classes des packages hiérarchiquement en dessous
sont alors directement utilisables. Notez que si vous tapez uniquement import MesClasses; les
classes du package MesClasses seront directement utilisables, mais pas les packages contenus dans
MesClasses.
Il existe plusieurs paquetages standard fournis avec Java, par exemple le package Math ou
encore le package java.lang qui est automatiquement ajouté par le compilateur.
int t[];
int []t;
La différence entre les 2 déclarations a une importance lorsque l’on souhaite déclarer plusieurs
tableaux :
Pour créer un tableau on utilise l’opérateur new comme pour des objets classiques. Voici un
exemple montrant comment créer un tableau de 10 entiers :
Il est également possible de fournir une liste d’expression entre accolades. Voici un exemple de
création d’un tableau d’entiers à 5 éléments.
Attention. Une fois un tableau créé, on ne peut plus modifier sa taille. Par contre, on peut créer
un nouveau tableau et - si besoin - recopier les valeurs du tableau initial. La taille d’un tableau
est toujours positive.
La taille d’un tableau Il est possible d’avoir accès à la taille d’un tableau à l’aide du champ
length.
Accès aux éléments d’un tableau Un élément est désigné en plaçant entre crochets sa position
après le nom du tableau. Une position est désignée par une expression entière comprise entre 0 (le
premier élément du tableau) et la taille du tableau-1 (dernier élément du tableau).
if(args.length == 0)
{
System.exit(0);
}
somme=0.0;
for(i=0; i< nbNotes; i++)
somme += notes[i] ;
0
t1 1
2
10
t2 11
Si maintenant on exécute :
0
t1 1
2
10
t2 11
t1[1] = 5;
System.out.println(t2[1]); //affiche 5 !!!
Résultat :
Point : 1, 2
Point : 4, 5
Point : 8, 9
class Util
{
static void raz(int t[])
{
for(int i=0; i<t.length; i++)
t[i] = 0;
}
La même chose s’applique à un tableau fourni en valeur de retour. Par exemple, la méthode
suivante fourni un tableau formé des n premiers entiers :
public static int[] suite(int n)
{
int [] res = new int[n];
for(int i=0; i<t.length; i++)
res[i]=i+1;
return res;
}
Un appel à suite fournira une référence à un tableau dont on pourra éventuellement modifier
la valeur des éléments.
Premier exemple. Ces trois déclarations sont équivalentes pour un tableau à 2 dimensions :
int t [] [];
int [] t [];
int [] [] t;
Elles déclarent une référence à un tableau, dans lequel chaque élément est lui-même une référence
à un tableau d’entiers. Notez que pour l’instant aucun tableau n’existe encore.
Considérons la déclaration :
58 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION
0
0
0
t
0
0
Second exemple.
int t [] [];
t = new int [2] [];
int [] t1 = new int[3];
int [] t2 = new int[2];
t[0] = t1;
t[1] = t2;
La situation peut s’illustrer comme ceci :
t1
0
0
0
t
0
0
t2
Troisième exemple.
class Util
{
static void raz(int t[] [])
{
int i,j;
3.13. LES TABLEAUX 59
for(i=0;i<t.length;i++)
for(j=0;j<t[i].length;j++)
t[i][j]=0;
}
for(i=0;i<t.length;i++)
{
System.out.println("ligne de rang "+i+" = ");
for(j=0;j<t[i].length;j++)
System.out.println(t[i][j] + " ");
System.out.println();
}
}
Cas des tableaux réguliers. Si on souhaite créer une matrice de N L lignes et N C colonnes
on peut toujours procéder comme ceci :
int t[][] = new int [NL] [];
int i;
for(i=0; i<NL; i++)
t[i] = new int [NC];
On peut également écrire en Java :
int t[][] = new int [NL] [NC];
60 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION
void playTape()
{
System.out.println("lecture de la bande");
}
void recordTape()
{
System.out.println("enregistrement de la bande");
}
if(t.getCanRecord()==true){
t.recordTape();
}
}
}
2. Deuxième Fichier :
class DVDPlayer{
private boolean canRecord=false;
void recordDVD()
{
System.out.println("enregistrement du DVD");
}
3.14. EXERCICES DE COURS 61
if(d.getCanRecord() == true){
d.recordDVD();
}
}
}
Réponses :
1. Le fichier ne compile pas, il manque la création d’une instance TapeDeck, il faut donc créer
un objet. Pour cela il faut rajouter juste après la déclaration de la méthode main, et avant
la ligne t.canRecord = true; la ligne TapeDeck t = new TapeDeck();
2. Le fichier ne compile pas, il manque la méthode playDVD() dans la classe DVDPlayer, on
pourra par exemple rajouter les lignes suivantes juste avant la fin de la classe :
void playDVD()
{
System.out.println("lecture du DVD");
}
Remarque importante : pour simplifier l’exercice et pouvoir le faire dès le début, il n’y a pas
de droits d’accès indiqués pour les variables d’instances, elles sont donc publiques par défaut. Ceci
n’est pas une bonne pratique : il vaut mieux déclarer les variables privées et utiliser l’encapsula-
tion. A vous donc d’améliorer la correction de l’exercice !
3.14.3 Compilateur 2
Voici trois programmes, indiquez si ces programmes compilent ; si la réponse est oui dites ce
qu’ils font, sinon comment les corrigez vous ?
1. public class XCopie{
public static void main(String [] args)
{
int orig = 42;
XCopie x = new XCopie();
int y = x.go(orig);
System.out.println(orig + " " + y);
}
int go(int arg)
{
arg = arg * 2;
return arg;
}
}
2. class Horloge{
private String heure;
c.setHeure("1245");
String h = c.getHeure();
System.out.println("heure: " + h);
}
}
3. Ce programme compile, mais que fait-il ?
class Resultat{
public static void main(String []args){
Resultat r = new Resultat();
r.go();
3.14. EXERCICES DE COURS 63
void go(){
int y=7;
for(int x=1; x < 8 ; x++){
y++;
if(x > 4){
++y;
System.out.print(y+" ");
}
if(y > 14)
{
System.out.println(" x = " + x);
break;
}
}
}
}
Réponses :
1. La classe compile et s’exécute, le résultat est 42 84
2. Le fichier ne compile pas, il manque le type de retour String pour la méthode getHeure.
3. java Resultat
13 15 x = 6
3.14.5 Compilateur 3
Voici deux programmes, indiquez si ces programmes compilent ; si la réponse est oui dites ce
qu’ils font, sinon comment les corrigez vous ? (Pour des raisons de simplicité, l’encapsulation des
données a été omise dans cet exercice, nous rappelons cependant que cet aspect est primordial en
programmation objet)
64 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION
1. class Livres{
private String titre;
private String auteur;
class TestLivres{
public static void main(String [] args)
{
Livres [] mesLivres = new Livres[3];
int x=0;
mesLivres[0].setTitre("Panique à Java");
mesLivres[1].setTitre("Java et moi");
mesLivres[2].setTitre("Cuisinez en Java");
mesLivres[0].setAuteur("Monique");
mesLivres[1].setAuteur("Michel");
mesLivres[2].setAuteur("Sophie");
while(x<3)
{
System.out.print(mesLivres[x].getTitre());
System.out.print("par");
System.out.print(mesLivres[x].getAuteur());
x=x+1;
}
}
}
2. class Hobbits{
String nom;
int z = -1;
while(z < 2)
{
z=z+1;
h[z]= new Hobbits();
h[z].nom="bilbo";
if(z==1){
h[z].nom="frodon";
}
if( z == 2)
{
h[z].nom="sam";
}
System.out.print(h[z].nom + " est un ");
System.out.println("bon nom de Hobbit ");
}
}
}
Réponses :
1. Il manque la création des objets livres, juste après int x=0; il faut rajouter
mesLivres[0] = new Livres();
mesLivres[1] = new Livres();
mesLivres[2] = new Livres();
2. Cette classe compile et fonctionne, nous rappelons que les indices de tableaux commencent
à zéro.
66 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION
1 2 3
4 5
6 7
8 9
10 11 12
13 14 15
16
17
18
19 20 21 22
23
24 25
26
HORIZONTALEMENT VERTICALEMENT
1 Mettre en oeuvre (-) 2 Type d’incrémentation (-)
4 Teste d’abord (-) 3 Elle fait le travail (-)
5 32 bits (-) 4 Spécifie l’action dans un for (-)
7 Réponse d’une méthode (-) 5 Fixe la valeur de départ (-)
8 Avant le vrai code (-) 6 Méthode qui modifie (-)
11 Changement de type (-) 9 Contraire d’incrément (-)
12 Executrice (-) 10 Répétition (-)
13 Fait partie d’un tableau (-) 12 Package pour les E/S (-)
14 Peut être locale (-) 15 Faits pour être transmis (-)
16 Package contenant les classes essentielles (-) 18 Méthode qui accède (-)
17 A une méthode parseInt (-) 20 Classe qui calcule (-)
19 Pour générer des nombres au hasard (-) 22 Plus petit qu’un short (-)
21 Impossible à convertir (-) 23 Type de boucle (-)
24 Certaines réalités le sont (-) 24 Editeur préféré des puristes (-)
25 Plus petit qu’un int (-)
26 Interface de développement d’applications
(-)
3.14. EXERCICES DE COURS 67
6 7
8 9
10 11 12 13
14
15
16
17
18
19
20 21 22
23
HORIZONTALEMENT VERTICALEMENT
1 Ils n’ont pas de comportement (-) 2 Implémente un comportement (-)
6 Tous les objets en dérivent (-) 3 Retourne une position (-)
7 Premier indice (-) 4 Retourne un nombre d’éléments (-)
10 Ajoute un objet (-) 5 Grand décimal (-)
11 Loup y es-tu ? (-) 8 Groupe de classes (-)
14 Mutateur (-) 9 Pour savoir si une liste est vide (-)
16 Croı̂t dynamiquement (-) 12 Sorte (-)
18 Homologue de 4 (vertical) (-) 13 Position d’un élément (-)
20 Inconstante (-) 15 Pas réel (-)
22 Sinon (-) 16 Acronyme de la bibliothèque (-)
23 Unité adressable (-) 17 Commun aux courses et aux tableaux (-)
19 Récupère une valeur (-)
21 Article (rien à voir avec Java) (-)
68 CHAPITRE 3. LA NOTION D’OBJETS : DÉFINITION ET UTILISATION
Chapitre 4
faitDesVisites traiterPatient()
donnerConseil() faireUneIncision()
void donnerConseil()
{
//donner un simple conseil
}
69
70 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME
void faireUneIncision()
{
//action spécifique du chirurgien
}
}
public Point()
{
this(0,0);
}
Imaginons que nous ayons besoin de manipuler des points colorés de manière à :
— garder les même fonctionnalités de Point,
— ajouter le traitement d’une couleur,
— redéfinir l’affichage d’un point avec sa couleur.
4.1. PRÉSENTATION ET DÉFINITION 71
PointCouleur est un Point, Point est la classe de base ou superclasse, PointCouleur est la sous-
classe ou classe dérivée. Un objet de type PointCouleur peut alors faire appel aux méthodes
publiques de PointCouleur et aux méthodes publiques de Point.
Un objet d’une classe dérivée peut accéder directement aux membres public de sa classe de
base, mais pas aux membres privés.
A retenir :
— Une classe dérivée n’accède pas aux membres privés (champs et méthodes) de sa su-
perclasse. Par exemple, les points x et y ne sont pas accessibles directement de la classe
PointCouleur.
— Une classe dérivée a accès aux membres publiques de sa superclasse. Pour accéder à une
méthode public de la superclasse on utilise le mot clé super.
— Un objet d’une classe dérivée accède aux membres publics de sa classe de base exactement
comme s’ils étaient définis directement dans la classe dérivée elle-même.
Exemple d’utilisation des 2 classes précédentes.
Je suis en 3 5
Je suis en 3 5
de couleur 3
72 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME
Je suis en 5 8
de couleur 2
Je suis en 6 5
de couleur 2
Quand il n’y a pas de constructeur, un pseudo-constructeur par défaut est appelé, nous nous
intéressons maintenant au constructeur de la classe dérivée.
Attention.
— Les mots clés this et super ne peuvent pas être utilisés en même temps.
— Lorsqu’une classe dérive d’une classe qui dérive elle aussi d’une autre classe, l’appel par
super ne concerne que le constructeur de la classe de base de niveau immédiatement
supérieur.
4.2. CONSTRUCTION ET INITIALISATION DES OBJETS DÉRIVÉS 73
super()
class B extends A
{
// pas de constructeur
}
Cet exemple provoque une erreur car il n’y a pas de constructeur sans argument dans A.
La construction d’un objet B dérivé d’un objet A entraı̂ne 6 étapes :
1. Allocation mémoire pour un objet de type B (y compris les champs définis dans la super-
classe A).
2. Initialisation par défaut de tous les champs de B (aussi bien ceux hérités de A que ceux
définis dans B) aux valeurs nulles classiques.
6. Exécution du constructeur de B.
B C
D E F G
4.3.1 Redéfinition
La redéfinition consiste à re-écrire une méthode définit dans la super-classe et à changer son
comportement. Le nombre et le type des arguments ainsi que la valeur de retour doivent être
exactement les mêmes.
Lorsque l’on utilise la méthode affiche d’un objet de type PointCouleur, la méthode définie
dans la classe PointCouleur est appliquée, sinon pour un objet de type Point uniquement c’est
la méthode définie dans la classe Point.
Imaginons que nous ayons une hiérarchie de classe avec plusieurs dérivations successives. Dans
la classe la plus “élevée” une méthode f et définie et cette méthode est redéfinie dans certaines de
ses sous-classes.
4.4. LE POLYMORPHISME 75
A définition de f
B redéfinition de f
C
redéfinition de f
D E F
Voici la liste des méthodes f qui seront appelées en fonction du type de l’objet considéré :
— pour A, la méthodes f de A,
— pour B, la méthodes f de A,
— pour C, la méthodes f de C,
— pour D, la méthodes f de D,
— pour E, la méthodes f de A,
— pour F, la méthodes f de C.
Attention. Les droits d’accès des méthodes redéfinies ne doivent pas être diminués : une méthode
publique dans la super-classe ne peut pas être redéfinie privée dans la classe dérivée, l’inverse est
cependant possible.
4.3.2 La surchage
La surcharge ou surdéfinition consiste à modifier le prototype d’une méthode existante, en
changeant le nombre d’arguments et/ou le type des arguments. Nous avons déjà vu cette notion
auparavant, dans le cadre de l’héritage, la recherche d’une méthode acceptable se fait en “remon-
tant” les dérivations successives.
4.3.3 En résumé
— Si une méthode possède la même signature dans une classe dérivée que dans une classe
parente :
— les types des valeurs de retour doivent être exactement les mêmes,
— les droits d’accès de la méthode de la classe dérivée ne doivent pas être moins élevés
que dans la classe parente,
— la clause throws de la méthode dérivée ne doit pas mentionner des exceptions non
indiquées dans la clause throws de la méthode de la classe parente. (Nous verrons ceci
plus tard).
Si ces 3 conditions sont réunies, nous avons une redéfinition sinon il y a une erreur.
— Si la signature de la méthode (nombre/type des arguments) n’est pas la même on a une
surcharge (ou surdéfinition). On rappelle que le type de la valeur de retour n’est pas pris
en compte dans le cas d’une surcharge.
Notez qu’il est possible de dupliquer des champs lors d’un processus d’héritage, dans ce cas seul
le champ de la classe dérivée n’est visible de “l’extérieur”. Ceci est à éviter.
4.4 Le polymorphisme
Le polymorphisme permet de manipuler des objets sans connaı̂tre tout à fait leur type. C’est
un principe extrêmement puissant en programmation orientée objet, qui complète l’héritage.
Exemple de base :
...
}
...
}
Tout élément de type PointCouleur est également de type Point, il est possible d’utiliser cette
caractéristique commune.
Point p; 3 x
(Point) p 5 y
p = new Point(3,5);
...
4 x
p = new PointCouleur(4,8,(byte) 2); (Point) p 8 y
2 couleur
Autre exemple.
p = new PointCouleur(4,8,(byte)2);
p.affiche(); //méthode affiche de la classe PointCouleur
B C
D E F G
Autre exemple : cette fois on redéfinit pas de méthode affiche dans la classe PointCouleur
public class Point
{
private int x,y;
Liaison dynamique. Lors d’un appel d’une méthode a.f() où a est supposé être de type d’une
classe T, le choix de f est déterminé :
— A la compilation : on détermine dans la classe T ou ses ascendants la signature de la
meilleure méthode f() convenant à l’appel, ce qui défini du même coup le type de la valeur
de retour.
— A l’exécution : on cherche la méthode f de syntaxe et de type de retour voulu à partir de la
classe correspondante au type effectif de l’objet référencé par a (de type T ou ascendant).
Si cette classe ne comporte pas de méthode appropriée on remonte le plus possible jusqu’à
ce qu’on en trouve une.
Exemple
o = p;
((Point)o).affiche();
Point p1 = (Point) o;
p1.affiche();
Attention. L’instruction o.affiche(); provoque une erreur car il n’y a pas de méthode affiche
définie dans la classe Object. Il faut alors faire une conversion (cast) explicite comme le montre
l’exemple ci-dessus.
...
public Point(...){...}
...
Exemple
public abstract class A
{
public void f()
{
... // méthode complètement sécifiée dans A
}
Intérêt.
— Permet de spécifier toutes les fonctionnalités que l’on souhaite disposer dans les classes
dérivées.
— Permet d’exploiter le polymorphisme en étant sûr que certaines méthodes existes.
4.8. LES CLASSES ABSTRAITES 81
Exemple.
abstract class Affichable
{
abstract public void affiche();
}
public Entier(int n)
{ valeur = n; }
public Flottant(float x)
{ valeur = x; }
public interface I2
{
int h();
}
— Les interfaces peuvent contenir des constantes de type static final. Ces constantes seront
donc accessibles en dehors d’une classe implémentant l’interface.
— Les interfaces peuvent se dériver, mais les classes dérivées obtenues sont aussi des interfaces.
interface I1
{
static final int MAXI = 20;
void f(int n);
}
interface I2 extends I1
{
void g();
}
class A implements I1
{
//redéfinit f, on a accès à MAXI directement
//par exemple if(i < MAXI) ...
}
I1 i = new A();
I1 i2 = new B();
I2 b = new B();
System.out.println("Constante "+I1.MAXI);
Graveur
graver()
GraveurCD GraveurDVD
graver() graver()
Combo
En Java il n’y a pas d’héritage multiple : on ne peut hériter que d’une seule classe. On contourne
alors le problème en utilisant des interfaces. Dans l’exemple du dessus, une solution pourrait être
de créer une classe Combo qui hérite de Graveur et qui implémente l’interface GraveurCD possédant
une méthode graverCD et l’interface graverDVD possédant une méthode graverDVD.
Exemple d’utilisation
class BBBB { }
class Class1{
public static void main(String[ ] arg){
AAAA a = new AAAA( );
CCCC c = new CCCC( );
int[ ] t = new int[10];
Object[ ] tabObj = new Object[5];
BBBB[ ] tabB = new BBBB[33];
4.11. EXERCICES DE COURS 85
Voici le résultat
classe de a : AAAA
classe de c : CCCC
classe de t : [I
classe de tabObj : [Ljava.lang.Object;
classe de tabB : [LBBBB;
D’autres utilisations de cette classe sont possibles, notamment pour la création et l’utilisation
de classes dynamiques. Ceci sort du cadre de ce cours, mais pour plus d’informations vous pouvez
consulter la documentation de Sun et jeter un coup d’oeil à la dernière partie du dernier chapitre.
L’idée de cet exercice est de trouver une application concrète à toute définition abstraite.
Plusieurs classes sont listées dans la colonne du milieu. L’objectif est d’imaginer des applications
dans lesquelles la classe listée pourrait être abstraite et des applications où elle pourrait être
concrète. Quelques exemples sont fournis pour aider à démarrer. Par exemple, la classe arbre
serait abstraite dans un programme pépinière où les différences entre un chêne et un peuplier sont
importantes. Mais dans un programme de simulation de golf, la classe arbre peut être concrète
(par exemple une sous-classe d’obstacle), parce qu’il n’y a aucun besoin de différencier les arbres.
86 CHAPITRE 4. LES CONCEPTS D’HÉRITAGE ET DE POLYMORPHISME
Chaise
Client
Commande
Livre
Magasin
Fournisseur
ClubDeGolf
Carburateur
Four
4.11.2 Compilateur
Voici un bout de programme Java incomplet :
class Monstre{
// -- méthode A --
}
3. Cet ensemble compile mais ne produit pas le bon résultat parce que la méthode fairePeur()
de la classe Monstre n’est pas redéfinie dans la classe Vampire.
4. Cet ensemble compile mais ne produit pas le bon résultat parce que la méthode fairePeur()
de Vampire accepte un byte alors qu’il faut un int.
Les ensembles 3 et 4 produisent en fait le résultat suivant :
5. Les variables statiques peuvent servir à compter les instances d’une classe.
6. Les constructeurs sont appelés avant que des variables statiques ne soient initialisées.
7. MAX SIZE serait un bon nom pour une variable statique finale.
8. Un bloc initialisateur statique s’exécute avant un constructeur de la classe.
9. Si une classe est finale, toutes ses méthodes doivent être finales.
10. Une méthode finale ne peut être redéfinie que si la classe est étendue.
11. Il n’y a pas de classe enveloppe pour les booléens.
12. On utilise une classe enveloppe pour traiter une valeur primitive comme un objet.
13. Les méthodes parseXXX retournent toujours une chaı̂ne de caractères.
Chapitre 5
La gestion d’exceptions
5.1 Présentation
La gestion d’exceptions permet :
— de détecter une anomalie et de la traiter indépendamment de sa détection,
— de séparer la gestion des anomalies du reste du code.
Une anomalie peut être due, par exemple, à des données incorrectes, à un fin de fichier
prématurée, à un événement non prévu par le programmeur.
Exemple. On dispose d’une classe Point pour laquelle on ne veut pas gérer des données négatives.
Nous lançons une exception lorsque les coordonnées sont négatives par l’instruction throw. Pour
commencer nous créons un objet donc le type sert à identifier l’exception concernée. Une classe
d’exception se définit en héritant de la classe Exception déjà définie dans l’API java.
Avant cela il faut indiquer le type d’exceptions qui peuvent être rencontrées dans les méthodes
de la classe Point. On ajoute alors throws ErreurCoordonnees dans la déclaration des méthodes
qui peuvent rencontrer ces exceptions. Voici un exemple :
this.x = x;
this.y = y;
}
89
90 CHAPITRE 5. LA GESTION D’EXCEPTIONS
}
}
Ensuite pour récupérer une exception lancée par une méthode, on utilise les instructions try
et catch :
public class TestExcept
{
public static void main(String args[])
{
try{ //Instructions qui peuvent voir une exception se déclencher
Point a = new Point(1,4);
a.affiche();
a = new Point(-3,5);
a.affiche();
}
catch(ErreurCoordonnees e)
{ //gestion de l’exception
System.err.println("Erreur de construction");
System.exit(-1);
}
}
}
Résultat.
Je suis un point de coordonnées 1 4
Erreur de construction
Attention : le bloc catch doit suivre immédiatement le bloc try.
}
}
this.x = x;
this.y = y;
}
x+=dx;
y+=dy;
}
this.x=x;
this.y=y;
}
5.3. DÉRIVATION ET REDÉCLENCHEMENT D’EXCEPTIONS 93
...
}
}
Il est possible de redéclencher une exception pour la transmettre à un niveau englobant.
try{
...
}
catch(MonException e)
{
...
throw e;
}
Exemple complet.
class ErreurCoord extends Exception { }
class ErreurBidon extends Exception { }
this.x = x;
this.y = y;
}
}
catch(ErreurBidon e)
{
System.err.println("ErreurBidon dans le catch de main");
}
System.err.println("Après le bloc try-catch de main");
}
}
Résultat.
try{
...
}
catch(Ex e)
{
...
}
finally{
...
}
Ici le même résultat aurait pu être obtenu en supprimant tout simplement le mot clé finally et
en mettant les instructions de ce bloc à la suite du gestionnaire.
Ce n’est pas la même chose dans ce cas :
Si une exception se produit dans f, on exécutera d’abord les instructions du bloc finally avant
de se brancher sur le gestionnaire d’exceptions.
De manière générale, le bloc finally peut s’avérer intéressant dans le cadre de l’acquisition
de ressources, c’est-à-dire tout ce qui nécessite une action pour la bonne bonne poursuite des
96 CHAPITRE 5. LA GESTION D’EXCEPTIONS
Autre cas.
try{
...
if(...)
break;
...
}
finally{
...
}
//suite
...
Si la commande break est exécutée, on exécute d’abord le bloc finally avant de passer à la suite.
Autre exemple possible.
try{
...
return 0;
}
finally{
...
return -1;
}
Ici la méthode semble devoir exécuter une instruction return 0;, mais il faut quand même exécuter
le bloc finally contenant à son tour return -1;. Dans ce cas c’est la dernière valeur qui sera
renvoyée (-1).
Exemple de traitement.
public class TestExcept
{
public static void main(String [] args)
{
try
{ //on ne fait pas de vérification sur les arguments pour alléger le code
int t[];
5.6. EXERCICES DE COURS 97
int n,i;
n = Integer.parseInt(args[0]);
System.out.print("taille voulue "+n);
t = new int[n];
i = Integer.parseInt(args[1]);
System.out.print(" indice : "+i);
t[i] = 12;
System.out.println(" *** fin normale");
}
catch(NegativeArraySizeException e)
{
System.out.println("Exception de taille de tableau négative "+e.getMessage());
}
catch(ArrayIndexOutOfBoundsException e)
{
System.out.println("Exception indice de tableau "+e.getMessage());
}
}
}
Il existe beaucoup d’exceptions et les traiter toutes serait fastidieux, on traite celles qui sont
les plus pertinentes pour le problème à traiter.
17. Les exceptions susceptibles de survenir à l’exécution doivent être gérées ou déclarées :
98 CHAPITRE 5. LA GESTION D’EXCEPTIONS
4 5 6 7
8 9 10 11
12
13 14 15 16
17
18
19
20
21 22
23 24
HORIZONTALEMENT VERTICALEMENT
4 Donne une valeur (-) 1 A beaucoup de méthodes statiques (-)
6 Intercepte (-) 2 Déclare une exception (-)
8 Arbre généalogique (-) 3 Otées de la pile (-)
12 On en hérite (-) 5 Patron d’objet (-)
14 Recette de code (-) 6 Programmer (-)
18 Programme (-) 7 Avant le catch (-)
19 Le plus petit primitif (-) 9 Faire du nouveau (-)
20 Constante (-) 10 Transformerions en bytecode (-)
21 Lance (-) 11 Objet (-)
22 Non abstraite (-) 13 Pas un comportement (-)
23 Instances de classes (-) 15 Posent des conditions (-)
24 Gestion ou ... telle est la loi (-) 16 Erreur ou problème (-)
17 A des décimales (-)
Chapitre 6
ch1
ch4 hello
La dernière instruction est équivalente à la deuxième.
Les objets de type String ne sont pas modifiables
99
100 CHAPITRE 6. LES CHAÎNES DE CARACTÈRES EN JAVA
int n=24;
int y=2005;
String d=new String("Nous le somme le " + n + " octobre " + y);
Un nouvel objet contenant la chaı̂ne bonjour monsieur est créé et l’ancienne chaı̂ne bonjour de-
vient candidate au ramasse-miettes.
Attention un nouvel objet est créé, référencé par ch1, on aurait pu écrire ch=ch.replace(’o’,’a’);
float f=2.5f;
String ch0=""+f; // première possibilité
String ch1=String.value(f); //deuxième possibilité
Notez la différence entre args.length qui la longueur du tableau et args[i].length() qui est
longueur de la chaı̂ne à la position i du tableau.
Autre exemple :
StringTokenizer st = new StringTokenizer("/home/toto/TP-JAVA","/-",false);
System.out.println("Il y a "+st.countTokens()+" éléments dans la cha^
ıne");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
Ce bout de code provoque l’affichage de :
home
toto
TP
JAVA
104 CHAPITRE 6. LES CHAÎNES DE CARACTÈRES EN JAVA
Chapitre 7
Les entrées/sorties
105
106 CHAPITRE 7. LES ENTRÉES/SORTIES
7.3 Lecture/écriture
Voici un exemple de classe effectuant une lecture/écriture d’un fichier. Dans un premier temps,
on lit le contenu d’un fichier nommé "poeme.txt" et on range chacune des lignes dans une structure
de type StringBuffer (cf le chapitre précédent pour ce type). Dans un second temps, on écrit
dans un fichier nommé "poeme copie.txt", on commence par y écrire Ma chaine puis un saut de
ligne, ensuite on écrit le contenu stocké dans un StringBuffer.
import java.io.*;
class GestionFichier {
public static void litEtEcrit(String args[]) {
StringBuffer contenu = new StringBuffer();
try{
//exemple de lecture
File cheminAbstraitEntree = new File("poeme.txt");
FileReader fluxLectureTexte = new FileReader(cheminAbstraitEntree);
BufferedReader tamponLecture = new BufferedReader(fluxLectureTexte);
String ligne;
tamponLecture.close();
fluxLectureTexte.close();
}
catch(IOException e)
{
System.err.println("Erreur de lecture");
}
108 CHAPITRE 7. LES ENTRÉES/SORTIES
try{
//exemple d’écriture
File cheminAbstraitSortie = new File("poeme_copie.txt");
FileWriter fluxEcritureTexte = new FileWriter(cheminAbstraitSortie);
BufferedWriter tamponEcriture = new BufferedWriter(fluxEcritureTexte);
tamponEcriture.write("Ma chaine\n");
tamponEcriture.flush();
tamponEcriture.write(contenu.toString());
tamponEcriture.flush();
tamponEcriture.close();
fluxEcritureTexte.close();
}
catch(IOException e)
{
System.err.println("Erreur d’écriture");
}
}
}
On commence par créer une variable de type File. Ensuite, dans le cas d’une lecture de fichier,
on crée une flux de type FileReader et ce flux est mis dans un tampon de type BufferedReader.
La lecture dans le tampon se fait à l’aide de la méthode readLine(). Pour l’écriture, on crée
un flux de type FileWriter, que l’on met dans un tampon BufferedWriter et l’écriture se fait
à l’aide de la méthode write. La méthode flush sert à vider le contenu du tampon. A chaque
fois, les différents flux doivent être d’abord ouverts (instructions new), puis fermés (instructions
close à la fin.
Note : pour limiter les accès au disque dur, java conserve les caractères devant être écrits dans
un fichier dans un tampon (buffer en anglais), puis écrit le contenu du tampon dans le fichier
lorsque le tampon est plein. Si l’on veut forcer l’écriture du contenu du tampon dans le fichier on
peut utiliser la commande flush. Il faut toujours appliquer flush au moins une fois juste avant
la fermeture du fichier.
Chapitre 8
109
110 CHAPITRE 8. QUELQUES ÉLÉMENTS DE L’API
— Double
Voici maintenant comment faire pour envelopper une valeur et revenir au type primitif. On trans-
met la valeur au constructeur de la classe enveloppe.
int i = 145;
Integer valeurI = new Integer(i);
.
.
.
int valeurOriginale = valeurI.intValue();
Notez que toutes les classes fonctionnent de la même manière, Boolean a une méthode booleanValue(),
Character a une méthode characterValue(), etc.
Ces classes ont également des méthodes statiques très utiles comme Integer.parseInt().
De manière générale, les méthodes de transformation parseXXX() acceptent une valeur de type
String et retournent une valeur de type primitif.
String s = "2";
int x = Integer.parseInt(s);
double d = Double.parseDouble("420.24");
8.3.2 ArrayList
Il s’agit d’une sorte de tableau d’objets de taille variable, fonctionnant comme une sorte de
liste. Il peut être vu comme un vecteur.
8.3. LES COLLECTIONS 111
Construction
Object elem;
...
v.add(elem);
Taille de la liste On utilise la méthode size() qui renvoie le nombre d’éléments contenus dans
la liste.
ListIterator it = v.listIterator();
while(it.hasNext())
{
Object o = it.next();
...
}
Autes méthodes
— La méthode boolean contains(Object o) renvoie true si l’objet est dans la liste et false
sinon.
— boolean isEmpty() renvoie true si la liste est vide et false sinon.
— int indexOf(Object o) renvoie l’indice de l’objet o dans la liste ou -1 s’il n’est pas présent
112 CHAPITRE 8. QUELQUES ÉLÉMENTS DE L’API
Construction et parcours
HashSet e;
...
Iterator it = e.iterator();
while(it.hasNext())
{
Object o = it.next();
....
}
HashSet e;
Object elem;
...
boolean existe = e.add(elem);
if(existe) Sytem.out.println(elem+" existe deja");
else Sytem.out.println(elem+" a ete ajoute");
Suppression
HashSet e;
Object elem;
...
boolean existe = e.remove(elem);
if(existe) Sytem.out.println(elem+" a ete supprime");
else Sytem.out.println(elem+" n’existe pas");
Autre possibilité.
HashSet e;
...
Iterator it = e.iterator();
it.next(); it.next();
it.remove();
8.3. LES COLLECTIONS 113
Opérations ensemblistes
— e1.addAll(e2) place dans e1 tous les éléments de e2.
— e1.retain(e2) garde dans e1 tout ce qui appartient à e2.
— e1.removeAll(e2) supprime de e1 tout ce qui appartient à e2.
Notion de Hachage Un ensemble HashSet est implémenté par une table de hachage, c’est-à-
dire que ses éléments sont stockés selon une position donnée. Cette position est définie selon un code
calculé par la méthode int hashCode() utilisant la valeur effective des objets. Les classes String
et File par exemple implémentent déjà cette méthode. Par contre les autres classes utilisent par
défaut une méthode dérivée de Object qui se content d’utiliser comme valeur la simple adresse
des objets (dans ces conditions 2 objets de même valeur auront toujours des codes de hachage
différents). Si l’on souhaite définir une égalité des éléments basés sur leur valeur effective, il faut
redéfinir la méthode hashCode dans la classe correspondante.
Voici une exemple avec la classe Point. La redéfinition de la méthode equals sert à définir
l’égalité entre les objets de manière à n’avoir qu’un seul élément dans un ensemble (pour rappel
dans un HashSet les éléments ne peuvent apparaitre qu’une seule fois).
import java.util.*;
class Point
{
private int x,y;
System.out.print("ensemble : ");
affiche(ens);
}
}
Résultat.
Remarque la fonction de hachage est dictée par la simplicité, des points de coordonnées différentes
peuvent avoir la valeur de hashCode. Il faudrait trouver une fonction qui renvoie une valeur unique
pour chaque point.
Résultat.
import java.util.*;
116 CHAPITRE 8. QUELQUES ÉLÉMENTS DE L’API
Résultat.
t initial = [4, 9, 2, 3, 8, 1, 3, 5]
t trie = [1, 2, 3, 3, 4, 5, 8, 9]
t melage = [9, 2, 1, 4, 3, 5, 3, 8]
t trie inverse= [9, 8, 5, 4, 3, 3, 3, 1]
Ajout d’information
Si la clé fournie à put existe déjà, la valeur associée remplacera l’ancienne (une clé donnée ne
pouvant figurer qu’une seule fois dans la table). put fourni en retour soit l’ancienne valeur si la
clé existait déjà soit null.
Les clés et les valeurs doivent obligatoirement être des objets. Il n’est pas nécessaire que les
clés soient de même type, pas plus que les éléments, ce sera cependant souvent le cas pour des
raisons évidentes de facilité d’exploitation de la table.
8.3. LES COLLECTIONS 117
Recherche d’information
Object o = m.get("x");
if(o==null) System.out.println("Aucune valeur associée à la clé");
if(m.containsKey("p"))
Integer n = (Integer) m.get("p");
Suppression d’information
HashMap m;
....
Set entrees = m.entrySet(); //entrees est un ensemble de paires
Iterator iter = entrees.iterator();
while(iter.hasNext())
{
Map.Entry entree = (Map.Entry) iter.next(); //paire courante
Object cle = entree.getKey(); //cle de la paire courante
Object valeur = entree.getValue(); //valeur de la paire courante
...
}
L’ensemble renvoyé par entrySet n’est pas une copie de la table, c’est une vue.
Autre vues
— L’ensemble des clés :
HashMap m;
...
Set cles = m.keySet();
— La collection des valeurs :
Collection valeurs = m.values();
Exemple complet
import java.util.*;
8.4 Divers
8.4.1 Eléments disponibles à partir de Java 1.5
Il existe plusieurs instructions et structures qui ont été ajoutées à partir de la version 1.5. Nous
indiquons deux principales ici, à vous de découvrir les autres si vous souhaitez aller plus loin.
— Si un tableau contient des éléments d’un certain type TypeElement (qui peut-être un type
primitif, un tableau ou une classe), la boucle
for(int i=0;i<tableau.length;i++)
{
lancerMethode(tableau[i]);
}
peut être écrite plus simplement :
for(TypeElement elt : tableau)
lancerMethode(elt);
— si liste est une structure de donnée capable d’être parcourue par un itérateur (c’est-à-dire
un objet de type Collection) et contenant toujours des élements de type TypeElement, la
boucle
for(Iterateur it=liste.iterator() ; it.hasNext() ; )
lancerMethode( (TypeElement) it.next());
peut être écrite plus simplement :
for(TypeElement elt: liste)
lancerMethode(elt);
8.4.2 Généricité
Si on souhaite définir une collection (vector, arraylist, ...) contenant un ensemble d’objets de
sorte que tous ces objets soient de type TypeObjet, on peut définir des collections ne contenant que
ce type d’objet en déclarant : List<TypeElement>. Ceci permet de vérifier que l’ajout d’éléments
à la liste sont bien de type TypeElement et que lors d’accès à des éléments de la liste on a la
certitude qu’ils sont bien de type TypeElement.
Un petit exemple.
— enfin, le moyen le plus naturel est de demander à un objet quelle est sa classe. L’exemple
suivant affecte encore le type java.math.BigInteger à la variable uneClasse :
Object unObjet = new
BigInteger("92233720368547758079223372036854775807");
...
uneClasse = unObjet.getClass();
Pour survoler cette question assez pointue, voici un exemple qui illustre quelques unes des
possibilités de e la classe Class et du paquet reflect. La méthode demoReflexion ci-dessous,
purement démonstrative, prend deux objets quelconques a et b et appelle successivement toutes
les méthodes qui peuvent être appelées sur a avec b pour argument, c’est-à-dire les méthodes
d’instance de la classe de a qui ont un unique argument de type la classe de b :
8.5. LES AUTRES ÉLÉMENTS DE L’API 121
import java.lang.reflect.Method;
import java.math.BigInteger;