Programmation Orienté Objet
Introduction au Langage de Programmation
JAVA
Standard Edition
Dr. HAJJI TARIK
[email protected] ENSAM – UMI
2020-2021
ENSAM 2020-2021- T.HAJJI- 1
Sommaire
• Les bases de java
• Les constructeurs
• Généralités sur les structures lexicales de Java
• Héritage
• La gestion des Exceptions
• Complément
• Introduction aux Interfaces graphiques
ENSAM 2020-2021- T.HAJJI- 2
Introduction Générale: L’évolution
Programmation séquentielle
Programmation parallèle
Programmation dynamique
Programmation distribuée
Programmation orienté objet Classes
Structures
de
La
données portabilité
Variables
Typage de données
ENSAM 2020-2021- T.HAJJI- 3
Ch I. JAVA: les bases
I. Généralités sur les objets
• Un objet est une variable améliorée: il stocke les
données, mais on peut effectuer des requêtes sur cet objet
(on demande à l’objet de faire des opérations sur lui-
même), en envoyant un message à cet objet. Ceci est
équivalent à un appel de fonction.
• Chaque objet à son espace mémoire
• Chaque objet a un type précis: c’est-à-dire chaque objet est
une instance (variable) d’une class (type)
• Un programme est ensemble d’objets qui s’envoient des
messages.
ENSAM 2020-2021- T.HAJJI- 4
II. Les bases à connaître
• Java est un langage orienté objet: l'entité de base de
tout code Java est la classe
• Sa syntaxe est proche du langage C
• Il est fourni avec le JDK (Java Developpment Kit)
– Outils de développement
– Ensemble de paquetages très riches et très variés
• Portable grâce à l’exécution par une machine
virtuelle
ENSAM 2020-2021- T.HAJJI- 5
• En Java, tout se trouve dans une classe.
– Il ne peut y avoir de déclarations ou de code en dehors
du corps d'une classe.
• La classe elle même ne contient pas directement du
code.
– Elle contient des déclarations de variables globales, que
l'on appelle des ``attributs'',
– et des méthodes (équivalents à des fonctions).
• Le code se trouve exclusivement dans le corps des
méthodes, mais ces dernières peuvent aussi contenir
des déclarations de variables locales (visibles
uniquement dans le corps de la méthode).
ENSAM 2020-2021- T.HAJJI- 6
III. Mon premier programme en Java
Considérons le code source du programme qui affiche à l’écran
Mon premier programme Java
// Fichier source MonPremProg.java
public class MonPremProg {
// déclaration de données globales
//définition des méthodes
public static void main(String args[]) {
// déclaration des données locales;
System.out.println(" Mon premier programme Java " );
}
}
ENSAM 2020-2021- T.HAJJI- 7
• De manière générale, dans tout programme autonome destiné
à être exécuté doit contenir une méthode particulière
nommée main() définie de la manière suivante:
public static void main(String args[]) {
/* corps de la méthode */
}
• Le paramètre args de la méthode main() est un tableau
d’objets de type String. Il n’est pas utilisé mais, il est exigé
par le compilateur Java.
• La classe contenant la méthode main() doit obligatoirement
être public afin que la machine virtuelle y accès. Dans
l’exemple, le contenu de la classe MonPremProg est réduit à
la définition d’une méthode main().
ENSAM 2020-2021- T.HAJJI- 8
• Un fichier source peut contenir plusieurs
classes mais une seule doit être public(ici
PremProg).
• Le nom du fichier source est identique au
nom de la classe publique qu'il contient,
suivi du suffixe .java, dans l’exemple
MonPremProg.java
ENSAM 2020-2021- T.HAJJI- 9
Compilation
• La compilation d’un programme Java ne traduit pas
directement le code source en fichier exécutable.
• Elle traduit d’abord le code source en un code
intermédiaire appelé « bytecode ».
• C’est le bytecode qui sera ensuite exécuté par une
machine virtuelle (JVM ; Java Virtual Machine).
• Ceci permet de rendre le code indépendant de la
machine qui va exécuter le programme.
ENSAM 2020-2021- T.HAJJI- 10
• Sun fournit le compilateur javac avec le JDK. Dans notre
exemple on exécute:
javac MonPremProg.java
• Cette compilation crée un fichier nommé
«MonPremProg.class» qui contient le bytecode
• On peut désigner le fichier à compiler par un chemin absolu ou
relatif : javac home/user2/MonPremProg.java
• Si un système possède une JVM il peut exécuter tous
les bytecodes (fichiers .class) compilés sur n’importe
qu’el autre système (c’est la notion de la portabilité
de java).
ENSAM 2020-2021- T.HAJJI- 11
Exécution du bytecode
• Le bytecode doit être exécuté par une JVM. Cette
JVM n'existe pas ; elle est simulée par un
programme qui:
– lit les instructions (en bytecode) du programme .class,
– les traduit dans le langage machine relatif à la machine
sur laquelle il sera exécuté.
– Lance leur exécution
ENSAM 2020-2021- T.HAJJI- 12
Exécution avec java
Dans le cas de l’environnement JDK de SUN.
• Pour compiler il suffit d’utiliser la commande javac:
javac MonPremProg.java
• Pour exécuter, Sun fournit le programme java qui simule
une JVM. Il suffira d’utiliser la commande:
java MonPremProg
qui interprète le bytecode de la méthode main() de la classe
MonPremProg
ENSAM 2020-2021- T.HAJJI- 13
L’exécution du programme MonPremProg affiche à l’écran,
comme résultat, la chaîne de caractères:
Mon premier programme Java,
grâce à l’instruction:
System.out.println(" Mon premier programme Java ");
Dans le programme, une ligne qui commence avec
// est un commentaire jusqu’à la fin de la ligne
Une ligne qui commence avec
/* est un commentaire qui peut être étalé sur
plusieurs lignes, et doit terminer avec */
ENSAM 2020-2021- T.HAJJI- 14
III. Les classes en Java
• Une classe décrit un ensemble d’objets qui partagent des
caractéristiques communes.
• On définit une classe pour représenter un problème au lieu
d’utiliser un type de données pour représenter une unité de
stockage dans l’ordinateur.
• On utilise les classes exactement de la même manière que
les types de données prédéfinies.
• On peut créer des variables (objets ou instance) d’un type
particulier.
ENSAM 2020-2021- T.HAJJI- 15
Pour créer un nouveau type d’objets on utilise le mot clé class.
Le canevas de base pour définir un nouveau type de nom
NomDuType est la suivante:
class NomDuType {
/* Corps de la classe: instructions de définitions
des méthodes et des champs */
}
ENSAM 2020-2021- T.HAJJI- 16
Dans la définition de la classe (corps de la classe), on
met deux types d’éléments:
- des données membres de la classe (appelées
champs) qui sont des objets de n’importe quel
type.
- des fonctions membres de la classe (appelées
méthodes).
ENSAM 2020-2021- T.HAJJI- 17
Exemple:
UnTest est une classe (un nouveau type) qui a
3 champs : float, int et boolean.
class UnTest {
float x;
int i;
boolean b;
}
ENSAM 2020-2021- T.HAJJI- 18
Définition des méthodes
La définition de base d’une méthode ressemble à la définition
d’une fonction en C.
typeRetour nomMethode (/* Liste des parametres */) {
/* corps de la méthode */
}
- nomMethode est le nom de la méthode
- Liste de paramètre (arguments de la méthode) donne les
types et les noms des informations qu’on souhaite passer à
la méthode lors de son appel.
- typeRetour est le type de la valeur qui est retournée par la
méthode après son appel. Si la méthode ne fourni aucun
résultat, alors typeRetour est remplacé par le mot clé void.
ENSAM 2020-2021- T.HAJJI- 19
• Le mode de passage des paramètres dans les méthodes
dépend de la nature des paramètres :
– par référence pour les objets
– par copie pour les types primitifs
Exemple
class C {
void methode1(int i){
i+=12;
}
void methode2() {
int i = 3;
methode1(i);
System.out.println(i=" + i); // i=3
}
}
ENSAM 2020-2021- T.HAJJI- 20
IV. Création et manipulation d’Objets
Une fois la classe est définie, on peut créer des objets de la même
manière que la déclaration des types primitif:
Considérons la classe ClasseTest
class ClasseTest {
// corps de la classe ClasseTest
}
l’instruction:
ClasseTest a; // déclare a comme objet (variable) de type ClasseTest
ENSAM 2020-2021- T.HAJJI- 21
Attention:
- La déclaration d’une variable de type primitif réserve un
emplacement mémoire pour stocker la variable
- La déclaration d’un objet de type A, ne réserve pas une
place mémoire pour un objet de type A, mais seulement
un emplacement pour une référence à un objet de type A.
les objets sont manipulés avec des références. Exemple
classique de la télécommande et la Télé.
Télé = objet
Télécommande = référence.
Objet
Référence
Télécommande Télé
ENSAM 2020-2021- T.HAJJI- 22
- Pour modifier l’objet (par exemple augmenter le
son, …) on utilise la référence (la télécommande).
- Avoir une référence ne veut pas dire avoir l’objet
(on peut avoir la télécommande sans avoir la télé).
ENSAM 2020-2021- T.HAJJI- 23
L’emplacement pour l’objet doit être demandé explicitement
dans le programme en faisant appel à l’opérateur new.
L’expression new NomClasse créée un emplacement pour un
objet de type NomClasse.
class NomClasse {
/* Corps de la classe */
}
NomClasse a = new NomClasse();
En générale:
1. Chaque objet met ses données membres dans sa propre zone
mémoire.
2. Les données membres ne sont partagées entre les objets.
ENSAM 2020-2021- T.HAJJI- 24
Exemple 1:
class UnTest {
float x;
int i;
boolean b;
}
UnTest data = new UnTest(); // créer un objet data
ENSAM 2020-2021- T.HAJJI- 25
Exemple 2:
Supposons qu’on veut avoir une chaîne de caractères alors
on crée une référence sur String:
String s;
Ici on a créé la référence mais pas l’objet. Pour créer
l’objet, il faut initialiser la référence s.
String s=new String ("cours Java");
ou
String s;
s = new String ("cours Java");
Ou tout simplement ( C’est un cas particulier en Java pour les
objets string) par:
String s= " cours Java "
ENSAM 2020-2021- T.HAJJI- 26
Accès aux données membres
Exemple 1:
class UnTest {
float x;
int i;
boolean b;
}
UnTest data = new UnTest(); // créer un objet data
Maintenant l’objet dont la référence data existe. Pour accéder à
une donnée membre on indique le nom de la référence à
l’objet suivi par un point, suivi par le nom du membre dans
l’objet de la manière suivante:
data.i=2; // affecte 2 au membre i
data.b=true; // affecte true au membre b
data.x=1.2f; // affecte 1.2 au membre x.
ENSAM 2020-2021- T.HAJJI- 27
Appels des méthodes
• Les méthodes ne peuvent être créer que comme des
composante d’une classe.
• Une méthode ne peut être appelée que pour un objet.
• L’appel d’une méthode pour un objet se réalise en
nommant l’objet suivi d’un point suivi du nom de la
méthode et de sa liste d’arguments:
nomObjet.nomMethode(arg1, ….).
où
nomObjet: nom de la référence à l’objet
nomMethode: nom de la méthode.
ENSAM 2020-2021- T.HAJJI- 28
Exemple: soit f () une méthode qui ne prend aucun paramètre
et qui retourne une valeur de type int.
Alors si on a une référence à un objet appelé a pour lequel f()
peut être appelée, on peut écrire: int x = a.f()
class UnTest {
float x;
int i;
boolean b;
int f() {
return 10;
}
}
UnTest a = new UnTest(); // créer un objet a;
int x = a.f(); // affecte à x la valeur retournée par f
ENSAM 2020-2021- T.HAJJI- 29
Cas particulier des types primitifs
Les types primitifs ont un traitement particulier:
1. on n’utilise pas new,
2. les variables (non les références) sont créées au moment de leurs
déclaration.
------------------------------------------------------------------------------
Type | Taille | Classe Wrapper |
--------- |--------------------------------|--------------------------------- |
boolean | | Boolean |
char | 16 bits, Unicode | Character |
byte | 8 bits | Byte |
short | 16 bits | Short |
int | 32 bits | Integer |
long | 64 bits | Long |
float | 32 bits IEEE 754 | Float |
double | 64 bits IEEE 754 | Double |
void | | Void |
ENSAM 2020-2021- T.HAJJI- 30
On peut traiter les type primitif comme des objet en
utilisant les classe wrapper qui leurs sont associées.
Exemple:
char c=‘x’; // type primitif directement déclaré
Character car = new Character(c); // classe wrapper
ou
Character car = new Character (‘x’);
ENSAM 2020-2021- T.HAJJI- 31
Remarque :
Quand une donnée d’un type primitif est membre d’une classe
on est assuré qu’elle a une valeur par défaut même si on ne
l’initialise pas:
----------------------------------------------------------------
Type primitifs | Valeur par défaut |
---------------------------- |-----------------------------------|
boolean | false |
char | ‘\u0000’ (null) |
byte | (byte)0 |
short | (short)0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
-----------------------------------------------------------------
ENSAM 2020-2021- T.HAJJI- 32
Attention: Cette garanti d’initialisation par
défaut ne s’applique pas aux variables locales
(définition d’une fonction (méthode)).
ENSAM 2020-2021- T.HAJJI- 33
V. Notion de portée et de durée de vie des objets
Le concept de portée fixe simultanément la visibilité et la
durée de vie des noms définis dans cette portée. La portée
est fixée par les accolades { }.
Exemple:
{
int x=12; // seul x est accessible
{
int q=96; // x et q tous les deux sont accessibles
}
// seul x est accessible
// q est hors de portée
}
ENSAM 2020-2021- T.HAJJI- 34
Attention: Ceci n’est pas permis en Java
{
int x=12;
{
int x=96; // illégale en Java, valable en C, C++
}
}
ENSAM 2020-2021- T.HAJJI- 35
Portée des objet: Considérons une classe nommée A
{
A a=new A();
} // fin de portée
La référence a disparaît à la fin de la portée, par contre
l’objet qui été référencé par a existe toujours, mais on
n’a aucun contrôle sur lui (il reste inaccessible).
Les objets n’ont pas la même durée de vie que les
types primitifs.
ENSAM 2020-2021- T.HAJJI- 36
Il n’existe aucun opérateur explicite pour détruire
l’objet dont on n’a pas besoin, mais il existe 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 on connaît le nombre de références à
un objet donné.
- Lorsqu’il n’existe plus aucune référence sur un objet,
on est certains que le programme ne peut pas y
accéder, donc l’objet devient candidat au ramasse
miette.
ENSAM 2020-2021- T.HAJJI- 37
VI. Champs de classe
Considérons la définition simpliste suivante:
class A {
int n;
float y;
}
Chaque objet de type A possède ses propres champs n et x. Par
exemple avec les déclarations
A a1=new A(), a2= new A();
a1.n et a2.n désignent deux champs différents.
a1.y et a2.y désignent aussi deux champs différents.
ENSAM 2020-2021- T.HAJJI- 38
On peut définir des champs qui n’existent qu’en un seul
exemplaire (ce qu’on nomme des champs de classe ou
champs statiques), en les déclarant avec l’attribut static. Par
exemple si nous définissons:
class A {
static int n;
float y;
}
A a1=new A(), a2=new A();
a1.n et a2.n désignent donc le même champs. Il est possible de
s’y référer en le nommant simplement
A.n // champs (statique) de la classe A
ENSAM 2020-2021- T.HAJJI- 39
VII. Quelques précisions
Identificateur:
• C’est une suite de caractères pour désigner les différentes
entités manipulés par un programme: variables, méthode,
classe, objet, …
• Il est formé de lettre et/ou de chiffres. Le premier caractère
est une lettre ou le caractère souligné (_).
Remarque:
- Tous les caractères sont significatifs.
- On fait la différence entre les majuscules et les minuscules.
- Mots clés: certains mots sont réservés par le langage et ne
peuvent pas être utilisés comme identificateur.
ENSAM 2020-2021- T.HAJJI- 40
Mise en page des programmes
La mise en page d’un programme Java est libre.
- Une instruction peut être étendue sur plusieurs lignes.
- Une ligne peut comporter plusieurs instructions
Emploi du code Unicode:
Java utilise le codage Unicode, qui est basé sur 2 octets (16 bits), par
conséquent on peut coder 65 536 symboles, ce qui permet de couvrir la
plus part des symboles utilisés dans le monde. Pour assurer la
portabilité, le compilateur commence par traduire le fichier source en
Unicode.
• On peut, par exemple, utiliser l’identificateur élément: int élément;
• On peut aussi utiliser le code Unicode d’un caractère (mais à éviter
pour la lisibilité) de la manière suivante: \uxxxx représente le caractère
ayant comme code Unicode la valeur xxxx.
Par exemple \u0041 représente le caractère A.
ENSAM 2020-2021- T.HAJJI- 41
Style de programmation non standard
- Mettre en Majuscule la première lettre des noms des classes.
Exemple:
class A () { //… }
- Si le nom de la classe est composée de plusieurs mots, ils
sont accolés (on ne les sépare pas par un trait bas) et la
première lettre de chaque mot est mise en majuscule.
Exemple:
class PremProg() { //…. }
- Pour le reste, méthodes, variables membres, noms des
références d’objets, le style retenu comme classe sauf que la
première lettre de l’identificateur est miniscule.
ENSAM 2020-2021- T.HAJJI- 42
Spécificateurs d’accès:
Il existe 3 mots clés pour spécifier l’accès au sein d’une classe:
public, private et protected.
• public: veut dire tout le monde
• private : veut dire que personne ne peut accéder à ces
définitions à part les méthodes interne de ce type.
• protected: se comporte comme private avec moins de
restriction. Une classe dérivée a un accès aux membres
protected mais pas aux membres private
• Accès par défaut, lorsqu’aucun de ces spécificateurs n’est
mentionné.
ENSAM 2020-2021- T.HAJJI- 43
Exemple:
class A {
public int x;
private int y;
public void initialise (int i, int j){
x=i;
y=j;
}
}
public class TestA{ // fichier source de nom TestA.java
public static void main (String args[]) {
A a;
a=new A(); // On peut aussi déclarer A a=new A();
a.initialise(1,3); // x vaut 1 et y vaut 3
a.x=2; // x vaut maintenant 2. On peut accéder à x car il est public
a.y=3; // ne compile pas car y est privée
}
}
ENSAM 2020-2021- T.HAJJI- 44
Ch. II. Les constructeurs
I. Introduction
Un objet a
– une adresse en mémoire (identifie l’objet)
– un comportement (ou interface)
– un état interne (les valeurs des variables)
• L’état interne est donné par des valeurs de variables
• Le comportement est donné par des fonctions ou
procédures, appelées méthodes
ENSAM 2020-2021- T.HAJJI- 45
Nous supposons qu’un objet de type Point est représenté par:
Un état interne : deux coordonnées entières l’abscisse (x) et
l’ordonnée (y).
Un Comportement: nous supposons que la classe dispose des
trois méthodes:
- initialise pour attribuer des valeurs aux coordonnées d’un
points
- déplace pour modifier les coordonnées d’un point
- affiche pour afficher les coordonnées d’un point
ENSAM 2020-2021- T.HAJJI- 46
public class Point{
private int x; // abscisse
private int y; // ordonnée
public void initialise (int abs,int ord){
x=abs;
y=ord;
}
public void deplace (int dx, int dy){
x+=dx;
y+=dy;
}
public void affiche (){
System.out.println(" abscisse : "+ x + " ordonnée : " + y);
}
}
ENSAM 2020-2021- T.HAJJI- 47
• Pour manipuler un objet, on déclare une référence sur la
classe de cet objet :
Exemple: Point a;
• Quand unPour créer un objet (instance de la classe),
on applique l'opérateur new.
Exemple: a = new Point();
• Une instance est créée,
- son état est conservé dans les variables d’instance
(son propre état interne)
- partage le code qui détermine son comportement
(les méthodes) avec les autres instances de la classe.
ENSAM 2020-2021- T.HAJJI- 48
Ainsi
a=new Point();
// créer un objet de type Point et place sa référence dans a.
Les objets interagissent en s’envoyant des messages: Les méthodes d’un
objet correspondent aux messages qu’on peut lui envoyer : quand un
objet reçoit un message, il exécute la méthode correspondante.
De manière générale on a:
objetQuiReçoitLeMessage.messageEnvoyé(paramètres);
Exemple des objets de type point:
a.initialise(1,1);
a.deplace(0,2);
ENSAM 2020-2021- T.HAJJI- 49
public class TstPoint{
public static void main (String args[]) {
Point a;
a=new Point();
a.initialise(1,1);
a.deplace(2,0);
Point b=new Point();
b.initialise(2,3);
b.affiche();
}
}
ENSAM 2020-2021- T.HAJJI- 50
Remarque:
- Pour chaque instance on appelle la méthode initialise pour
initialiser l’objet ainsi créer.
- Si on n’appelle pas la méthode initialise, l’objet de type Point
ainsi créer ne sera pas initialisé.
Créer un objet, ne garanti pas son initialisation
ENSAM 2020-2021- T.HAJJI- 51
II. Définitions de Constructeur
On peut garantir l’initialisation d’un objet grâce à une
méthode spéciale appelée constructeur qui permet
d’automatiser le mécanisme d’initialisation d’un objet.
Quand une classe possède un constructeur, Java l’appel
automatiquement à toute création d’objet avant qu’il ne
puisse être utilisé.
La création et l’initialisation sont deux concepts unifiés
ENSAM 2020-2021- T.HAJJI- 52
• Chaque classe a un ou plusieurs constructeurs qui servent à
– créer les instances
– initialiser l’état de ces instances
• Un constructeur est une méthode qui:
– porte le même nom que la classe
– n’a pas de type retour
– peut disposer d’un nombre quelconque d’arguments
(éventuellement aucun).
Exemple:
Prenons l’exemple de la classe point et transformons la
méthode initialise en un constructeur en la nommant Point.
ENSAM 2020-2021- T.HAJJI- 53
public class Point{
private int x; // abscisse
private int y; // ordonnée
public Point (int abs, int ord) { // Constructeur, remplace initialise
x=abs;
y=ord;
}
public void deplace (int dx, int dy){
x+=dx;
y+=dy;
}
public void affiche (){
System.out.println(" abscisse : "+ x + " ordonnée : " + y);
}
}
ENSAM 2020-2021- T.HAJJI- 54
Les instructions:
Point a=new Point();
a.initialise(1,1)
Sont remplacées par
Point a = new Point(1,1); // crée l’instance et l’initialise
On peut aussi écrire:
Point a;
a=new Point(1,1);
Attention.
Dans la dernière version de la définition de la classe Point, l’instruction
Point a = new Point();
ne convient plus car le constructeur a besoin de deux arguments
ENSAM 2020-2021- T.HAJJI- 55
Un autre exemple
public class Etudiant {
private String nom, prenom; // Variables d’instance
private int codeNatEtudiant;
// Constructeur
public Etudiant(String n, String p) {
nom = n;
prenom = p;
}
public void setCNE (int cne) {
codeNatEtudiant = cne;
}
public static void main(String[] args) {
Etudiant e1;
e1 = new Etudiant("Mohammed", "Ali"); // création d'une instance de la classe Etudiant
e1.setCNE(23456765);
}
}
ENSAM 2020-2021- T.HAJJI- 56
III. Quelques règles concernant les constructeurs
1. Aucun type, même void, ne doit figurer devant son nom.
2. Lorsque le code d’une classe ne comporte pas de
constructeur, un constructeur par défaut sera
automatiquement ajouté par Java.
Dans ce cas on peut créer des instances (objets) de la classe
par des instructions telles que:
Point a=new Point(); //ok si Point n’a pas de constructeur
Etudiant e=new Etudiant(); //ok si Etudiant n’a pas de constructeur
ENSAM 2020-2021- T.HAJJI- 57
De manière générale:
Si pour une classe A donnée, l’instruction suivante est acceptée
A a=new A();
Cela signifie que:
- Soit A ne possède pas de constructeur
- Soit A possède un constructeur sans arguments.
ENSAM 2020-2021- T.HAJJI- 58
3.Un constructeur ne peut pas être appelé de la même manière que les
autres méthodes. Par exemple:
Point a=new Point(1,1); // ok car Point possède un constructeur
e1.Etudiant(("Mohammed", "Ali"); // Interdit car Etudiant possède
// un constructeur.
4. Un constructeur peut être déclaré private. Dans ce cas il ne pourra plus
être appelé de l’extérieur, c’est-à-dire il ne pourra pas être utiliser pour
instancier des objet. Exemple:
class A{
private A() { … } // constructeur privée sans arguments
…
}
…
A a=new A(); // erreur, le constructeur correspondant A() est privé
ENSAM 2020-2021- T.HAJJI- 59
Dans les exemples précédents, le constructeur initialise les
champs privés de l’objet.
Remarque: les champs d’un objet sont toujours initialisés par
défaut, contrairement à ce qui se passe pour les variables
locales, .
La création d’un objet entraîne toujours, par ordre chronologique,
les opérations suivantes:
- Initialisation par défaut de tous les champs de l’objet
- Une initialisation explicite lors de la déclaration du champ
- L’exécution des instructions du corps du constructeur
ENSAM 2020-2021- T.HAJJI- 60
Initialisation par défaut des champs d’un objet:
------------------------------------ |---------------------------------
Type du champ | Valeur par défaut
------------------------------------ |--------------------------------
boolean | false
char | caractère de code nul
int, byte, short, int long | 0
float, double | 0.f ou 0.
class | null
---------------------------------------------------------------------
ENSAM 2020-2021- T.HAJJI- 61
Initialisation explicite des champs d’un objet:
Une variable locale peut être initialiser lors de sa déclaration. Il
en va de même pour un champs. Considérons:
class A{
public A (…) {… } // Constructeur A
…
private int n=10;
private int p;
}
ENSAM 2020-2021- T.HAJJI- 62
L’instruction suivante
A objA=new A(…);
Entraîne successivement
- l’initialisation implicite des champs n et p de l’objet objA à 0
- L’initialisation (explicite) du champ n à la valeur figurant
dans sa déclaration, soit 10
- L’exécution des instruction du constructeur
ENSAM 2020-2021- T.HAJJI- 63
Appel du constructeur
Le corps du constructeur n’est exécuté qu’après
l’initialisation par défaut et l’initialisation explicite.
ENSAM 2020-2021- T.HAJJI- 64
class A {
public A() { // ici n vaut 20, p vaut 10 et np vaut 0
np=n*p;
n=5;
}
public void affiche(){
System.out.println("n="+n+" p="+p+" np= " + np)
}
private int n=20, p=10;
private int np;
}
public class Init {
public static void main (String args[]) {
A a=new A();
// ici a.n vaut 5, a.p vaut 10, mais a.np vaut 200
a.affiche();
}
}
Résultat de l’appel de la méthode affiche: n=5 p=10 np = 200
ENSAM 2020-2021- T.HAJJI- 65
IV. Surcharge des méthodes
On parle de surcharge (en anglais overload) lorsqu’un
même symbole possède plusieurs significations
différentes entre lesquelles on choisit en fonction du
contexte. Par exemple a+b, la signification du
symbole + dépend du type des variables a et b.
• En Java et C++, on peut surcharger une
méthode, c’est-à-dire, ajouter une méthode qui
a le même nom mais pas la même signature
qu’une autre méthode :
ENSAM 2020-2021- T.HAJJI- 66
Considérons l’exemple suivant ou la classe Etudiant est dotée
de 2 méthodes calcMoyenne:
- La première a deux arguments de ype float
- La deuxième a quatre arguments de type float
ENSAM 2020-2021- T.HAJJI- 67
class Etudiant {
private String nom, prenom; // Variables d’instance
private int codeNatEtudiant;
// Constructeur
public Etudiant(String n, String p) {
nom = n;
prenom = p;
}
public void setCNE (int cne) {
codeNatEtudiant = cne;
}
public float calcMoyenne(float m1, float m2) {
return (m1+m2)/2;
}
public float calcMoyenne(float m1, float m2, float m3, float m4) {
return (m1+m2+m3+m4)/4;
}
}
ENSAM 2020-2021- T.HAJJI- 68
public class TestEtudiant {
public static void main(String[] args) {
Etudiant e1;
e1 = new Etudiant("Mohammed", "Ali"); //
e1.setCNE(23456765);
System.out.println(“Moy1="+e1.calcMoyenne(10, 12));
System.out.println(“Moy2="+e1.calcMoyenne(10, 12, 8, 13));
}
}
ENSAM 2020-2021- T.HAJJI- 69
Considérons maintenant l’exemple suivant ou la classe Point
est dotée de 3 méthodes déplace:
- La première a deux arguments de type int
- La deuxième a un seul argument de type int
- La troisième a un seul argument de type short
ENSAM 2020-2021- T.HAJJI- 70
class Point {
public Point (int abs, int ord){ // Constructeur
x=abs; y=ord;
}
pubic void deplace (int dx, int dy) {
x+=dx; y+=dy;
}
pubic void deplace (int dx) {
x+=dx;
}
pubic void deplace (short dx) {
x+=dx;
}
private int x,y;
}
ENSAM 2020-2021- T.HAJJI- 71
public class Surdef1 {
public static void main(String arg[]) {
Point a=new Point(1,2);
a.deplace(1,3); // appel deplace (int,int)
a.deplace(2); // appel deplace (int)
short p=3;
a.deplace(p); // appel deplace (short)
byte b=2;
a.deplace(b); /* appel deplace(short) après
conversion de b en short */
}
}
ENSAM 2020-2021- T.HAJJI- 72
Il peut y avoir ambiguïté
Supposons que la classe Point est dotée des deux méthodes deplace
suivantes:
public void deplace(int dx, byte dy) {
x+=dx; y+=dy;
}
public void deplace(byte dx, int dy) {
x+=dx;
}
Considérons alors les instructions suivantes:
Point a =new Point(2,4)
int n; byte b;
a.deplace(n,b); // ok appel de deplace (int,byte)
a.deplace(b,n); // ok appel de deplace (byte,n)
a.deplace(b,b); // erreur, ambiguité
ENSAM 2020-2021- T.HAJJI- 73
• En Java, il est interdit de surcharger une méthode en
changeant le type de retour uniquement:
– c’est-à-dire ne différencier les 2 méthodes que par
leur type retour
Par exemple, one peut pas avoir les 2 méthodes
suivante dans une classe :
int moyenne(int, int); // division entière par 2
float moyenne(int,int); // moyenne réelle
ENSAM 2020-2021- T.HAJJI- 74
Surcharge des constructeurs (Plusieurs constructeurs)
En Java et C++, une classe peut avoir plusieurs constructeurs. Comme le nom
du constructeur est identique du nom de la classe, il ne peut y avoir qu’un seul
nom de constructeur. Par contre on peut avoir différentes initialisations.
class Etudiant {
private String nom, prenom; // Variables d’instance
private int codeNatEtudiant;
public Etudiant(String n, String p) { // Constructeur
nom = n;
prenom = p;
}
public Etudiant(String n, String p, int cne) {// Constructeur
nom = n;
prenom = p;
codeNatEtudiant = cne;
}
}
ENSAM 2020-2021- T.HAJJI- 75
public class TestEtudiant {
public static void main(String[] args) {
Etudiant e1, e2;
e1 = new Etudiant("Mohammed", "Ali");
e2 = new Etudiant("Ouardi", " fatima", 22564321);
}
}
ENSAM 2020-2021- T.HAJJI- 76
V. Autoréférences: emploi de this
Il se peut qu’au seine d’une méthode, on ait besoin de faire
référence à l’objet ayant appelé cette méthode.
class A {
….
public void f(….) {
…… // ici l’emploi de this désigne la référence à l’objet
// ayant appelé la méthode f
}
}
ENSAM 2020-2021- T.HAJJI- 77
Exemple 1:
class Point {
public Point (int abs, int ord){
x=abs; y=ord;
}
…
private int x, y;
}
Comme les identificateurs abs et ord sont des arguments
muets pour la méthode Point, alors on peut les noter x et y
qui n’ont rien avoir avec les champs private x et y. Dans
ce cas la classe Point peut s’écrire comme suit:
ENSAM 2020-2021- T.HAJJI- 78
class Point {
public Point (int x, int y){
this.x=x; //this.x permet d’accéder au champs x de l’objet courant
this.y=y; //this.y permet d’accéder au champs y de l’objet courant
}
…
private int x,y;
}
ENSAM 2020-2021- T.HAJJI- 79
Exemple 2:
class Etudiant {
private String nom, prenom;
public Etudiant(String nom, String prenom) {
this.nom = nom;
this.prenom = prenom;
}
ENSAM 2020-2021- T.HAJJI- 80
Exemple 3:
Deux objets de même classe Point coïncident si leurs
coordonnées sont identiques. On peut écrire la méthode coïncide
de la façon suivante:
pubic boolean coincide (Point pt) {
return ((pt.x==this.x) && (pt.y==this.y))
}
Point a=new Point(x1,y1);
Point b=new Point(x2,y2);
Les point a est b sont identiques si: a.coincide(b) est TRUE
ENSAM 2020-2021- T.HAJJI- 81
Utilisation du mot clé this dans un constructeurs
Il est possible, qu’au sein d’un constructeur, on peut
appeler un autre constructeur de la même classe.
Pour faire on fait appel au mot clé this qui est
utilisé cette fois comme nom de méthode.
Supposons que la classe Point admet deux
constructeurs. Un constructeur initialise à l’origine
(au point (0,0)) et un autre initialise à un point
quelconque.
ENSAM 2020-2021- T.HAJJI- 82
Version 1: La classe Point peut être définie de la façon suivante:
class Point {
public Point (int abs, int ord){
x=abs; y=ord;
}
public Point( ) {
x=0;
y=0;
}
…
private int x,y;
}
Point a = new Point (); // créer l’objet a et l’initialise à l’origine,
// au point (0,0).
Point b = new Point (2,3); // créer l’objet b et l’initialise au point (2,3).
ENSAM 2020-2021- T.HAJJI- 83
Version 2: la classe Point peut être définie de la façon suivante:
class Point {
public Point (int abs, int ord){
x=abs; y=ord;
}
public Point( ) {
this (0,0);
}
…
private int x,y;
}
Point a = new Point (); // créer l’objet a et l’initialise à l’origine,
// au point (0,0).
Point b = new Point (2,3); // créer l’objet b et l’initialise au point (2,3).
ENSAM 2020-2021- T.HAJJI- 84
public class feuille {
int i=0;
feuille increment () {
i++;
return this;
}
public static void main (String[] args ) {
feuille x=new feuille ();
System.out.println("ii = " + x.increment().increment().increment().i );
}
}
Sortie:
i= 3
ENSAM 2020-2021- T.HAJJI- 85
public class feuille {
int i=0;
feuille increment () {
i++;
return this;
}
void print () {
System.out.println(" i = " + i);
}
public static void main (String[] args ) {
feuille x=new feuille ();
x.increment().increment().increment().print( )
}
}
Sortie:
i= 3
ENSAM 2020-2021- T.HAJJI- 86
Ch. III. Généralités sur les structures lexicales de Java
Types de variables
• Les variables d’instances
– sont déclarées en dehors de toute méthode
– conservent l’état d’un objet, instance de la classe
– sont accessibles et partagées par toutes les méthodes de la classe
• Les variables locales
– sont déclarées à l’intérieur d’une méthode
– conservent une valeur utilisée pendant l’exécution de la méthode
– ne sont accessibles que dans le bloc dans lequel elles ont été
déclarées
ENSAM 2020-2021- T.HAJJI- 87
Identificateurs
Un identificateur Java est une chaîne de caractères de
longueur quelconque:
– Le premier caractère est une lettre Unicode
– Le reste de la chaîne peut contenir des lettres et/ou des
chiffres et/ou le caractère souligné « _ »
– Les minuscules et les majuscules sont différenciés
Un identificateur Java ne doit pas être un mot-clé ou les
constantes true, false ou null
ENSAM 2020-2021- T.HAJJI- 88
L’affectation
• L’affectation est réalisée au moyen de l’opérateur « = ».
Signifie prendre la valeur du coté droit et la copier du coté
gauche.
• La partie droite est une constante, une variable ou une
expression qui retourne une valeur. La partie gauche est une
variable.
variable = constante ou variable ou expression
ENSAM 2020-2021- T.HAJJI- 89
• Soient A et B de type primitif
A=B; signifie que le contenu de B est copié dans A.
Si ensuite on modifie le contenu de A, alors le contenu de B
ne sera pas modifié.
• Soient A est B deux objets,
A=B; signifie qu’on copie la référence de B dans A. (A et B
pointeront vers le même objet référencé par B).
Si ensuite le contenu de A ou de B sont modifiés alors le
contenu de A et de B sont modifiés.
ENSAM 2020-2021- T.HAJJI- 90
Exemple:
class A {
public int i;
}
public class TestAffectation {
public static void main(String[] args) {
A a=new A(); A b=new A();
a.i=6; b.i=11;
a=b; // a et b contiennent la même référence, on a: a.i=11
a.i=20; // b.i et a.i valent 20
b.i=13; // a.i et b.i valent 13.
}
}
ENSAM 2020-2021- T.HAJJI- 91
Types de données en Java
• 2 grands groupes de types de données sont manipulés par
Java:
– types primitifs
– objets (instances de classe)
ENSAM 2020-2021- T.HAJJI- 92
Types primitifs
• Boolean:
prend deux valeurs true ou false
• Nombres entiers :
byte: représenté sur 1 octet,
short: représenté sur 2 octets,
int : représenté sur 4 octets,
long : représenté sur 8 octets
• Nombres à virgule flottante :
float : représenté sur 4 octets),
double : représenté sur 8 octets
• Caractère (un seul caractère) :
char : codé sur 2 octet par le codage Unicode
ENSAM 2020-2021- T.HAJJI- 93
Nombres entiers
• byte : compris entre –128 et 127
• short : compris entre –32 768 et 32 767
• int : compris entre –2.147 483 648 et 2 147 483 647
• long : compris entre -923 372 036 854 775 808
et 923 372 036 854 775 807
ENSAM 2020-2021- T.HAJJI- 94
Constantes nombres
Une constante « entière »
- est de type int
- Si elle est suffixée par « L » alors elle est de type long
Exemple:
108 // constante de type int
89L // constante de type long
014 // 14 en octal (base 8)= 12 en décimal
0xA7 // A7 en hexadécimal (base 16) = 167 en décimal
ENSAM 2020-2021- T.HAJJI- 95
Types des résultats des calculs
avec des nombres entiers
• Tout calcul entre entiers donne un résultat de type int
• si au moins un des opérandes est de type long, alors le
résultat est de type long
Exemple
byte b1 = (byte)20;
byte b2 = (byte)15;
byte b3 = b1 + b2;
provoquera une erreur car b1 + b2 est de type int (codé sur
32 bits) alors que b3 est de type byte (codé sur 8 bits
seulement)
ENSAM 2020-2021- T.HAJJI- 96
Les nombres flottants
• float : environ 7 chiffres significatifs ;
– valeur absolue (arrondie) maximal 3,4 x 1038 ( constante prédéfinie
Float.MAX_VALUE)
– valeur absolue (arrondie) minimal 1,4 x 10-45 (constante prédéfinie
Float.MIN_VALUE)
• double : environ 15 chiffres significatifs ;
– valeur absolue maximal (arrondie) 1,8 x 10308 (constante prédéfinie
Double.MAX_VALUE)
– valeur absolue minimal (arrondie) 4,9 x 10-324 (constante prédéfinie
Double.MIN_VALUE)
ENSAM 2020-2021- T.HAJJI- 97
Constantes nombres
Une constante réelle est de type float si elle est suffixée
par « F » et de type double sinon
Exemple:
.567e2 // 5,67 de type double
5.123E-2F // 0,05123 type float
ENSAM 2020-2021- T.HAJJI- 98
Type caractère
• permet de manipuler des caractères: Java représente un caractère sur 2 octets
• Une variable de type caractère est déclaré par: char c1,c2;
• Constante de type caractère: ‘a’ , ‘E’
Les caractères disposant d’une notation spéciale.
Notation Code Unicode Abréviation usuelle Signification
\b 0008 BS(Back Space) Retour arrière
\t 0009 HT(Horizontal Tabulation) Tabulation horizontale
\n 000a LF(Line Feed) Saut de ligne
\f 000c FF(Form Feed) Saut de page
\r 000d CR(Cariage Return) Retour chariot
\" 0022
\’ 0027
\\ 005c
ENSAM 2020-2021- T.HAJJI- 99
Type booléen
Sert à représenter une valeur logique du type vrai/faux
Une variable de type booléen est déclaré par: boolean b;
b prend une des deux valeurs false ou true
ENSAM 2020-2021- T.HAJJI- 100
Constante null
• Référence inexistante (indique qu’une variable de
type non primitif ne référence rien) ; convient pour
tous les types non primitifs
– null
ENSAM 2020-2021- T.HAJJI- 101
Transtypage
• Java est un langage fortement typé
• Dans certains cas, il est nécessaire de forcer le
programme à considérer une expression comme étant
d’un type qui n’est pas son type réel ou déclaré
• On utilise pour cela le cast (transtypage) :
(type-forcé) expression
int x = 10, y = 3; double z;
z=x/y; // donne z=3.0; car x/y donne 3
// sin on veut que z=3.3333.. et pas 3.0
z = (double)x / y; // cast de x suffit
ENSAM 2020-2021- T.HAJJI- 102
Casts autorisés
• En Java, 2 seuls cas sont autorisés pour les casts :
– entre types primitifs,
– entre classes mère/ancêtre et classes filles.
ENSAM 2020-2021- T.HAJJI- 103
Casts entre types primitifs
• Un cast entre types primitifs peut occasionner une
perte de données :
conversion d'un int vers un short
• Un cast peut provoquer une simple perte de précision:
la conversion d'un long vers un float peut faire perdre
des chiffres significatifs mais pas l'ordre de grandeur
ENSAM 2020-2021- T.HAJJI- 104
Casts entre types primitifs
• Une affectation entre types primitifs peut utiliser un cast
implicite si elle ne provoque aucune perte
• De même, on peut affecter un entier à une variable de type
nombre à virgule flottante
• Sinon, elle doivent comporter un cast explicite
• L’oubli de ce cast explicite provoque une erreur à la
compilation
ENSAM 2020-2021- T.HAJJI- 105
Exemples de Casts
• short s = 1000000; // erreur !
• Cas particulier d’une affectation statique (repérable par le
compilateur) d’un int « petit » :
short s = 65; // pas d’erreur
• Pour une affectation non statique, le cast est obligatoire :
int i = 60;
short b = (short)(i + 5);
• Les casts de types « flottants » vers les types entiers tronquent
les nombres :
int i = (int)1.99; // i = 1, et pas 2
ENSAM 2020-2021- T.HAJJI- 106
Casts entre entiers et caractères
• Ils font correspondre un entier et un caractère qui a
comme code Unicode la valeur de l’entier
• La correspondance char → int, long s’obtient par
cast implicite
• Le code d’un char peut aller de 0 à 65.535 donc char
→ short, byte nécessite un cast explicite (short ne va
que jusqu’à 32.767)
• Les entiers sont signés et pas les char donc long, int,
short ou byte → char nécessite un cast explicite
ENSAM 2020-2021- T.HAJJI- 107
L’attribut final
L’attribut final indique que la valeur de la variable ne peut être
modifiée : on pourra lui donner une valeur une seule fois dans
le programme.
Variable d’instance final
• Une variable d’instance final est constante pour chaque objet.
Attention: elle peut avoir 2 valeurs différentes pour 2 objets
différents.
• Une variable d'instance final peut ne pas être initialisée à sa
déclaration mais elle doit avoir une valeur à la sortie de tous
les constructeurs
ENSAM 2020-2021- T.HAJJI- 108
Variable locale final
• Si la variable est d’un type primitif, sa valeur ne peut changer
• Si la variable référence un objet, elle ne pourra référencer un
autre objet mais l’état de l’objet pourra être modifié
final Etudiant e = new Etudiant("Mohammed", "Ali");
...
e.nom = "Ahmed"; // ok, changement de l’état e l’objet
e.setCNE(32211452); // ok, changement de l’état e l’objet
e = new Etudiant("Ahmed"); // Interdit, référence à un
// autre objet
ENSAM 2020-2021- T.HAJJI- 109
Instructions de contrôle
ENSAM 2020-2021- T.HAJJI- 110
Structure Alternative « if… else »
if (expressionBooléenne)
bloc-instructions ou instruction
else
bloc-instructions ou instruction
int x = y + 5;
if (x % 2 == 0) {
type = 0;
x++;
}
else
type = 1;
Un bloc serait préférable, même s’il n’y a qu’une seule instruction
ENSAM 2020-2021- T.HAJJI- 111
Expression conditionnelle
expressionBooléenne ? expression1 : expression2
Est équivalent
if (expressionBooléenne)
expression1;
else
expression2;
Exemple:
int y = (x % 2 == 0) ? x + 1 : x;
est équivalent à
int y;
if (x % 2 == 0)
y = x + 1;
else
y = x;
Parenthèses pas indispensables
ENSAM 2020-2021- T.HAJJI- 112
Distinction de cas suivant une valeur
switch(expression) {
case val1: instructions;
break; // Attention, sans break, les instructions du cas suivant
// sont exécutées !
...
case valn: instructions;
break;
default: instructions;
}
expression est de type char, byte, short, ou int, ou de type énumération
• S’il n’y a pas de clause default, rien n’est exécuté si expression ne
correspond à aucun case
ENSAM 2020-2021- T.HAJJI- 113
Exemple de switch
char lettre;
int nbVoyelles = 0, nbA = 0,
nbT = 0, nbAutre = 0;
...
switch (lettre) {
case 'a' : nbA++;
case 'e' : // pas d’instruction !
case 'i' : nbVoyelles++;
break;
case 't' : nbT++;
break;
default : nbAutre++;
}
ENSAM 2020-2021- T.HAJJI- 114
Répétitions « tant que »
while(expressionBooléenne)
bloc-instructions ou instruction
do bloc-instructions ou instruction
while(expressionBooléenne)
Le bloc d’instructions est exécuté au moins une fois
ENSAM 2020-2021- T.HAJJI- 115
Répétition for
for(init; test; incrément){
instructions;
}
est équivalent à
init;
while (test) {
instructions;
incrément
}
ENSAM 2020-2021- T.HAJJI- 116
Exemple de for
int somme = 0;
for (int i=0; i < n; i++) {
somme += tab[i];
}
System.out.println(somme);
ENSAM 2020-2021- T.HAJJI- 117
Instructions liées aux boucles
• break sort de la boucle et continue après la boucle
• continue passe à l’itération suivante
• break et continue peuvent être suivis d’un nom
d’étiquette qui désigne une boucle englobant la
boucle où elles se trouvent (une étiquette ne peut se
trouver que devant une boucle)
ENSAM 2020-2021- T.HAJJI- 118
Etiquette de boucles
boucleWhile: while (pasFini) {
...
for (int i=0; i < n; i++) {
...
if (t[i] < 0)
continue boucleWhile;
...
}
...
}
ENSAM 2020-2021- T.HAJJI- 119
Les tableaux
• En Java les tableaux sont considérés comme des
objets.
– les variables de type tableau contiennent des
références aux tableaux
– les tableaux sont créés par l’opérateur new
– ils ont une variable d’instance (final) pour
désigner taille du tableau:
final int length
ENSAM 2020-2021- T.HAJJI- 120
Déclaration et création des tableaux
• Déclaration : La taille n’est pas fixée à la déclaration
int[] tabNotes;
Ou
int tabNotes[]; // possible, mais non recommandé
Attention: En java il n'est pas possible de définir des tableaux de taille
statique à la déclaration.
int[10] tab; // ne compile pas
• Création : C’est pendant la création qu’il faut donner la taille du tableau
tabNotes = new int[5];
Remarques:
– Chaque élément du tableau reçoit la valeur par défaut du type de base
du tableau
– La taille ne pourra plus être modifiée par la suite
– Les éléments d’un tableau peuvent être d’un type primitif ou d’un type
objet
ENSAM 2020-2021- T.HAJJI- 121
Initialisation
On peut lier la déclaration, la création et l’initialisation d’un tableau;
Sa longueur est alors calculée automatiquement d’après le nombre de
valeurs données.
Attention : cette syntaxe n'est autorisée que dans la déclaration) :
int[] tab = {10, 3*6, 4};
Etudiant [] etudiantsSMI = {
new Etudiant("Mohammed", "Ali"),
new Etudiant("Fatima", "Zahra")
}
On peut affecter « en bloc » tous les éléments d'un tableau avec un
tableau anonyme
int[] t;
t = new int[] {1, 2, 3};
ENSAM 2020-2021- T.HAJJI- 122
Indices du tableau: Les indices du tableau commence à 0 et se
termine à tab.length - 1
Taille du tableau
int[] tab = {10, 3*6, 4};
tabNotes = new int[8];
int l1 = tab.length; // l1 = 3
int l2 = tabNots.length; // l2 = 8
int e = tabNotes[8];
/* La compilation produit le message suivant :
ArrayIndexOutOfBoundsException */
ENSAM 2020-2021- T.HAJJI- 123
Exemple de tableau de chaînes: Paramètres de la ligne de commande
class TestArguments {
public static void main(String[] args) {
for (int i=0; i < args.length; i++)
System.out.println(args[i]);
}
}
$javac TestArguments
$ java TestArguments arg1 arg2
affichera
• arg1
• arg2
ENSAM 2020-2021- T.HAJJI- 124
Tableaux d’objets
Les éléments d’un tableau peuvent être d’un type objet.
Attention: Il faut créer les objets avant de les utiliser. Considérons
l’exemple de la classe Etudiant.
Etudiants [] etudiantSMI = new Etudiant[100];
// Chaque élément du tableau contient une référence vers un objet de type Etudiant
etudiantSMI[0].setCNE(11225467);
// setCNE(int cne) est une méthode de la classe Etudiant.
etudianSMI[0].codeNatEtudiant = cne;
// Erreur: etudiantSMI[0] est objet de type Etudiant, donc il faut le créer avant de l’utiliser
Etudiant [] etudiantSMI = new Etudiant[100];
// Chaque élément du tableau est une référence à un objet de type Etudiant
etudiantSMI[0] = new Etudiant(); // Création de l’objet etudiantSMI[0]
etudiantSMI[0].setCNE(11225467);
ENSAM 2020-2021- T.HAJJI- 125
Affectation de Tableaux:
Java permet de manipuler globalement les tableaux par
affectation de leurs références
Soient t1 et t2 deux tableaux
t1=t2; // La référence contenue dans t2 est affectée à t1
Maintenant t1 et t2 désignent le même objet tableau qui était
initialement référencé par t2
t1[2]=3; // t2[2] vaut aussi 3 t2[2]=3.
t2[4]=6; // t1[4] vaut aussi 6 t1[4]=6;
Attention: Si on veut copier uniquement les valeurs de t2 dans
t1, alors dans ce cas on peut faire une copie élément par
élément
ENSAM 2020-2021- T.HAJJI- 126
Les tableaux en argument d’une méthode:
Le passage des tableaux comme paramètres des méthodes se
fait par référence (comme les objets) et non par copie
(comme les types primitifs)
La méthode agit directement sur le tableau et non sur sa copie
ENSAM 2020-2021- T.HAJJI- 127
Tableaux à plusieurs dimensions
• Déclaration
int[][] notes; // c’est un tableau de tableaux
Chaque élément du tableau contient une référence vers un
tableau
• Création: Il faut donner au moins les premières dimensions
notes = new int[30][3]; // Tableau de 30 étudiants, chacun
// des 30 étudiants a au plus 3 notes
notes = new int[30][]; // Chacun des 30 étudiants a un
// nombre de notes variable
ENSAM 2020-2021- T.HAJJI- 128
Déclaration, création et initialisation
int[][] notes = { {10, 11, 9}, // 3 notes
{15, 8}, // 2 notes
...
};
Affectation
• notes[10][2] = 12;
ENSAM 2020-2021- T.HAJJI- 129
Exemple
int[][] t;
t = new int[2][];
int[] t0 = {0, 1};
t[0]= t0; // t[0] est un tableau
t[1] = new int[] {2, 3, 4, 5}; // Déclaration et initialisation
for (int i = 0; i < t.length; i++) {
for (int j = 0; j < t[i][].length; j++) {
System.out.print(t[i][j] + "; ");
}
System.out.println();
}
ENSAM 2020-2021- T.HAJJI- 130
Ch. IV. Héritage
L'héritage est une notion fondamentale en Java et de
manière générale dans les langages de
programmation par Objets.
Il permet de créer des classes dérivées (classes qui
héritent) par combinaison de classes déjà existantes
(classes de base) sans toucher au code source de la
classe de base (on a seulement besoin du code
compilé de la classe de base).
ENSAM 2020-2021- T.HAJJI- 131
La classe dérivée:
– se comporte comme la classe de base mais avec
quelques différences.
– on a seulement besoin du code compilé de la
clase de base.
On peut par exemple:
– ajouter de nouvelles méthodes
– adapter (modifier) certaines méthodes
ENSAM 2020-2021- T.HAJJI- 132
Syntaxe:
class <ClasseDerivee> extends <ClasseDeBase>
Interprétation:
Permet de définir un lien d'héritage entre deux classes:
- La classe <ClasseDeBase> est le nom de la classe de base. Elle
s'appelle une classe mère, une classe parente ou une super-classe. Le
mot clef extends indique la classe mère :
- La classe <ClasseDerivee> est le nom de la classe dérivée. Elle hérite
de la classe <ClasseDeBase> . On l'appelle une classe fille ou une sous-
classe
Remarque:
- Par défaut il n’y a pas de extends dans la définition d’une classe: une
classe hérite de la classe Object
- Un objet de la classe dérivée ClassDerivee accède aux membres publics de
sa classe de base ClasseDeBase
ENSAM 2020-2021- T.HAJJI- 133
Exemple
class FormeGeometrique{ // classe mère
// Définition des attributs et des méthodes
}
class Point extends FormeGeometrique { // classe dérivée
// Définition de nouveaux attributs (propres à la classe Point)
// Définition de nouvelles méthodes (propres à la classe Point)
// Modification des méthodes qui sont déjà définies dans la classe mère
// Point hérite des méthodes et attributs de la superclasse
FormeGeometrique
}
class Cercle extends Point {
Définition de nouveaux attributs (propres à la classe Cercle)
// Définition de nouvelles méthodes (propres à la classe Cercle)
// Modification des méthodes qui sont déjà définies dans la classe mère
// Cercle hérite des méthodes et attributs de la superclasse Point
}
ENSAM 2020-2021- T.HAJJI- 134
Une instance (un objet) d’une classe dérivée peut faire
appelle (accès depuis l’extérieur)
- Aux méthodes et aux membres publics de la classe
dérivée
- Aux méthodes et membres publics de la classe de
base
De même (pour l’accès depuis l’intérieur)
- Une méthode d’une classe dérivée a accès aux
membres publics de sa classe de base
- Une méthode d’une classe dérivée n’a pas accès aux
membres privées de sa classe de base
ENSAM 2020-2021- T.HAJJI- 135
Quand on écrit la classe <ClasseDerivee> on doit seulement:
– écrire le code (variables ou méthodes) lié aux nouvelles
possibilités: offrir de nouveaux services.
– redéfinir certaines méthodes: enrichir les services rendus
par une classe.
ENSAM 2020-2021- T.HAJJI- 136
Lorsqu'une classe hérite d'une autre classe
Elle bénéficie automatiquement des définitions des
attributs et des méthodes de la classe mère.
Elle peut y ajouter ses propres définitions.
• ajouter des variables, des méthodes et des
constructeurs.
• redéfinir des méthodes: exactement le même
nom et la même signature.
• surcharger des méthodes: même nom mais pas
les mêmes arguments (signature).
ENSAM 2020-2021- T.HAJJI- 137
Remarques2:
- Java ne permet pas l’héritage multiple: chaque classe a
une et une seule classe mère dont elle hérite les
variables et les méthodes.
- C++ permet l’héritage multiple.
ENSAM 2020-2021- T.HAJJI- 138
Construction des classes dérivée
- Lorsqu'on construit une instance de ClasseDerivee, on obtient
un objet dont une partie est construite grâce à la définition de
ClasseDerivee et une partie grâce à la définition de la
super-classe ClasseDeBase.
- En Java, le constructeur de la classe drivée doit prendre en
charge l’intégralité de la construction de l’objet.
Le constructeur de la classe dérivée doit faire appel au
constructeur de la classe de base en appelant explicitement
la méthode super() ainsi que la liste des paramètres
appropriés.
Remarque: l’appel du constructeur de la super classe (appel de
la méthode super()) doit être la première instruction dans la
définition du constructeur de la classe dérivée.
ENSAM 2020-2021- T.HAJJI- 139
Exemple 1:
class ClasseDeBase{
public ClasseDeBase(int i) { // constructeur de la classe de base
System.out.println(" Classe de Base: " + i);
}
}
class ClasseDerivee1 extends ClasseDeBase {
public ClasseDerivee1(int i, int j) { // constructeur de la classe dérivée 1
super(i+j); // appel du constructeur ClasseDeBase
System.out.println(" Classe dérivée1: " + i+ " , "+j);
}
}
class ClasseDerivee2 extends ClasseDerivee1{
public ClasseDerivee2(int i) { // constructeur de la classe dérivée 2
super(i,i); // appel du constructeur ClasseDerivee1
System.out.println(" Classe dérivée2: " + i);
}
}
ENSAM 2020-2021- T.HAJJI- 140
public class TestHeritage{
public static void main (String args[]) {
ClasseDerivee2 a=new ClasseDerivee2(7);
}
}
Sortie:
Classe de Base : 14
Classe dérivée1: 7 , 7
Classe dérivée2: 7
ENSAM 2020-2021- T.HAJJI- 141
Exemples des cas possibles:
Exemple 1:
class ClasseDeBase{
ClasseDeBase(arguments) {
…
}
…
}
class ClasseDerivee extends ClasseDeBase {
// Pas de constructeur
….
}
On obtient une erreur de compilation. Un constructeur de la classe dérivée
doit être défini est qui doit appeler le constructeur de la classe de base.
ENSAM 2020-2021- T.HAJJI- 142
Exemple2:
class ClasseDeBase{
ClasseDeBase(arguments) { // constructeur de la classe de base
…
}
}
class ClasseDerivee extends ClasseDeBase {
ClasseDerivee(arguments) { // constructeur de la classe de base
// l’appel de super () est obligatoire sinon une erreur
….
}
}
ENSAM 2020-2021- T.HAJJI- 143
Exemple 3:
class ClasseDeBase{
ClasseDeBase(arguments) { // constructeur de la classe de base
…
}
ClasseDeBase() { // constructeur sans paramètre de la classe de base
…
}
}
class ClasseDerivee extends ClasseDeBase {
ClasseDerivee(arguments) {// constructeur de la classe dérivée
// l’appel de super () n’est pas obligatoire.
// si super() n’est pas appelé explicitement,
// le constructeur par défaut de ClasseDeBase est appelé
….
}
}
ENSAM 2020-2021- T.HAJJI- 144
Exemple 4:
class ClasseDeBase{
// Pas de constructeur
…
}
class ClasseDerivee extends ClasseDeBase {
// Pas de constructeur
….
}
La création d’un objet de type ClasseDerivee entraîne l’appel
de constructeur par défaut qui appelle le constructeur par
défaut de la ClasseDeBase.
ENSAM 2020-2021- T.HAJJI- 145
Exemple pratique:
class ClasseDeBase{
ClasseDeBase() {
System.out.println(" Classe de Base: ");
}
}
class ClasseDerivee1 extends ClasseDeBase {
ClasseDerivee1() {
// Appel implicite du constructeur par défaut de ClasseDeBase
System.out.println(" Classe dérivée1: ");
}
}
class ClasseDerivee2 extends ClasseDerivee1{
ClasseDerivee2() {
// Appel implicite du constructeur par défaut de ClasseDerivee1
System.out.println(" Classe dérivée2: " );
}
}
ENSAM 2020-2021- T.HAJJI- 146
public class TestHeritage{
public static void main (String args[]) {
ClasseDerivee2 a=new ClasseDerivee2();
}
}
Sortie:
Classe de Base :
Classe dérivée1:
Classe dérivée2:
ENSAM 2020-2021- T.HAJJI- 147
Résumé:
Si on n'appelle pas le constructeur de la super-classe,
le constructeur par défaut est utilisé si:
- aucun constructeur n’est défini
- au moins un constructeur sans paramètre est
défini
sinon le compilateur déclare une erreur
ENSAM 2020-2021- T.HAJJI- 148
Notion de la Redéfinition
Ne pas confondre redéfinition et surcharge des
méthodes :
– on redéfinit une méthode quand une nouvelle
méthode a le même nom et la même signature
qu’une méthode héritée de la classe mère.
– on surcharge une méthode quand une nouvelle
méthode a le même nom, mais pas la même
signature, qu’une autre méthode de la même classe
ENSAM 2020-2021- T.HAJJI- 149
- Lorsqu'un attribut ou une méthode ont été définis dans une
classe et sont redéfinis dans une classe dérivée (qui en
hérite), les éléments visibles sont ceux redéfinis dans la
classe dérivée. Les éléments de la classe de base (héritée)
sont alors masqués.
class Rect extends ObjetGraphique {
void move(int dx, int dy);
...
}
class Cercle extends ObjetGraphique{
void move(int dx, int dy);
...
}
ENSAM 2020-2021- T.HAJJI- 150
- On peut avoir les mêmes méthodes move() dans
des classes héritant les unes des autres. Dans ce
cas, c'est la classe la plus dérivée de l'objet qui
détermine la méthode à exécuter, sans que le
programmeur ait à faire des tests sur le type de
l'objet à traiter.
ENSAM 2020-2021- T.HAJJI- 151
A* *: signifie la définition ou la
redéfinition d’une méthode f.
Class A: méthode de A
B Classe B: méthode de A
C* Classe D: méthode de D
Classe E: méthode de A
Classe C: méthode de C
Classe F: méthode de C
E
D* F
ENSAM 2020-2021- T.HAJJI- 152
- la redéfinition d'une méthode d'une classe consiste à fournir
dans une sous-classe une nouvelle implémentation de la
méthode. Cette nouvelle implémentation masque alors
complètement celle de la super-classe
- Grâce au mot-clé super, la méthode redéfinie dans la
sous-classe peut réutiliser du code écrit dans la méthode de la
super-classe, qui n'est plus visible autrement.
- super à un sens uniquement dans une méthode (comme le
mot-clé this).
ENSAM 2020-2021- T.HAJJI- 153
class Point {
public void afficher(){
System.out.pritln(" je suis en "+ x +" et " +y);
}
private int x,y;
}
class PointCol extends Point {
public void afficher() { // redéfinition de la méthode afficher
super.afficher(); // appel de la méthode afficher de la classe de base
System.out.println (" et ma couleur est: "+ couleur);
}
private byte couleur;
}
ENSAM 2020-2021- T.HAJJI- 154
Soit une classe B qui hérite d'une classe A. Soit f une
méthode définie dans A. Dans une méthode d'instance
g() de B, super. sert à désigner un membre de A.
par exemple: super.f() désigne la méthode f() de A
Attention: On ne peut remonter plus haut que la classe
mère pour récupérer une méthode redéfinie :
– pas de « super.super.»
ENSAM 2020-2021- T.HAJJI- 155
Exemple:
class A {
public void f(){
System.out.println("Je suis dans la classe de base A");
}
}
class B extends A {
public void g(){
super.f(); // désigne la méthode f() de la classe mère A
System.out.println("je suis aussi dans la class derivee B");
}
}
public class TestRedefinition {
public static void main (String args[]) {
B b = new B();
b.g();
}
}
ENSAM 2020-2021- T.HAJJI- 156
Héritage (Suite)
• Une Classe final ne peut pas avoir de classes filles
• La redéfinition d'une méthode public ne peut être
private
• Une méthode final ne peut pas être redéfinie
ENSAM 2020-2021- T.HAJJI- 157
Sous-type
Le type B est un sous-type de A si on peut affecter une
expression de type B dans une variable de type A.
Les type primitifs
- int est un sous type de float
- float est un sous type de double
- …
Les objets
Les sous-classes d’une classe A sont des sous types de A.
Dans ce cas on peut écrire:
A a = new B(…); // la variable a est de type A, alors que
// l’objet référencé par a est de type B.
A aa; B b=new B();
aa=b; // aa de type A, référence un objet de type B
ENSAM 2020-2021- T.HAJJI- 158
Définitions:
- La classe (ou le type) réelle de l’objet est la classe du
constructeur qui a créé l’objet
Exemple: Soit B une sous classe de A
A a = new B(…); // B est la classe (type) réelle de l’objet a
- Le type (la clase) déclaré de l’objet est le type qui est
donné au moment de la déclaration de la variable qui
référence l’objet.
Exemple: Soit B une sous classe de A
A a = new B(…); // A est le type déclaré de l’objet a
ENSAM 2020-2021- T.HAJJI- 159
Cas des tableaux
Soit B une classe qui hérite de la classe A, alors on peut écrire :
A[] tab = new B[5];
Attention: Dans tab[] il faut les valeurs de type réel et non
celles du type déclaré (c’est à dire des valeur de type B et non
de type A). Par exemple, si on a les instructions suivantes:
A[] tab = new B[5];
A a = new A();
tab[0] = a;
• Passe à la compilation mais provoquera une erreur à
l’exécution car tab[0] reçoit une valeur de type A et
non une valeur de type B
ENSAM 2020-2021- T.HAJJI- 160
Polymorphisme
Exemple introductif:
Considérons l’exemple suivant où B est une classe qui hérite de la classe A. Soient f() une méthode
qui reédinie dans B et g une méthode définie dans A. On suppose que
class A {
public void f(){
System.out.println("Methode f(): Classe de base A");
}
public void g(){
System.out.println("Methode g(): Classe de base A");
}
}
class B extends A {
public void f(){
System.out.println("Methode f(): Classe B derivee de A");
public void h(){
System.out.println("Methode h(): Classe B derivee de A");
}
}
ENSAM 2020-2021- T.HAJJI- 161
A a=new A(); B b = new B();
a.f(); // appelle la méthode définie dans A
a=b; // A a = new(B);
a.f(); // appelle la méthode définie dans B
a.g(); // appelle la méthode définie dans A
b.g(); // appelle la méthode définie dans A
Il faut noter que la même écriture a.f(); peut correspondre à des appels
différents de la méthode f(). Ceci est réalisé grâce au polymorphisme.
Le Polymorphisme veut dire que le même service peut avoir un
comportement différent suivant la classe dans laquelle il est utilisé.
C’est un concept fondamental de la programmation objet, indispensable
pour une utilisation efficace de l’héritage
ENSAM 2020-2021- T.HAJJI- 162
Attention c’est important:
Si on a:
B b = new B();
b.h(); // appelle la méthode h() définie dans B
A a=new B();
a.h(); // erreur à la compilation même si la classe réelle possède
// la méthode h(). En effet la classe déclarée (classe A)
// ne ossède pas la méthode h().
• En effet, en Java, dès la compilation on doit garantir l’existence de la
méthode appelée typage statique) : la classe déclarée de l’objet qui
reçoit le message (ici la classe A ou une de ces classes ancêtres
(polymorphisme)) doit posséder cette méthode ().
ENSAM 2020-2021- T.HAJJI- 163
• Le polymorphisme est obtenu grâce au mécanisme
de la liaison retardée (tardive) « late binding »: la
méthode qui sera exécutée est déterminée
– seulement à l’exécution, et pas dès la compilation
– par le type réel de l’objet qui reçoit le message (et pas
par son type déclaré)
ENSAM 2020-2021- T.HAJJI- 164
Mécanisme de la liaison retardée: cas des méthodes redéfinies
Soit D la classe réelle d’un objet d (D est la classe du constructeur qui a créé
l’objet d) et soit A la classe de déclaration de l’objet d (A d = new D();).
Si on a l’instruction: d.f();
quelle méthode f() sera appelée ?
1. Si la méthode f() n’est pas définie dans une classe ancêtre de la classe de
déclaration (classe A) alors erreur de compilation.
2. Si non
– Si la méthode f() est redéfinie dans la classe D, alors c’est cette
méthode qui sera exécutée
– Sinon, la recherche de la méthode f() se poursuit dans la classe mère de
D, puis dans la classe mère de cette classe mère, et ainsi de suite,
jusqu’à trouver la définition d’une méthode f() qui sera alors exécutée.
ENSAM 2020-2021- T.HAJJI- 165
La méthode appelée ne dépend que du type réel de l’objet et non du type
déclaré.
Soit B une classe qui hérite de la classe A, et soit f() une méthode définie
dans A et redéfinie dans B. Si on a:
A a = new B();
a.f();
Alors la méthode f() appelée est celle définie dans B.
• Si C hérite de B et si la méthode f() est redéfinie dans C, alors si on a:
A a = new C(); // Ok car C hérite de B qui hérite de A
a.f();
La méthode appelée est celle définie dans C.
ENSAM 2020-2021- T.HAJJI- 166
Utilités du Polymorphisme:
Le polymorphisme permet d’éviter les codes qui
comportent de nombreux embranchements et tests.
Exemple
Considérons une classe FormeGeometrique. Supposons
que les classes Rectangle et Cercle héritent de la super
classe FormeGeometrique. Dans un tableau hétérogène, on
ranges des objets de type Rectangle, Cercle. Ensuite on
affiche le contenu du tableau
ENSAM 2020-2021- T.HAJJI- 167
Exemple
class FormeGeometrique {
public void dessineRectangle() {} // méthode vide
public void dessineCercle() {} // méthode vide
}
class Rectangle extends FormeGeometrique {
public void dessineRectangle() {
System.out.println("Je suis un rectangle ");
}
}
class Cercle extends FormeGeometrique {
public void dessineCercle() {
System.out.println("Je suis un cercle .... ");
}
}
ENSAM 2020-2021- T.HAJJI- 168
public class TestFigPoly {
public static void main(String[] args) {
FormeGeometrique [] figures = new FormeGeometrique[3];
figures[0]=new Rectangle();
figures[1]=new Cercle();
figures[2]=new Cercle();
for (int i=0; i < figures.length; i++) {
if (figures[i] instanceof Rectangle)
figures[i].dessineRectangle();
else if (figures[i] instanceof Cercle)
figures[i].dessineCercle();
}
}
}
instanceof: Soit b est une instance d'une classe B alors (b instanceof A)
retourne true si B est une sous classe de A. En particulier (b instanceof
B) retourne true.
ENSAM 2020-2021- T.HAJJI- 169
Inconvénients dans le code précédent:
1. Si on veut rajouter une nouvelle forme géométrique par
exemple, triangle alors on est obliger de:
- changer dans le code source aux niveaux définition de la classe
(rajouter la méthode vide public void dessineTriangle() {})
- Rajouter un branchement pour la forme Triangle
2. Si on traite plusieurs formes géométrique, alors le code
comportera plusieurs tests.
En exploitant le polymorphisme, on peut améliorer le
code précédent
- Eviter les tests
- Rendre le code extensible: rajouter de nouvelles
sous-classes sans toucher au code existant)
ENSAM 2020-2021- T.HAJJI- 170
L’exemple peut être réécrit de la manière suivante en
exploitant le polymorphisme
class FormeGeometrique {
public void dessineFigure() {} // méthode vide
}
class Rectangle extends FormeGeometrique {
public void dessineFigure() {
System.out.println("Je suis un rectangle ");
}
}
class Cercle extends FormeGeometrique {
public void dessineFigure() {
System.out.println("Je suis un cercle .... ");
}
}
ENSAM 2020-2021- T.HAJJI- 171
public class TestFigPoly {
public static void main(String[] args) {
FormeGeometrique [] figures = new FormeGeometrique[3];
figures[0] = new Rectangle();
figures[1] = new Cercle();
figures[2] = new Cercle();
for (int i=0; i < figures.length; i++)
figures[i].dessineFigure();
// pas besoin de spécifier quelle forme
}
}
ENSAM 2020-2021- T.HAJJI- 172
Un autre exemple d’exploitation du Polymorphisme:
Considérons l’exemple précédent, en supposant que le
rectangle et le cercle sont caractérisés par des champs privés
qui désignent:
- La position dans le plan (position du centre)
- La longueur et la largeur pour le rectangle
- Le rayon pour le Cercle.
Considérons un tableau hétérogène qui contient des formes
géométriques différentes: rectangles, cercles, ...
Le but et d’afficher le type de la forme géométrique et ses
caractéristiques
ENSAM 2020-2021- T.HAJJI- 173
1ère solution:
class FormeFigure {
public void dessineFigure() {}
}
class Rectangle extends FormeFigure {
public Rectangle(int x, int y, int longueur, int largeur) { // centre (x,y)
this.x=x; this.y=y;
this.largeur=largeur; this.longueur=longueur;
}
public void dessineFigure() {
System.out.println(" Je suis un Rectangle de longueur " +longueur +" et de largeur " + largeur);
System.out.println(" ma position dans le plan est (" + x + " ," + y + ")" );
}
private int x,y;
private int largeur, longueur;
}
ENSAM 2020-2021- T.HAJJI- 174
class Cercle extends FormeFigure {
public Cercle(int x, int y, int rayon){ // centre (x,y)
this.x=x; this.y=y;
this.rayon=rayon;
}
public void dessineFigure(){
System.out.println(" Je suis un cercle de Rayon " + rayon);
System.out.println(" ma position dans le plan est (" + x + " ," + y + ")" );
}
private int x,y;
private int rayon;
}
ENSAM 2020-2021- T.HAJJI- 175
public class TestFigPolySuite {
public static void main(String[] args) {
FormeFigure [] figures = new FormeFigure[2];
figures[0]=new Rectangle(1,2,15,6);
figures[1]=new Cercle(0,0,4);
for (int i=0; i < figures.length; i++)
figures[i].dessineFigure();
}
}
ENSAM 2020-2021- T.HAJJI- 176
2ème solution : on ne redéfinie pas la position dans le plan qui est la même pour
toutes les formes géométrique.
class FormeFigure {
public FormeFigure(int x, int y) { // constructeur
this.x=x; this.y=y;
}
public void dessineFigure() {
afficheMonIdentite();
System.out.println(" ma position dans le plan est (" + x + " ," + y + ")" );
}
public void afficheMonIdentite(){}; // méthode vide
private int x,y;
}
ENSAM 2020-2021- T.HAJJI- 177
class Rectangle extends FormeFigure {
public Rectangle(int x, int y, int longueur, int largeur) { // centre (x,y)
super(x,y);
this.largeur=largeur; this.longueur=longueur;
}
public void afficheMonIdentite() {
System.out.println(" Je suis un Rectangle de longueur " +longueur +" et de largeur " + largeur);
}
private int largeur, int longueur;
}
class Cercle extends FormeFigure {
public Cercle(int x, int y, int rayon){ // centre (x,y)
super(x,y);
this.rayon=rayon;
}
public void afficheMonIdentite() {
System.out.println(" Je suis un cercle de Rayon " + rayon);
}
private int rayon;
}
ENSAM 2020-2021- T.HAJJI- 178
public class TestFigPolySuite {
public static void main(String[] args) {
FormeFigure [] figures = new FormeFigure[2];
figures[0]=new Rectangle(1,2,15,6);
figures[1]=new Cercle(0,0,4);
for (int i=0; i < figures.length; i++)
figures[i].dessineFigure();
// la méthode afficheMonIdentite() appelée est celle définie dans la classe
//réelle de l’objet et non celle définie dans la classe de déclaration
}
}
ENSAM 2020-2021- T.HAJJI- 179
Cast (transtypage): conversions de classes
• Le « cast » est le fait de forcer le compilateur à
considérer un objet comme étant d’un type qui n’est
pas le type déclaré ou réel de l’objet
• En Java, les seuls casts autorisés entre classes sont les
casts entre classe mère et classes filles
ENSAM 2020-2021- T.HAJJI- 180
Syntaxe
• Pour caster un objet en classe A :
(C) o;
• Exemple :
FormeFigure a = new FormeFigure();
Rectangle r = (Rectangle) a;
FormeFigure b;
(Rectangle) b = r;
ENSAM 2020-2021- T.HAJJI- 181
UpCast : classe fille → classe mère
• Upcast : un objet est considéré comme une instance
d’une des classes ancêtres de sa classe réelle
• Il est toujours possible de faire un upcast car tout
objet peut être considéré comme une instance d’une
classe ancêtre
• Le upcast est souvent implicite
ENSAM 2020-2021- T.HAJJI- 182
DownCast : classe mère → classe fille
• Downcast : un objet est considéré comme étant d’une
classe fille de sa classe de déclaration
• Toujours accepté par le compilateur
• Mais peut provoquer une erreur à l’exécution ; à
l’exécution il sera vérifié que l’objet est bien de la
classe fille
• Un downcast doit toujours être explicite
ENSAM 2020-2021- T.HAJJI- 183
Classes abstraites
Une classe abstraite (abstract class) est une classe déclarée
avec le mot clé abstract class.
Par exemple:
abstract class A {
…
}
Dans une classe abstraite on peut trouver: des champs, des
méthodes et des méthodes abstraites
ENSAM 2020-2021- T.HAJJI- 184
• Une méthode est abstraite, lorsqu’on la déclare sans
donner son implémentation: on ne fournit que le type
de la valeur de retour et la signature (l’entête de la
méthode).
• Les méthodes abstraite sont déclarée avec le mot clè
abstract.
• Exemple:
abstract void f(int i, float x);
abstract double g();
• La méthode peut être implémentée par les classes
filles.
ENSAM 2020-2021- T.HAJJI- 185
- On peut créer une référence sur un objet de type A (une
classe abstraite) mais il est interdit de créer une instance (un
objet) d’une classe abstraite
A a; // autorisé
A a = new A(); // n’est pas autorisé.
- Si B hérite de A alors on peut écrire:
A a = new B();
- Une méthode static ne peut être abstraite (car on ne peut
redéfinir une méthode static)
- Une classe qui contient au moins une méthode abstraite est
automatiquement abstraite
- Une méthode déclarée abstraite doit obligatoirement être
déclarée public.
ENSAM 2020-2021- T.HAJJI- 186
Intérêt des classe abstraites:
– la classe mère définit la structure globale d’un algorithme
– elle laisse aux classes filles le soin de définir des points
bien précis de l’algorithme.
ENSAM 2020-2021- T.HAJJI- 187
Interfaces
Définition des interfaces
• Une classe est purement abstraite si toutes ses
méthodes sont abstraites.
• Une interface est une « classe » purement abstraite
déclarée avec le mot clé interface, dont toutes les
méthodes sont publiques.
• Une interface est une liste de noms de méthodes
publiques.
ENSAM 2020-2021- T.HAJJI- 188
Exemples d’interfaces
public interface Figure {
public abstract void dessineFigure();
public abstract void deplaceFigure(int dx, int dy);
}
Les interfaces sont implémentées par des classes
• Une classe implémente une interface I si elle déclare «
implements I » dans son en-tête
ENSAM 2020-2021- T.HAJJI- 189
Ch. V La gestion des Exceptions
Définition
• Une exception est un signal indiquant que quelque chose
d’exceptionnelle (comme une erreur) s’est produit.
• Elle interrompt le flot d’exécution normal du programme
• Plutôt que de compliquer le code du traitement normal, on
traite les conditions anormales à part
• Le traitement « normal » apparaît ainsi plus simple et plus
lisible
ENSAM 2020-2021- T.HAJJI- 190
Exemple:
Considérons la classe point qui a un constructeur à
deux arguments (position du oint dans le plan) et
une méthode deplace qui permet de déplacer le
point dans le plan. Le programme doit s’arrêter si
au moins une des coordonnées du point est négatif.
ENSAM 2020-2021- T.HAJJI- 191
public class Point {
private int x, y;
public Point(int x, int y) {
if ((x < 0) || (y < 0)) { // Détection de l’Exception
System.out.println("Erreur de Construction"); // traitement de l’exception
System.exit(-1);
}
else {
this.x = x ; this.y = y; // traitement normal
}
}
public void deplace(int dx, int dy) {
if ((x+dx < 0) || (y+dy < 0)) { // Détection de l’ Exception
System.out.println("Mauvais Déplacement"); // traitement de l’exception
System.exit(-1);
}
else {
x = x+dx ; y = y+dy; // traitement normal
}
}
}
ENSAM 2020-2021- T.HAJJI- 192
Un 1er test
public class TestException {
public static void main(String[] argv) {
private int i=3;
// début du bloc susceptible de générer une exception
Point a = new Point(6,1);
a.deplace(-1,2);
a = new Point(3, 4);
a.deplace(3,-2);
// Fin du bloc susceptible de générer une exception
System.out.println (" Exécution bien passée i= "+i);
}
}
Sortie du programme:
Exécution bien passée i=3
Les appels de Point(6,1), deplace(-1, 2) , Point(3,4) et deplace(3,-2) n’ont généré
aucune exception..
ENSAM 2020-2021- T.HAJJI- 193
Un 2ème test
public class TestException {
public static void main(String[] argv) {
private int i=3;
// début du bloc susceptible de générer une exception
Point a = new Point(6,1);
a.deplace(-1,2);
a = new Point(-2, 4);
a.deplace(3,-6);
// Fin du bloc susceptible de générer une exception
System.out.println (" Exécution bien passée i= "+i);
}
}
Sortie du programme:
Erreur de Construction
Les appels de Point(6,1) et deplace(-1, 2) n’ont pas généré d’exception, donc ils sont
exécutés normalement. Par contre l’appel de Point(-2,4) a généré une exception et
a provoqué l’arrêt du programme (appel de System.exit(-1);). L’appel de
deplace(3,-6) et l’instruction System.out.printl() n’ont pas été exécutés.
ENSAM 2020-2021- T.HAJJI- 194
Dans cet exemple le traitement normal, la détection de
l’exception et son traitement ne sont pas séparés.
Java permet de séparer la détection de l’exception et de son
traitement.
Le traitement normal et le traitement des exceptions sont
dissociés.
– Plutôt que de compliquer le code du traitement normal, on traite
les conditions anormales à part
– Le traitement « normal » apparaît ainsi plus simple et plus lisible
– Le traitement des exceptions (erreurs) s’effectue dans une zone du
programme spéciale (bloc « catch »)
ENSAM 2020-2021- T.HAJJI- 195
Pratiquement:
Le code dans lequel une exception peut se produire est mis dans un bloc try, c’est-à-
dire on délimite un ensemble d’instructions susceptibles de déclencher une exception
par des blocs try {…}
try {
/* code */
}
La gestion des exceptions est obtenue par des blocs catch, où un bloc catch est associé à
une exception donnée.
Attention: Le bloc catch doit être juste après le bloc try, sinon erreur de compilation.
catch ( Type1Exception e) { // de type Type1Exception qui hérite de Exception
/* code */
}
catch ( Type2Exception e) { // de type Type2Exception qui hérite de Exception
/* code */
}
…..
ENSAM 2020-2021- T.HAJJI- 196
Lorsqu’une exception se produit dans le bloc try, alors :
– un saut est effectué vers un bloc (s) catch
– les blocs catch sont vérifiés un après l ’autre jusqu ’à ce
qu’on trouve un bloc correspondant à l ’exception
– le code du bloc catch est exécuté
ENSAM 2020-2021- T.HAJJI- 197
Exemple1:
Un 1er Test
public class Test {
public static void main(String[] argv) {
try {
Point a = new Point(6, 1);
a = new Point(-2, 4);
}
catch (ErrConst e) { // Erreur de type ErrConst qui hérite de Exception
System.out.println("Erreur de Construction");
System.exit(-1);
}
System.out.println(" Excution bien passée");
}
}
Sortie du programme:
Erreur de Construction
L’appel de Point(-2,4); génère une exception, donc un saut est effectué vers le bloc catch().
L’exécution du code du bloc catch() a provoqué l’arrêt du programme (appel de System.exit(-1);),
par conséquent l’appel de deplace(3,-6) et les instructions a près le bloc try n’ont pas été exécutés.
ENSAM 2020-2021- T.HAJJI- 198
Exemple1:
Un 2ème Test
public class Test {
public static void main(String[] argv) {
try {
Point a = new Point(6, 1);
a = new Point(2, 4);
}
catch (ErrConst e) { // Erreur de type ErrConst qui hérite de Exception
System.out.println("Erreur de Construction");
System.exit(-1);
}
System.out.println(" Exécution bien passée");
}
}
Sortie du programme:
Exécution bien passée
L’appel de Point(6,1) et Point(2,4) n’ont pas généré d’exception donc le code catch()
n’a pas été exécuté par conséquent l’exécution est allée au-delà du bloc try.
ENSAM 2020-2021- T.HAJJI- 199
Exemple2:
public class Test {
public static void main(String[] argv) {
try { Point a = new Point(1,4);
a.deplace(-3,1);
a = new Point(7, 4);
}
catch (ErrConst e) {
System.out.println("Erreur Construction");
System.exit(-1);
}
catch (ErrDepl ed) {
System.out.println("Erreur de Déplacement");
System.exit(-1);
}
}
}
Sortie du programme: Erreur de Déplacement
L’appel de deplace(-3,1) a généré une exception, donc un saut est effectué vers le bloc
catch(ErrDepl). L’exécution du code du bloc catch(ErrDepl) a provoqué l’arrêt du programme
(appel de System.exit(-1);).
ENSAM 2020-2021- T.HAJJI- 200
Comment lancer (déclencher) une exception
Une méthode déclare qu’elle peut lancer une exception par le mot clé
throws
Par exemple:
public void deplace(int dx, int dy) throws ErrDepl {
// Déclare que la méthode depalce() peut générer une exception
...
}
Ensuite la méthode lance une exception, en créant une nouvelle valeur
(un objet) d’exception en utilisant le mot clé throw
public void deplace (int dx, int dy) throws ErrDepl {
if ((x+dx <0) || (y+dy<0)) throw new ErrDepl();
// Détection de l’exception et Création d’une nouvelle valeur d’exception
x = x+dx ; y = y+dy; // traitement normal
}
ENSAM 2020-2021- T.HAJJI- 201
ErrDepl et ErrConst sont deux classes qui hérite de la classe
Exception. Elles peuvent être définie de la manière suivante:
class ErrDepl extends Exception{
}
class ErrConst extends Exception{
}
ENSAM 2020-2021- T.HAJJI- 202
Un Premier exemple complet:
class ErrConst extends Exception {}
class Point { private int x, y;
public Point(int x, int y) throws ErrConst {
if ((x < 0) || (y < 0)) throw new ErrConst(); // déclenche une exception
this.x = x ; this.y = y; // traitement normal
}
}
public class Test {
public static void main(String[] argv) {
try {
Point a = new Point(6, 1);
a = new Point(-2, 4);
}
catch (ErrConst e) { // Erreur de type ErrConst qui hérite de Exception
System.out.println("Erreur de Construction");
System.exit(-1);
}
}
}
ENSAM 2020-2021- T.HAJJI- 203
Un Deuxième exemple complet:
class ErrConst extends Exception {}
class ErrDepl extends Exception {}
public class Point {
private int x, y;
public Point(int x, int y) throws ErrConst {
if ((x < 0) || (y < 0)) throw new ErrConst();
this.x = x ; this.y = y;
}
public void deplace(int dx, int dy) throws ErrDepl{
if ((x+dx < 0) || (y+dy < 0)) throw new ErrConst();
x = x+dx ; y = y+dy;
}
}
ENSAM 2020-2021- T.HAJJI- 204
public class Test {
public static void main(String[] argv) {
try {
Point a = new Point(1,4);
a.deplace(0,1);
a = new Point(7, 4);
a.deplace(3,-5);
a.deplace(-1,-5);
}
catch (ErrConst e) {
System.out.println("Erreur de Construction");
System.exit(-1);
}
catch (ErrDepl ed) {
System.out.println("Erreur de Déplacement");
System.exit(-1);
}
}
}
ENSAM 2020-2021- T.HAJJI- 205
Les classes d’erreurs/exceptions
• La classe java.lang.Throwable (classe fille de Object) est
la superclasse de toutes les erreurs et exceptions rencontrées
dans le langage de programation Java.
• Java.lang est un package qui rassemble les classes de base
de Java. Toutes les classes et interfaces de java.lang sont
automatiquement importées par le compilateur.
ENSAM 2020-2021- T.HAJJI- 206
Arbre d’héritage des exceptions
Throwable
Error
Exception
RuntimeException Exceptions contrôlées
ENSAM 2020-2021- T.HAJJI- 207
Quelques sous-classes de RuntimeException (Exceptions
non contrôlées par le compilateur)
• NullPointerException: Tentative d'utiliser une référence null
• ClassCastException : Tentative de cast d'un objet dans un type
incorrecte.
• IndexOutOfBoundsException : Un indice (sur un tableau, une chaîne)
ou un intervalle défini par deux indices ont dépassé les limites
inférieures ou supérieures. Ses sous-classes :
– ArrayIndexOutOfBoundsException, pour les tableaux (indice
négatif ou supérieur ou égal à la taille du tableau).
– StringIndexOutOfBoundsException, pour les chaînes de
caractères.
ENSAM 2020-2021- T.HAJJI- 208
• ArrayStoreException : Tentative de stocker dans un tableau un
élément qui n'est pas du type des éléments du tableau ou castable dans
ce type.
• ArithmeticException: Une exception est survenue sur une opération
arithmétique, comme une division d'un entier par zéro. )
• NegativeArraySizeException : Tentative de créer un tableau ou une
chaîne avec une taille négative.
• IllegalArgumentException : Une méthode a été appelée avec un
mauvais argument ou invoquée sur un mauvais objet. Sa sous-classe
NumberFormatException
– NumberFormatException : Tentative de convertir dans un type
numérique une chaîne de caractères mal formatée.
ENSAM 2020-2021- T.HAJJI- 209
Constructeurs des exceptions
La création d'exception personnalisée peut être réalisée en rajoutant des constructeurs et
des membres supplémentaires. Par convention, toutes les exceptions doivent avoir au
moins 2 constructeurs :
– un sans paramètre
– un autre dont le paramètre est une chaîne de caractères utilisée pour décrire le
problème
Méthodes de la classe Throwable
• getMessage() retourne le message d’erreur décrivant l’exception
• printStackTrace() affiche sur la sortie standard la liste des appels de méthodes ayant
conduit à l’exception
• Exception(): constructeur sans argument
• Exception(String): Constructeur avec Argument
ENSAM 2020-2021- T.HAJJI- 210
Un premier exemple: définir un constructeur personnalisé qui a pour
argument une chaîne de caractères:
class ErrConst extends Exception {
public ErrConst() {}
public ErrConst(String msg) {
super(msg);
}
}
class Point {
private int x, y;
public Point(int x, int y) throws ErrConst {
if ((x < 0) || (y < 0)) throw new ErrConst(" Erreur aux points x=" +x+" et y=" +y);
this.x = x ; this.y = y;
}
}
ENSAM 2020-2021- T.HAJJI- 211
public class Test {
public static void main(String[] argv) {
try {
Point a = new Point(6, 1);
a = new Point(-2, 4);
}
catch (ErrConst e) {
System.out.println(e.getMessage);
System.exit(-1);
}
}
}
Sortie:
Erreur au point x= -2 et y=4
ENSAM 2020-2021- T.HAJJI- 212
Un deuxième exemple: définir un constructeur personnalisé qui a 2
arguments de type int :
class ErrConst extends Exception {
private int x, y;
public ErrConst(int x, int y) {
this.x=x; this.y=y;
}
public int getX() { return x;}
public int getY() { return y;}
}
class Point {
private int x, y;
public Point(int x, int y) throws ErrConst {
if ((x < 0) || (y < 0)) throw new ErrConst(x,y);
this.x = x ; this.y = y;
}
}
ENSAM 2020-2021- T.HAJJI- 213
public class Test {
public static void main(String[] argv) {
try {
Point a = new Point(6, 1);
a = new Point(-2, 4);
}
catch (ErrConst e) {
System.out.println(" Erreur aux points" +e.getX()+" et " +e.getY());
System.exit(-1);
}
}
}
Sortie:
Erreur aux points -2 et 4
ENSAM 2020-2021- T.HAJJI- 214
Méthode printStackTrace():
class ErrConst extends Exception{}
class Point {
private int x, y;
public Point(int x, int y) throws ErrConst {
if ((x < 0) || (y < 0)) throw new ErrConst();
this.x = x ; this.y = y;
}
}
ENSAM 2020-2021- T.HAJJI- 215
public class ExceptionTestStack {
public static void main(String[] argv) {
try {
Point a = new Point(6, 1);
a = new Point(-2, 4);
}
catch (ErrConst e) { // Erreur de type ErrConst qui hérite de Exception
System.out.println("Erreur de Construction");
e.printStackTrace();
System.exit(-1);
}
}
}
Sortie du Programme:
Erreur de Construction
ErrConst
at Point.<init>(ExceptionTestStack.java:5)
at ExceptionTestStack.main(ExceptionTestStack.java:14)
ENSAM 2020-2021- T.HAJJI- 216
Bloc finally
• C’est une instruction optionnelle qui est exécutée quelle que soit le
résultat du bloc try (c’est à dire qu’il ait déclenché ou non une exception)
• Il permet de spécifier du code dont l’exécution est garantie quoi qu’il
arrive.
try {
...
}
catch (…) {
...
}
finally {
...
}
ENSAM 2020-2021- T.HAJJI- 217
Exemple:
public class ExceptionTestStack {
public static void main(String[] argv) {
try {
Point a = new Point(6, 1);
a = new Point(-2, 4);
}
catch (ErrConst e) { // Erreur de type ErrConst qui hérite de Exception
System.out.println("Erreur de Construction");
e.printStackTrace();
}
finally {
System.out.println("Fin du Programme");
System.exit(-1);
}
}
}
Dans toutes les exécutions (déclenchement ou non d’exception), les instructions du
bloc finally seront exécutées.
ENSAM 2020-2021- T.HAJJI- 218
Ch. VI Complément
I. Interface
Classe qui implémente partiellement une interface
• Soit une interface I1 et une classe C qui l’implémente :
public class C implements I1 { … }
• C peut ne pas implémenter toutes les méthodes de I1
• Mais dans ce cas C doit être déclarée abstract (il lui
manque des implémentations)
• Les méthodes manquantes seront implémentées par les
classes filles de C
ENSAM 2020-2021- T.HAJJI- 219
• Implémentation de plusieurs interfaces
• Une classe peut implémenter une ou plusieurs
interfaces (et hériter d'une classe…) :
public class CercleColore extends Cercle
implements Figure, Coloriable {
ENSAM 2020-2021- T.HAJJI- 220
II. Champs de classe
Certaines variables sont partagées par toutes les
instances d’une classe. Ce sont les variables de
classe (modificateur static)
- Si une variable de classe est initialisée dans sa
déclaration, cette initialisation est exécutée une
seule fois quand la classe est chargée en mémoire
ENSAM 2020-2021- T.HAJJI- 221
Exemple
class A {
static int n;
float y;
}
A a1=new A(), a2=new A();
a1 et a2 deux instances différentes mais qui partagent la même variable n
a1.n et a2.n désignent donc le même champs. Il est possible de s’y
référer en le nommant simplement
a1.n=5; // automatiquement on a : a2.n =5
On peut écrire
A.n=5; // champs (statique) de la classe A, dans ce cas a1.n=5 et a2.n=5
ENSAM 2020-2021- T.HAJJI- 222
Exemple:
public class Etudiant {
private String nom, prenom;
private static int nbEtudiants = 0;
public Etudiant(String n, String p) {
nom = n;
prenom = p;
nbEtudiants++; // à chaque création d’un nouveau étudiant, le
// nombre d’étudiant augment de 1.
}
...
}
ENSAM 2020-2021- T.HAJJI- 223
III. Méthodes de classe
Ce sont des méthodes qui ont un rôle indépendant d’un objet
spécifique. Elles exécutent une action indépendante d’une
instance particulière de la classe
La déclaration d’une méthode de classe se fait à l’aide du
mot clé static.
L’appelle d’une telle méthode ne nécessite que le nom de la
classe correspondante.
Une méthode de classe ne pourra pas accéder à des champs
usuels (champs non statiques).
ENSAM 2020-2021- T.HAJJI- 224
Exemple
class A {
…
private float x; // champs usuel
static int n; // champs de classe
public static void f() { // méthode de clase
… //ici, on ne pas accéder au champs x, champs usuel
… // ici on peut accéder au champs n
}
}
ENSAM 2020-2021- T.HAJJI- 225
class Etudiant {
private String nom, prenom;
private static int nbEtudiants;
public Etudiant(String nom, String Prenom){
this.nom=nom;
this.prenom=prenom;
nbEtudiants++;
}
public static int getNbEtudiants(){
return nbEtudiants;
}
}
ENSAM 2020-2021- T.HAJJI- 226
public class MethodeStatic{
public static void main(String[] argv) {
Etudiant e1= new Etudiant("Mohammed","Ali");
Etudiant e2 =new Etudiant("Fatima","Zahra");
// les trois instructions suivante font la même chose
System.out.println("Nb etudiants = "+Etudiant.nbEtudiants);
System.out.println("Nb etudiants = "+e1.nbEtudiants);
System.out.println("Nb etudiants = "+e2. Etudiant.nbEtudiants);
}
}
ENSAM 2020-2021- T.HAJJI- 227
IV. Méthodes d’accès aux valeurs des variables depuis l’extérieur
Comment peut on accéder à la valeur d’une variable protégée ??
Exemple:
class Etudiant {
private String nom, prenom;
public Etudiant(String nom, String Prenom){
this.nom=nom; this.prenom=prenom;
}
}
public class MethodeStatic{
public static void main(String[] argv) {
Etudiant e= new Etudiant("Mohammed","Ali");
// comment afficher le nom de l’etudiant e; ??
// System.out.println("Nom = "+e.nom); ne marche pas car nom est privé
}
}
ENSAM 2020-2021- T.HAJJI- 228
Un accesseur est une méthode permettant de lire, depuis l’extérieur, le
contenu d'une donnée membre protégée.
Exemple:
class Etudiant {
private String nom, prenom;
public Etudiant(String nom, String Prenom){
this.nom=nom; this.prenom=prenom;
}
public String getNom (){
return nom;
}
}
public class MethodeStatic{
public static void main(String[] argv) {
Etudiant e= new Etudiant("Mohammed","Ali");
System.out.println("Nom = "+e.getNom());
}
}
ENSAM 2020-2021- T.HAJJI- 229
Comment peut on peut on modifier (depuis l’extérieur) le contenu d'une
donnée membre protégée ?.
Exemple:
class Etudiant {
private int cne;
public void setCNE (int cne){
this.cne=cne;
}
}
public class MethodeStatic{
public static void main(String[] argv) {
Etudiant e= new Etudiant();
// e.cne=23541654; ne marche pas car cne est un champ protégé (private)
}
}
ENSAM 2020-2021- T.HAJJI- 230
Un modificateur (mutateur) est une méthode permettant de modifier le
contenu d'une donnée membre protégée.
Exemple:
class Etudiant {
private int cne;
public void setCNE (int cne){
this.cne=cne;
}
}
public class MethodeStatic{
public static void main(String[] argv) {
Etudiant e= new Etudiant();
e.setCNT(23541654);
}
}
ENSAM 2020-2021- T.HAJJI- 231
V. Package (paquetage)
• Les classes Java sont regroupées en paquetages (packages en anglais)
comparables aux «bibliothèques» des autres langages comme le langage
C,...
• Le langage Java est fourni avec un grand nombre de paquetages
• Dans un fichier .java, on doit indiquer à quels packages appartiennent les
classes qu’on utilise à l’aide de la clause import, elle se place en début de
fichier avant la déclaration de la première classe ou interface du fichier :
import nomClasse; // Importe une classe sans package
import nomPackage.nomClasse; // Importer une classe d'un package
import nomPackage.*; // Importer toutes les classes d'un package
ENSAM 2020-2021- T.HAJJI- 232
• Le package par défaut : c’est le package qui n’a pas de nom, et auquel
appartiennent toutes les classes situées dans le même répertoire (ou dans
les répertoires accessibles dans la variable classpath)
Exemple 1:
class ClasseTest {
public ClasseTest() {
System.out.println(" Ceci est un exemple : ");
}
}
public class TestPackage {
public static void main(String[] args) {
System.out.print("Entrer un entier nouvelle largeur : ");
int i = Clavier.lireInt();
}
}
ENSAM 2020-2021- T.HAJJI- 233
Remarques:
- La compilation du fichier source TestPackage.java génère
deux fichiers .class à savoir ClasseTest.class et
TestPackage.class, ses deux fichiers appartiennent au même
package par défaut (package sans nom)
- La classe Clavier.class doit se trouver dans le même
répertoire que le fichier TestPackage.class (ou dans un
répertoire défini dans CLASSPATH), sinon le compilateur
génère une erreur.
ENSAM 2020-2021- T.HAJJI- 234
Package java.lang
Le package java.lang contenant les classes standards telles que System
• Par défaut, les classes du paquetage java.lang (contenant les classes
standards tel que System) sont automatiquement importées. Dans un
programme .java on peut faire appel à toutes les classes de java.lang
sans spécifier leur chemin.
Exemple: On peut utliser la classe Exception sans spécifier le chemin
class SimpleException extends Exception {}
qui est équivalent à
class SimpleException extends java.lang.Exception {}
ou
import java.lang.Eception;
class SimpleException extends Exception {}
ENSAM 2020-2021- T.HAJJI- 235
Accès aux package
Pour pouvoir accéder aux classes d'un package à partir d'une
classe ne faisant pas partie du package:
– il suffit de donner le chemin d'accès relatif à la classe lorsqu’on
fait appel à elle. Par exemple pour appeler la classe Button du
package java.awt, il suffit d'écrire java.awt.Button à chaque
appel de la classe Button
– Ou d'utiliser l'instruction import, suivie du chemin de la classe.
Par exemple import java.awt.Button; dans ce cas, on peut
appeler la classe Button sans spécifier le chemin.
– Ou d’importer toutes les classes d'un package. Par exemple on
importe toutes les classes du package java.awt par la syntaxe :
import java.awt.*;
ENSAM 2020-2021- T.HAJJI- 236
Remarque:
Un paquetage peut avoir des sous-paquetages, par exemple
java.awt.event est un sous-paquetage de java.awt
Attention:
L'instruction import java.awt.*; ne permet pas d’importer les
classes des sous-paquetages de java.awt
Si on veut par exemple importer les classes de java.awt et aussi
celles de java.awt.event alors on devra écrire:
import java.awt.*;
import java.awt.event.*;
ENSAM 2020-2021- T.HAJJI- 237
Quelques paquetages du SDK (Software Development Kit)
• java.lang : classes de base de Java
• java.util : utilitaires
• java.io : entrées-sorties
• java.awt : interface graphique
• javax.swing : interface graphique avancée
• java.applet : applets
• java.net : réseau
• java.rmi : distribution des objets
ENSAM 2020-2021- T.HAJJI- 238
Définition:
Pour créer un package, il suffit de commencer le fichier source
contenant les classes à regrouper par l'instruction package suivi du
nom que l'on désire donner au package.
Attention: un package portant le nom Pack1 doit être stocké dans un
répertoire de nom Pack1. De plus, pour que le compilateur puisse
trouver le package, il est essentiel qu'il "connaisse" l'emplacement
du package. Pour cela Java utilise une variable d'environnement
appelée classpath donnant la liste des chemins d'accès aux classes.
Par défaut le compilateur (ainsi que la machine virtuelle)
recherchent les classes dans le répertoire courant et le répertoire des
classes standards.
ENSAM 2020-2021- T.HAJJI- 239
Exemple
import graphics.*;
public class TestPackage1 {
public static void main(String[] args) {
Rectangle r=new Rectangle();
r.dessineRectangle();
Cercle.dessineCercle(); // méthode de classe
Triangle t=new Triangle();
t.dessineTriangle();
}
}
ENSAM 2020-2021- T.HAJJI- 240
// dans un fichier Rectangle.java situé dans le répertoire ./grahics
package graphics;
public class Rectangle{
public void dessineRectangle() { System.out.println(" Dessine un rectangle "); }
}
// dans fichier Cercle.java situé dans le répertoire ./grahics
package graphics;
public class Cercle {
public static void dessineCercle() {
System.out.println(" Dessine un cercle .... ");
}
}
// dans fichier Triangle.java situé dans le répertoire ./grahics
package graphics;
public class Triangle {
public void dessineTriangle() { System.out.println(" Dessine un triangle.... "); }
}
ENSAM 2020-2021- T.HAJJI- 241
Ch. VII. Introduction aux Interfaces
graphiques
Cette partie du cours est essentiellement développé à
partir du livre "Programmer en Java" de Claude
Delannoy, Edition Eyrolles.
ENSAM 2020-2021- T.HAJJI- 242
Les applications informatiques nécessitent des échanges
d’informations entre l’utilisateur et la machine.
Cet échange d’informations peut s’effectuer avec une interface
utilisateur (UI en anglais) en mode texte (ou console) ou en
mode graphique.
Une interface graphique est composée de plusieurs fenêtres
qui contiennent divers composants graphiques (widgets) tels
que
– boutons
– listes déroulantes
– menus
– champ texte
– etc.
Les interfaces graphiques sont souvent appelés GUI (pour
Graphical User Interface en Anglais)
ENSAM 2020-2021- T.HAJJI- 243
Il existe deux principaux types de composants
susceptibles d’intervenir dans une interface
graphique :
– les conteneurs qui sont destinés à contenir d’autres
composants, comme par exemple les fenêtres ;
– les composants atomiques qui sont des composants qui
ne peuvent pas en contenir d’autres, comme par exemple
les boutons.
ENSAM 2020-2021- T.HAJJI- 244
Java offre des facilités pour construire des interfaces graphiques
grâce aux deux bibliothèques :
– AWT (Abstract Window Toolkit, JDK 1.1)
– Swing (JDK/SDK 1.2).
Les packages principaux pour chaque bibliothèque:
– AWT : java.awt et java.awt.event
– Swing : javax.swing, javax.swing.event
Il est conseillé d’importer systématiquement toutes les classes
des paquetages pour éviter l’interrogation sur la répartition des
classes utilisées des différents paquetages
import java.awt.*;
import javax.swing.*;
ENSAM 2020-2021- T.HAJJI- 245
Afficher une fenêtre
import java.awt.*; import javax.swing.*;
class MaFenetre extends JFrame { // classe fenêtre personnalisée
public MaFenetre() {
setTitle("Ma Premiere fenetre ");
//initialisation du titre de la fenêtre
setSize(300,150);
// situé dans le coin supérieur gauche de dimension 300*150 pixels
// On peut utiliser setBounds(100,40,300,200); le coin supérieur gauche de
// la fenêtre est placé au pixel de coordonnées 100, 40 et ses dimensions
// seront de 300 *200 pixels
}
}
ENSAM 2020-2021- T.HAJJI- 246
public class TestFenetre {
public static void main(String[] args) {
JFrame fen = new MaFenetre();
fen.setVisible(true); // rend visible la fenêtre de référence fen
// on peut utiliser l’instruction
// fen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// qui mis fin au programme lorsque la fenêtre se ferme
}
}
ENSAM 2020-2021- T.HAJJI- 247
Attention:
• Une fenêtre de type JFrame peut être redimensionnée, déplacée, et réduite
à une icône (comme une fenête Windows).
• La fermeture d'une fenêtre de type JFrame ne met pas fin au programme,
mais rend simplement la fenêtre invisible.
• Pour mettre fin au programme,
– on peut par exemple faire un Ctrl C.
– On peut appeler la méthode
JFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) qui
permet de définir le comportement de la fenêtre lors de sa fermeture.
– Le paramètre JFrame.EXIT_ON_CLOSE indique la fin du
programme lorsque la fenêtre se ferme.
ENSAM 2020-2021- T.HAJJI- 248
Ajout d’un composant dans une fenêtre:
L’ajout d’un bouton dans le contenu d’une fenêtre se passe en 3 étapes
1. Créer une instance de la classe JButton : JButton monBouton = new JButton () ;
2. On utilise la méthode getContentPane() de la classe JFrame qui retourne une
référence, de type Container, sur le contenu de la fenêtre.
Par exemple: Container c= getContentPane();
3. On ajoute un composant dans le contenu d’une fenêtre (un objet de type
Container) en utilisant la méthode add de la classe Container.
Par exemple l’appel c.add(monBouton); permet d’ajouter l’objet de référence
monBouton dans le contenu de référence c.
La disposition des composants dans le contenu peut être gérée automatiquement
par un gestionnaires de disposition (Layout Manger en anglais) associé à chaque
container. Le gestionnaire de la classe FlowLayout permet de disposer les
composants un à coté de l’autre et ligne par ligne. La méthode setLayout(new
FlowLayout()); permet d’utiliser ce gestionnaire
ENSAM 2020-2021- T.HAJJI- 249
Ajout d’un bouton dans une fenêtre: On utilise le constructeur de la classe JButton pour
créer un bouton. Le contenu de la clase fenêtre qui sera modifié. La classe de la méthode
main() reste inchangé.
import javax.swing.*; import java.awt.*;
class MaFenetre extends JFrame {
public MaFenetre() {
setTitle(" Premier Bouton ");
setSize(300,200);
Container c=getContentPane();
// création d’un bouton de référence monBouton portant l’étiquette Un bouton
monBouton = new JButton("Un Bouton");
c.add(monBouton);
c.setLayout(new FlowLayout());
// ou tout simpolement getContentPane().setLayout(new FlowLayout());
// getContentPane().add(monBouton);
}
private JButton monBouton;
}
ENSAM 2020-2021- T.HAJJI- 250
Ajout de deux bouton dans une fenêtre:
import javax.swing.*; import java.awt.*;
class MaFenetre extends JFrame {
public MaFenetre() {
setTitle(" Plusieurs boutons ");
setSize(300,200);
boutJaune = new JButton("Bouton A");
boutVert = new JButton("Bouton B");
getContentPane().add(boutJaune);
getContentPane().add(boutVert);
getContentPane().setLayout(new FlowLayout());
boutJaune.setBackground(Color.yellow);
// La couleur de fond du composant de label Bouton A devienne jaune
boutVert.setBackground(Color.green);
// La couleur de fond du composant de label Bouton A devienne verte
}
private JButton boutJaune, boutVert;
}
ENSAM 2020-2021- T.HAJJI- 251
Programmation événementielle
Dans la programmation séquentielle c’est le programme qui garde le
contrôle des opérations(les instructions du programme principal sont
exécutées les unes après les autres dans l’ordre de leurs d’écriture).
Dans la programmation événementielle, le programme est dirigé par les
événements
• L’utilisateur pilote l’ordre d’exécution des instructions en cliquant où il
veut.
• Le programme ne fait que réagir aux actions de l’utilisateur
• Dès que vous lancez un programme avec interface graphique :
– une boucle infinie est générée
– le programme attend les événements (enfoncer une touche du clavier,
cliquer avec la souris etc.)
– Attrape et traite les événements quand ils se produisent.
ENSAM 2020-2021- T.HAJJI- 252
• Une interface graphique est basée sur 2 grandes
composantes différentes :
– L'aspect visuel qui comprend les cadres et les composants
d'interfaces qui sont placés dessus
– L'interaction avec les utilisateurs (action de la souris, du
clavier) c'est à dire en fait la gestion des événements.
ENSAM 2020-2021- T.HAJJI- 253
• Evénement = Objet construit par le système en réponse à une
action de l’utilisateur et qui contient toutes les informations
concernant cette action
Exemples: Action de l’utilisateur dans la fenêtre de l’application
• clic souris : connaissances des coordonnées du point de clic
• frappe d’un caractère : connaissance du caractère frappé
ENSAM 2020-2021- T.HAJJI- 254
Traitement des événements
En Java tout événement a une source. On appelle source d’un
événement l’objet qui a généré cet événement (exemple
bouton, fenêtre, ...).
Exemple
– L’événement émis suite à un clic souris dans une fenêtre est
de type MouseEvent
– L’événement émis suite à un clic souris sur un bouton est de
type ActionEvent
ENSAM 2020-2021- T.HAJJI- 255
Pour traiter un événement, on associe à la source, des
objets particuliers appelés écouteurs.
Par exemple on associe au bouton un « écouteur » qui va
écouter si le bouton est cliqué ou non.
En fonction des événements qu’ils traitent, un écouteur
doit implémenter une interface particulière, dérivée de
l’interface EventListener, qui correspond à une
catégorie d’événements.
ENSAM 2020-2021- T.HAJJI- 256
L’interface MouseListener correspond à une catégorie d’événements souris. Elle comporte
cinq méthodes correspondant chacune à un événement souris particulier.
public interface MouseListener extends EventListener {
public void mousePressed(MouseEvent e) ;
//appelé lorsqu’un bouton de la souris est pressé sur un composant
//l’argument e de type MouseEvent correspond à l’objet événement généré
public void mouseReleased(MouseEvent e) ;
//appelé lorsqu’un bouton de la souris est relâché sur un composant
public void mouseClicked(MouseEvent e) ;
//appelé lors d’un clic souris sur un composant (la souris n’a pas été déplacée entre
// l’appui et le relâchement du bouton)
public void mouseEntered(MouseEvent e) ;
//appelé lorsque la souris passe de l’extérieur à l’intérieur d’un composant
public void mouseExited(MouseEvent e) ;
//appelé lorsque la souris sort d’un composant (la souris passe de l’intérieur à l’extérieur
//du composant)
}
ENSAM 2020-2021- T.HAJJI- 257
Exemple 1: Gestion de clics dans une fenêtre, à chaque clic dans la fenêtre,
le message « clic dans la fenetre » s’affiche.
import java.awt.* ; import java.awt.event.* ;
import javax.swing.* ; import javax.swing.event.* ;
class MaFenetre extends JFrame {
public MaFenetre () {
setTitle("Gestion des clics ");
setBounds(10,20,300,100);
addMouseListener(new EcouteurSouris());
// la fenêtre fait appel à un écouteur d’événements souris
// pour traiter les clics souris
}
}
ENSAM 2020-2021- T.HAJJI- 258
//L’écouteur d’événements souris doit implémenter l’interface
MouseListener qui correspond à une catégorie d’événements souris.
class EcouteurSouris implements MouseListener {
//Redéfinition de la méthode appelée lors d’un clic souris
public void mouseClicked(MouseEvent e) {
System.out.println("clic dans la fenetre");
}
//La redéfinition des autres méthodes est "vide"
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
}
ENSAM 2020-2021- T.HAJJI- 259
Exemple 2: La classe fenêtre personnalisée qui implémente MouseListner
import java.awt.* ; import java.awt.event.* ;
import javax.swing.* ; import javax.swing.event.* ;
class MaFenetre extends JFrame implements MouseListener {
public MaFenetre () {
setTitle("Gestion des clics ");
setBounds(10,20,300,100);
addMouseListener(this); // la fenêtre est son propre écouteur d’événements souris
}
public void mouseClicked(MouseEvent e) {
//L’argument e correspond à l’objet événement généré dans la fenêtre lors d’un clic souris.
int x = e.getX() ;
int y = e.getY() ; //x et y coordonnées du curseur de la souris au moment du clic
System.out.println("clic dans la fenetre au point de coordonnees "+x+","+y);
}
// La redéfinition des autres méthodes est "vide"
public void mousePressed(MouseEvent e) { }
……
}
ENSAM 2020-2021- T.HAJJI- 260
La notion d’adaptateur
Pour chaque interface XXXListener (par exemple
MouseListener) possédant plusieurs méthodes, Java fournit
une classe particulière XXXAdapter (par exemple
MouseAdpter), appelée adaptateur, qui implémente toutes
les méthodes de l’interface avec un corps vide.
Pour définir un écouteur d’événements de type XXXEvent
(par exemple MouseEvent), il suffit alors de dériver
l'écouteur de la classe XXXAdapter (par exempe Mouse
Adapter) et de redéfinir uniquement les méthodes voulues.
ENSAM 2020-2021- T.HAJJI- 261
Exemple:
class MaFenetre extends JFrame {
public MaFenetre () {
super("Une fenetre qui traite les clics souris") ;
setSize(300, 200) ;
addMouseListener(new EcouteurSouris());
}
}
class EcouteurSouris extends MouseAdapter {
//redéfinition uniquement de la méthode appelée lors d’un clic souris
public void mouseClicked(MouseEvent e) {
System.out.println("clic dans la fenetre") ;
}
}
ENSAM 2020-2021- T.HAJJI- 262
Gestion des événements sur des boutons
• Un bouton ne peut déclencher qu’un seul événement
de type ActionEvent.
• L’interface ActionListener ne comporte qu’une seule
méthode actionPerformed.
• C’est à l’intérieur de la actionPerformed que nous
écrirons le code qui s’exécutera lors du click.
ENSAM 2020-2021- T.HAJJI- 263
Exemple: Clic sur un bouton affiche « Action sur Bouton Essai »
import javax.swing.*; import java.awt.*; import java.awt.event.*;
class MaFenetre extends JFrame implements ActionListener{
public MaFenetre() {
setTitle(" Premier Bouton ");
setSize(300,200);
monBouton = new JButton("Essai");
getContentPane().setLayout(new FlowLayout());
getContentPane().add(monBouton);
monBouton.addActionListener(this);
//gère l’action de l’utilisateur sur le bouton MonBouton
}
public void actionPerformed (ActionEvent ev){
System.out.println("Action sur Bouton Essai ");
}
private JButton monBouton;
}
ENSAM 2020-2021- T.HAJJI- 264
Exemple: Même classe écouteur pour tous les boutons, le clique sur un bouton affiche le
même message « Action sur Bouton Essai »
import javax.swing.*; import java.awt.*; import java.awt.event.*;
class MaFenetre extends JFrame implements ActionListener{
public MaFenetre() {
setTitle(" Deux boutons ");
setSize(300,200);
getContentPane().setLayout(new FlowLayout());
monBoutonA = new JButton("Bouton A");
getContentPane().add(monBoutonA);
monBoutonA.addActionListener(this);
monBoutonB = new JButton("Bouton B");
getContentPane().add(monBoutonB);
monBoutonB.addActionListener(this);
}
public void actionPerformed (ActionEvent ev){
System.out.println("Action sur un Bouton Essai");
}
private JButton monBoutonA, monBoutonB;
}
ENSAM 2020-2021- T.HAJJI- 265
Exemple: Même classe écouteur pour tous les boutons. L’affiche
personnalisé. L’affichage dépend du bouton cliqué
import javax.swing.*; import java.awt.*; import java.awt.event.*;
class MaFenetre extends JFrame implements ActionListener{
public MaFenetre() {
setTitle(" Premier Bouton ");
setSize(300,200);
monBoutonA = new JButton("Bouton A");
monBoutonB = new JButton("Bouton B");
Container contenu=getContentPane();
contenu.setLayout(new FlowLayout());
contenu.add(monBoutonA);
contenu.add(monBoutonB);
monBoutonA.addActionListener(this);
monBoutonB.addActionListener(this);
}
ENSAM 2020-2021- T.HAJJI- 266
public void actionPerformed (ActionEvent ev){
//utilisation de la méthode getSource de la classe EventObject qui fournit
une référence de type Object sur la source de l’événement concerné
if (ev.getSource()==monBoutonA)
System.out.println("Action sur un BoutonA");
if (ev.getSource()==monBoutonB)
System.out.println("Action sur un BoutonB");
}
private JButton monBoutonA, monBoutonB;
}
ENSAM 2020-2021- T.HAJJI- 267
Exemple: Un classe écouteur pour chaque bouton. Par exemple, le clic sur le bouton 1
ferme la fenêtre, le clic sur le bouton 2 affiche le message « action sur le bouton 2 »
import java.awt.* ; import java.awt.event.* ;
import javax.swing.* ; import javax.swing.event.* ;
class MaFenetre extends JFrame{
private JButton MonBouton1, MonBouton2 ;
public MaFenetre () {
super("Plusieurs boutons") ;
setSize(300, 200) ;
Container contenu = getContentPane() ;
MonBouton1 = new JButton("Bouton 1") ;
contenu.add(MonBouton1) ;
MonBouton2 = new JButton("Bouton 2") ;
contenu.add(MonBouton2) ;
contenu.setLayout(new FlowLayout());
MonBouton2.addActionListener(new EcouteurBouton2());
MonBouton1.addActionListener(new EcouteurBouton1());
}
}
ENSAM 2020-2021- T.HAJJI- 268
class EcouteurBouton2 implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("action sur le bouton 2") ;
}
}
class EcouteurBouton1 implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
ENSAM 2020-2021- T.HAJJI- 269
Création dynamique de plusieurs boutons
import javax.swing.*; import java.awt.*; import java.awt.event.*;
class MaFenetre extends JFrame { // implements ActionListener{
public MaFenetre() {
setTitle(" Bouton Dynamic ");
setSize(300,200);
Container contenu=getContentPane();
contenu.setLayout(new FlowLayout());
crBouton=new JButton ("CREATION BOUTON");
contenu.add(crBouton);
EcouteCr ecouteCr=new EcouteCr(contenu);
crBouton.addActionListener(ecouteCr);
}
private JButton crBouton;
}
ENSAM 2020-2021- T.HAJJI- 270
class EcouteCr implements ActionListener {
public EcouteCr(Container contenu){
this.contenu=contenu;
}
public void actionPerformed(ActionEvent ev) {
JButton nouvBout=new JButton("Bouton");
contenu.add(nouvBout);
contenu.validate();
}
private Container contenu;
}
ENSAM 2020-2021- T.HAJJI- 271
Les cases à cocher : La case à cocher de type JCheckBox permet à
l’utilisateur d’effectuer un choix de type oui/non.
Exemple
import java.awt.* ; import java.awt.event.* ;
import javax.swing.* ; import javax.swing.event.* ;
class MaFenetre extends JFrame {
private JCheckBox MaCase;
public MaFenetre () {
super("Une fenetre avec une case") ;
setBounds(10,40,300,200) ;
MaCase = new JCheckBox("Une case") ;
// création d’une case à cocher de référence MaCase portant l’étiquette Une case
getContentPane().add(MaCase) ;
}
}
ENSAM 2020-2021- T.HAJJI- 272
Par défaut, une case à cocher est construite dans l’état non coché (false). La
méthode isSelected de la classe AbstractButton permet de connaître l’état
(true ou false) d’une case à cocher à un moment donné. La méthode
setSelected de la classe AbstractButton permet de modifier l’état d’une
case à cocher.
Exemple:
MaCase.setSelected(true) ;
//coche la case de référence MaCase
ENSAM 2020-2021- T.HAJJI- 273
Quelques Références:
Livre:
- Titre: Programmer en JAVA ,
AuteurClaude Delnoy,
Editeur: Eyrolles
- Thinking in Java, Bruce Eckel
Ressources Internet:
- http://www.java.sun.com,
- http://metarisk.inapg.inra.fr/content/download, Cours Interface
graphique en Java API swing, Juliette Dibie-Barthélemy mai 2005
ENSAM 2020-2021- T.HAJJI- 274
WindowBuilder
• Window Builder est un plugin pour Eclipse qui
permet de faire la génération automatique et facile
des interfaces graphiques Swing et SWT, il permet
aussi de générer le code source java.
• Il n’est pas forcément directement intégrer dans
Eclipse mais il s’intègre facilement.
• Eclipse>Help>Eclipse Market Place > Find:
Window Builder, GO (il faut être connecter à
l’internet) puis installe!
ENSAM 2020-2021- T.HAJJI- 275
Java les collections
• Tableaux,
• Collection,
• Map
• Vues et ponts entre les structures Itération
• Legacy
ENSAM 2020-2021- T.HAJJI- 276
Structures de données
En Java, il existe 3 sortes de structures de
données
Les tableaux
● Structure de taille fixe, accès direct aux élements
Les Collections
● Structure modifiable, différents algorithmes de stockage
Les Map
● Structure modifiable, stocke des couples clé -> valeur
ENSAM 2020-2021- T.HAJJI- 277
Contrats !
Tous les algorithmes sur les structures données pré-
suppose que les classes des objets stockés respect un
certain contrat
Il vous faudra peut-être pour cela implanter correctement
equals, hashCode, toString ou compareTo sur les objets
de la collection
Si ce n'est pas le cas, au mieux une exception est levée,
au pire cela fait n'importe quoi
ENSAM 2020-2021- T.HAJJI- 278
Exemples
public class StupidInteger {
private final int value;
public StupidInteger(int value) {
this.value = value;
}
}
StupidInteger[] array = new StupidInteger[1];
array[0] = new StupidInteger(1);
Arrays.sort(array); // ClassCastException
HashSet<StupidInteger> set = new HashSet<>();
set.add(new StupidInteger(1));
set.contains(new StupidInteger(1)); // renvoie false !
ENSAM 2020-2021- T.HAJJI- 279
Null comme valeur d'une collection
•Stocker null dans une collection n'est pas une bonne idée car
cela plantera quand on sortira l'élement de la collection
•De plus, en fonction des versions du JDK, null est accepté ou
non
•1.0, 1.1, null est pas accepté
•HashTable, Vector, Stack
•1.2, 1.3, 1.4, null est accepté
•HashMap, ArrayList, etc
•1.5 ..., null est pas accepté
•PriorityQueue, ArrayDeque, etc)
ENSAM 2020-2021- T.HAJJI- 280
Null comme Collection
On utilise jamais null lorsque l'on s'attend à avoir un tableau ou
une Collection
public Collection<String> getFoo() {
return null; // ahhh, utiliser Collections.emptyCollection()
}
public String[] getBar() {
return null; // ahhhh, utiliser NULL_ARRAY
}
private static final String[] NULL_ARRAY = new String[0];
Il existe des collections vide qui sont des constantes,
Collections.emptySet(), Collections.emptyList(), etc.
ENSAM 2020-2021- T.HAJJI- 281
Les tableaux
Ils ont une taille fixe définie à l'initialisation et sont toujours
mutable (copie défensive!)
Les tableaux sont spécialisés pour les types primitifs
–byte[], int[], long[], double[]
–Ils n'héritent pas de Object[] mais de Object
Pour les tableaux d'objets, les cases sont initialisés à null (=>
ahhh), pour les primitifs, les cases sont initialisé à false, 0,
0.0
ENSAM 2020-2021- T.HAJJI- 282
Les tableaux
Les tableaux hérite de Object (ou Object[] qui
hérite de Object) et n'ont pas les méthodes toString,
equals et hashCode redéfinie en fait seul clone() est
redéfinie
Quasiment toutes les méthodes (statique) de
manipulation des tableaux sont regroupés dans
java.util.Arrays
ENSAM 2020-2021- T.HAJJI- 283
Algos classiques (java.util.Arrays)
fill() remplie un tableau (memset en C) copies
–System.arrayCopy (memcopy comme en C)
–Arrays.copyOf() (créé et recopie un tableau + grand
ou + petit)
–Object.clone()
duplique mais on utilise plutôt
Arrays.copyOf(array, array.length)
toString, deepToString, equals, hashCode, etc
ENSAM 2020-2021- T.HAJJI- 284
Collection
L'interface java.util.Collection est l'interface de base de
toutes les structures de donnée qui stocke des éléments
Il existe 4 sous-interfaces
–Set, ensemble sans doublon
–List, les listes ordonées et indexées
–Queue, les files (FIFO)
–Deque, les files “double ended”
ENSAM 2020-2021- T.HAJJI- 285
Collection<E> paramétrée
Les collections sont homogènes, elle
contiennent des éléments qui ont le même type
(pas forcément la même classe) donc elles sont
paramétrés par une variable de type, souvent
nommée E pour type d'un élément
ENSAM 2020-2021- T.HAJJI- 286
Collection et type primitif
Les collections ne sont pas spécialisées pour les types primitfs,
ce sont des collections d'objet
On utilise le boxing/unboxing si il n'y a pas d'exigence de
performance
ArrayList<Integer> list = new ArrayList<>();
list.add(1); // boxing en Integer.valueOf(1)
int value = list.get(0); // unboxing avec .intValue()
attention si on stocke null dans la list, il y aura un NullPointerException
ENSAM 2020-2021- T.HAJJI- 287
Collection et taille
A l'exception des collections concurrentes, toutes
les collections maintiennent une taille accessible
en temps constant (O(1))
int size()
permet d'obtenir la taille (sur 31 bits)
boolean isEmpty()
permet de savoir si la collection est vide
ENSAM 2020-2021- T.HAJJI- 288
Collection & mutabilité
Les Collections sont mutables par défaut
boolean add(E)
● Ajoute un élement, renvoie vrai si la collection est modifiée
boolean remove(Object)
Supprime un élement, renvoie vrai si la collection est modifiée
boolean removeIf(Predicate<? super E> predicate)
Supprime un élement si le predicate est vrai, renvoie vrai si la
collection est modifiéevoid clear()
Supprime tous les élements d'une collection (peu utilisée)
ENSAM 2020-2021- T.HAJJI- 289
Operations optionnelles
• Les opérations de mutations (add, remove, clear,
addAll, etc) peuvent levées l'exception
UnsupportedOperationException pour dire que dire que
l'opération n'est pas supportée
• Cela permet de représenter des collections non-
mutable ou des collections mutables mais de taille fixe
ENSAM 2020-2021- T.HAJJI- 290
Collection non mutable
Collections.unmodifiableCollection() permet de créer
un proxy implantant seulement les opération non-
optionnel devant une collection modifiable
ArrayList<String> list = new ArrayList<>();
List<String> list2 = Collections.unmodifiableList(list);
list2.add("hello"); // UnsupportedOperationException
list.add("hello"); // ok
list2.size(); // renvoie 1
Cela permet d'éviter les copie défensive !
ENSAM 2020-2021- T.HAJJI- 291
Object ou E ?
Pourquoi remove(Object) et add(E) ?
A cause des interfaces interface I {}
interface J {}
class A implements I, J {}
public static void main(String[] args) {
Collection<I> collection = ...
A a = new A();
collection.add(a);// ok, A est un sous-type de I J
j = a;
collection.remove(j); // doit être valide
}
ENSAM 2020-2021- T.HAJJI- 292
Recherche
java.util.Collection possède une méthode de recherche
boolean contains(Object)
Renvoie vrai si l'objet est stocké dans la collection
Note sur la complexité:
contains (ou add, remove, etc.) a une complexité différente suivant
la structure de donnée
contains pour une HashSet est O(1), pour un TreeSet est O(ln n) et pour une
ArrayList O(n)
ENSAM 2020-2021- T.HAJJI- 293
Les méthodes de object
equals, hashCode et toString sont implantés et
délègue à l'implantation des élements de la
collection
Cela permet d'ajouter une collection à une
collection mais dans ce cas la collection ajoutée ne
doit pas être modifiée à postériori !
Ajouter des objets mutables à une collection est
poetentiellement dangereux !
ENSAM 2020-2021- T.HAJJI- 294
Exemple
ArrayList<String> list = new ArrayList<>();
HashSet<List<String>> set = new HashSet<>();
set.add(list);
list.add("hello");
set.contains(list); // false
note, si on a pas de chance, set.contains(list) peut renvoyer
vrai car même si la valeur de hashCode a changé, la liste
peut être rangée dans la même case de la table de hachage
ENSAM 2020-2021- T.HAJJI- 295
Bulk (opérations groupées)
Les méthodes groupés:
boolean addAll(Collection<? extends E>)
ajoute tous les élements de la collection dans this
boolean removeAll(Collection<?>)
supprime les élements de this qui sont aussi dans la collection
boolean retainAll(Collection<?>)
retient dans this les élements qui appartiennent à this et à la collection (intersection)
boolean containAll(Collection<?>)
renvoie vrai si this contient tous les élements de la collection
ENSAM 2020-2021- T.HAJJI- 296
Bulk
addAll(), par exemple, est équivalent à
public boolean addAll(Collection<? extends E> c) {
boolean result = false;
for(E element: c) {
result |= this.add(element);
}
return result;
}
mais peut être écrite de façon plus efficace en fonction de
l'implantation
ENSAM 2020-2021- T.HAJJI- 297
Les concepts de collections
Il existe 4 sous-interfaces
– Set, ensemble sans doublon
– List, les listes ordonées et indexées
– Queue, les files (FIFO)
– Deque, les files “double ended”
ENSAM 2020-2021- T.HAJJI- 298
java.util.Set
Ensemble d'élements sans doublons
Par ex. les options de la ligne de commande
Exactement la même interface que Collection, la sémantique
des méthodes est pas la même
par ex, add() renvoie false si doublons
ENSAM 2020-2021- T.HAJJI- 299
Implantations de Set
HashSet
table de hachage
Ensemble sans ordre, add/remove en O(1)
LinkedHashSet
Table de hachage + list chainée
Ordre d'insertion (ou d'accès), add/remove en O(1)
TreeSet
Arbre rouge/noir (ordre de comparaison)
Ordre par un comparateur, add/remove en O(ln n)
EnumSet
Bit set
Ordre des valeurs de l'enum, add/remove en O(1)
ENSAM 2020-2021- T.HAJJI- 300
Exemple
Détecter des doublons dans les arguments de la ligne de commande
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
for(String arg: args) {
if (!set.add(arg)) {
System.err.println("argument " + arg + " specified
twice"); return;
}
}
}
ENSAM 2020-2021- T.HAJJI- 301
java.util.List
Liste d'élement indexé conservant l'ordre d'insertion
Par ex, la liste des vainqueurs du tour de France
Méthodes supplémentaires
E get(int index), E set(int index, E element)
accès
● en utilisant un index
int indexOf(E element), lastIndexOf(E element)
comme
● contains mais qui renvoie un index ou -1
void sort(Comparator<? Super E>)
Trie
● en fonction d'un ordre de comparaison
ENSAM 2020-2021- T.HAJJI- 302
Implantations de List
ArrayList
Tableau dynamique
Ajout à la fin en 0(1), ajout au début en O(n), accès indexé en O(1)
LinkedList
List doublement chaînée
Ajout à la fin en 0(1), ajout au début en O(1), accès indexé en O(n)
ENSAM 2020-2021- T.HAJJI- 303
Problème de l'interface List
java.util.List est pas une interface dangereuse en terme de
complexité
–Accès indexé à une LinkedList est en O(n)
–ajouter un élement en tête d'une ArrayList est en O(n)
On accède pas de façon indexée à une java.util.List, pas de
problème si c'est une ArrayList
ENSAM 2020-2021- T.HAJJI- 304
java.util.RandomAccess
Les listes à accès indexées en tant constant doivent implanter
java.util.Random (marker interface)
Il est possible de tester à l'exécution l'accès indexée est en temps
constant list instanceof RandomAccess
Note: ce n'est pas exactement ce que dit la doc de l'interface
RandomAccess mais c'est vrai en pratique
ENSAM 2020-2021- T.HAJJI- 305
Liste ou tableau ?
Si on connait le nombre d'éléments, on utilise plutôt un
tableau car c'est plus efficace
Mais un tableau de type paramétré est unsafe
Il est habituelle dans du code utilisateur d'utiliser
List<Set<String>> au lieu de Set<String>[]
Il est aussi possible de voir un tableau comme une liste (cf
plus tard)
ENSAM 2020-2021- T.HAJJI- 306
Exemple
private static int sum(List<Integer> list) {
int sum = 0;
for(int i = 0; i < list.size(); i++) { sum += Ne jamais écrire ça !
list.get(i);
}
return sum;
}
public static void main(String[] args) {
List<Integer> list;
if (args[0].equals("linked")) {
list = new LinkedList(1_000_000);
} else {
list = new ArrayList(1_000_000);
}
for(int i = 0; i < 1_000_000; i++) {
list.add(i);
} System.out.println(sum(list)); // si list est une LinkedList, c'est long !
}
ENSAM 2020-2021- T.HAJJI- 307
java.util.Queue
Représente une file d'élements FIFO
(la convention est ajout à la fin et retire au début)
– Par ex, une file d'attente
Méthodes supplémentaires
boolean offer(E)
ajoute à la fin, renvoie vrai si ajouté
E poll()
retire en tête, null si vide
E peek()
regarde l'élement en tête sans le retirer, null si vide
ENSAM 2020-2021- T.HAJJI- 308
Implantations de Queue
ArrayDeque
Tableau dynamique circulaire
Ajout/suppression en O(1)
PriorityQueue
Tas (ordre de comparaison)
Ajout/suppression en O(ln n)
LinkedList
Liste double chainée
Ajout/suppression en O(1)
ENSAM 2020-2021- T.HAJJI- 309
Autre sémantique
En plus de offer/poll/peek, il existe 3 autres méthodes
qui ont la même sémantique mais plante au lieu de
renvoyer null/false, elles sont peu utilisées en pratique
boolean add(E element)
comme offer() mais lève IllegalArgumentException si plein
E remove()
comme poll() mais lève NoSuchElementException si vide
E element()
comme peek() mais lève NoSuchElementException si vide
ENSAM 2020-2021- T.HAJJI- 310
Exemple
public class Tree {
private final int value;
private final Tree left, right;
public Tree(int value, Tree left, Tree right) { t
his.value = value;
this.left = left;
this.right = right;
}
public void breadthSearch(Consumer<? super Tree> consumer) {
ArrayDeque<Tree> queue = new ArrayDeque<>();
queue.offer(this);
while (!queue.isEmpty()) {
Tree tree = queue.poll();
consumer.accept(tree);
Tree left = tree.left, right = tree.right;
if (left != null) { queue.offer(left); }
if (right != null) { queue.offer(right); }
}
}
public static void main(String[] args) {
Tree tree = ...
tree.breadthSearch(System.out::println);
}
}
ENSAM 2020-2021- T.HAJJI- 311
java.util.Deque
Sous-type de Queue qui permet d'ajouter ou retirer en début ou en fin de
la file
Méthodes supplémentaires
boolean offerFirst(E), boolean offerLast(E)
ajouté au début ou à la fin
E pollFirst(), E pollLast()
retirer au début ou à la fin
E peekFirst(E), E peekLast()
voir sans retirer au début ou à la fin et aussi addFirst(E), addLast(E), E removeFirst(), E
removeLast(), E getFirst(), E getLast()
ENSAM 2020-2021- T.HAJJI- 312
Map
Appelée aussi table associative ou dictionnaire une
map fonctionne avec deux élements
–un élement clé (de type K) et
–un élement valeur (de type V)
Les clés insérés n'ont pas de doublons
Il est possible d'avoir des doublons au niveau des
valeurs
ENSAM 2020-2021- T.HAJJI- 313
java.util.Map<K,V>
méthodes
V put(K key, V value)
insére un couple clé/valeur et supprime si il existe le couple clé/valeur
précédent ayant la même clé, renvoie l'ancienne valeur ou null
V get(Object key)
renvoie la valeur correspondant à une clé ou null si il n'y a pas de couple
clé/valeur ayant la clé key
isEmpty() et size()
renvoie si la map est vide/le nombre de couples clé/valeur
ENSAM 2020-2021- T.HAJJI- 314
Implantations
HashMap
Table de hachage sur les clés
Les couples ne sont pas ordonées, insertion/accès/suppression en O(1)
LinkedHashMap
Table de hachage sur les clés + liste doublement chaînée
Couples ordonnées par ordre d'insertion (ou d'accès), insertion/accès/suppression en O(1)
TreeMap
Arbre rouge noir (ordre de comparaison)
Couple ordonée suivent l'ordre de comparaison, insertion/accès/suppression en O(ln n)
IdentityHashMap
Table de hachage fermé, ==/System.identityHashCode pour le test égalité/hashCode
Couple non ordonée, insertion/accès/supression en O(1)
WeakHashMap
Table de hachage avec clés stocké dans des références faibles
Couple non ordonée, insertion/accès/supression en O(1)
ENSAM 2020-2021- T.HAJJI- 315
Obtenir un couple
en plus de get(), il existe les méthodes
V getOrDefault(V defaultValue)
permet de renvoyer une valeur par défaut au lieu de null comme pour get
V computeIfAbsent(K key, Function<K,V> fun)
renvoie la valeur associée à la clé, si il n'y a pas de valeur, appel la fonction
pour obtenir valeur et enregistre le couple clé/valeur (pratique pour un
cache)
boolean containsKey()
permet de savoir si la clé existe, très peut utilisé car
–soit cela veut dire que l'on veut un Set
–soit que l'on va faire un get() derrière dans ce cas autant faire un get() uniquement
ENSAM 2020-2021- T.HAJJI- 316
Insérer/supprimer un couple
en plus de put(), il existe les méthodes
V putIfAbsent(K key,V value)
comme put mais n'ajoute pas un couple existe déjà
V remove(Object key)
boolean remove(Object key, Object value)
supprime un couple (à partir de la clé, ou du couple)
V replace(K key, V value)
boolean replace(K key, V oldValue, V newValue)
remplace un couple (à partir de la clé, ou du couple)
ENSAM 2020-2021- T.HAJJI- 317
bulk
Opérations groupées
void putAll(Map<? extends K, ? extends V> map)
insére tous les élements de map dans this
void replaceAll(BiFunction<? super K, ? super V,
? extends V> fun)
remplace toutes les valeurs des couples existant dans la Map par de nouvelles
fournies pas la fonction
Il n'y a pas de removeAll(map) car cela peut être fait sur
l'ensemble des clés (cf suite du cours)
ENSAM 2020-2021- T.HAJJI- 318
Map.Entry<K,V>
interface interne de l'interface Map, représente des couples
clé/valeur mutable
Opérations
K getKey()
renvoie la clé du couple
V getValue()
renvoie la valeur du couple
V setValue(V value)
change la valeur du couple (opération mutable optionelle)
ENSAM 2020-2021- T.HAJJI- 319
Implantations de Map.Entry
Map.Entry possède deux implantations, déclarés
bizarrement en tant que classe interne de AbstractMap
implantent aussi equals/hashCode/toString
AbstractMap.SimpleImmutableEntry<K,V>
version non mutable, setValue jète une UOE
AbstractMap.SimpleEntry<K,V>
version mutable, equals/hashCode/toString passe par les getters et donc ils
peuvent être redéfinie
ENSAM 2020-2021- T.HAJJI- 320
Exemple
Calculer un histogramme d'occurences
public static <T> void histo(
List<? extends T> list, Map<? super T, Integer> map) {
for(T element: list) {
map.put(element, 1 + map.getOrDefault(element, 0));
}
}
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
histo(Arrays.asList(args), map);
System.out.println(map);
} ENSAM 2020-2021- T.HAJJI- 321
Exemple
public class Trie {
private boolean terminal;
private final HashMap<Character,Trie> map = new HashMap<>();
public void add(String s) {
Trie trie = this;
for(int i = 0; i < s.length(); i++) {
char letter = s.charAt(i);
trie = trie.map.computeIfAbsent(letter, l -> new Trie());
}
trie.terminal = true;
}
public boolean contains(String s) {
Trie trie = this;
for(int i = 0; i < s.length(); i++) {
char letter = s.charAt(i);
trie = trie.map.get(letter);
if (trie == null) {
return false;
}
}
return trie.terminal;
}
}
ENSAM 2020-2021- T.HAJJI- 322
Pont et vue entre les collections
Il existe deux types méthodes de “conversions”
–Copier les données d'une structure vers une nouvelle
les données sont dupliquer dans la nouvelle structure
–Voir une structure de donnée comme une autre
les données reste dans la structure initiale et sont aussi vue dans la
nouvelle structure, on parle de vue
ENSAM 2020-2021- T.HAJJI- 323
Interopération par copie
Collection vers les tableaux
Object[] Collection.toArray()
Créer un tableau d'Object
<T> T[] Collection.toArray(T[] array)
Utilise le tableau pris en paramètre, ou crée un nouveau tableau si le tableau
est trop petit
– si le tableau est trop grand, on met null après le dernier élement ??
ENSAM 2020-2021- T.HAJJI- 324
Interopération par copie
Collection vers Collection
Toutes les collections ont un constructeur qui prend une
Collection<? extends E>
Sur une collection addAll(Collection<? extends E>) permet
d'ajouter
Tableau vers Collection
<T> Collections.addAll(Collection<? super T> coll, T... array)
ENSAM 2020-2021- T.HAJJI- 325
Interopération par vue
Tableau vers List
<T> List<T> Arrays.asList(T... array)
List vers List
list.subList(int start, int end) permet d'obtenir une sous-liste d'une liste
Map vers Set
Set<E> Collections.newSetFromMap(
Map<E, Boolean> map)
Queue vers Queue
Queue<T> Collections.asLifoQueue(
Deque<T> deque)
ENSAM 2020-2021- T.HAJJI- 326
Interopération par vue
Map vers l'ensemble des clés
Set<K> map.keySet()
Map vers la collection des valeurs
Collection<V> map.values()
Map vers l'ensemble des couples clé/valeur
Set<Map.Entry<K,V>> map.entrySet()
ENSAM 2020-2021- T.HAJJI- 327
Exemple
Il n'existe pas de méthode contains() ou shuffle() qui prend en
paramètre un tableau dans java.util.Arrays mais en utilisant une
vue ...
public static void main(String[] args) {
List<String> list = Arrays.asList(args);
list.contains("bingo");
// search if "bingo" is contained in args
Collections.shuffle(list);
// the array args is now suffled
}
ENSAM 2020-2021- T.HAJJI- 328
Itération Interne vs Externe
Il existe deux types d'itération sur une collection
–Itération interne
On envoie du code (sous forme de lambda) à exécuter par la structure de donnée,
la structure parcours sa structure et pour chaque élement appel la lambda avec
l'élement en paramètre
–Itération externe
On demande à la collection un curseur (un Iterator) qui va servir à parcourir la
collection en retournant un élement à chaque appel
java.util.Iterable sert d'interface pour les 2 façons d'itérer
ENSAM 2020-2021- T.HAJJI- 329
Itération interne
Iterable.forEach(Consumer<? super E> consumer) avec
@FunctionalInterface
public interface Consumer<T> {
public void accept(T t);
}
exemple : List<String> list = ...
list.forEach(e -> System.out.println(e));
ENSAM 2020-2021- T.HAJJI- 330
Itération interne sur une Map
Map.forEach(BiConsumer<? super K, ? super V> bc) avec
@FunctionalInterface
public interface BiConsumer<T, U> {
public void accept(T t, u u);
}
exemple :
Map<String, Option> map = ... map.forEach((key, value) -> {
System.out.println("key: " + key + " value: " + value);
});
ENSAM 2020-2021- T.HAJJI- 331
Iteration externe
Iterator<E> Iterable.iterator()
avec
public interface Iterator<E> {
public boolean hasNext(); existe t'il un élement suivant ?
la méthode n'a pas d'effet de bord
public E next();
}
renvoie l'élement courant et passe au
suivant ou NoSuchElementException
exemple :
List<String> list = ...
Iterator<String> it = list.iterator(); // on récupère le 'parcoureur'
while(it.hasNext()) {
System.out.println(it.next());
}
ENSAM 2020-2021- T.HAJJI- 332
Position de l'itérateur
A la création :
e1 e2 e3 e4 e5 e6
Itérateur au début
Après un appel à iterator.next() :
e1 e2 e3 e4 e5 e6
L'itérateur est après le premier élement
Si hasNext() renvoie false :
e1 e2 e3 e4 e5 e6
Itérateur est après le dernier élement
ENSAM 2020-2021- T.HAJJI- 333
Syntaxe pour l'itération externe
La boucle for(type var: iterable) sur un Iterable
est transformé en
Iterator<type> it = iterable.iterator(); while (it.hasNext()) {
type var = it.next();
...
}
Attention à ne pas confondre avec
for(type var: array) qui parcours le tableau avec des index
ENSAM 2020-2021- T.HAJJI- 334
Ecrire son itérateur
2 phases
–On écrit le code itératif sans itérateur
–On répartit le code dans l'itérateur
Par exemple, avec un code d'itération interne
public class MyArray<E> {
private final E[] array;
...
public void forEach(Consumer<? super E> consumer) {
for(int i = 0; i < array.length; i++) {
E element = array[i]; consumer.accept(element);
}
}}
ENSAM 2020-2021- T.HAJJI- 335
Ecrire son itérateur (2)
public class MyArray<E> { private final E[] array;
...
public void forEach(Consumer<? super E> consumer) { for(int i
= 0; i < array.length; i++) {
E element = array[i]; consumer.accept(element);
}
}
condition
public Iterator<E> iterator() { return new Iterator<E>() { public
boolean hasNext() {
...
}
public E next() {
...
renvoie de l'élement +
} incrémentation
}
}
}
ENSAM 2020-2021- T.HAJJI- 336
Ecrire son itérateur (3)
public class MyArray<E> { private final E[] array;
...
public void forEach(Consumer<? super E> consumer) { for(int i
= 0; i < array.length; i++) {
E element = array[i];
consumer.accept(element);
}
}
public Iterator<E> iterator() {
return new Iterator<E>() {
private int i = 0;
public boolean hasNext() { La variable locale i se
return i < array.length; transforme en champ
}
public E next() {
E element = array[i]; i++;
return e;
}
}
}
}
ENSAM 2020-2021- T.HAJJI- 337
Attention cet Iterateur est FAUX !
Interface Iterator !
L'interface Iterator n'oblige pas à ce que
hasNext() et next() soit appelé dans cette ordre
Il faut vérifier qu'un code utilisateur puisse
appeler
–hasNext() plusieurs fois sans appel à next()
–next() sans appeler une seul fois hasNext()
ENSAM 2020-2021- T.HAJJI- 338
Ecrire son itérateur (3)
public class MyArray<E> { private final E[] array;
...
public void forEach(Consumer<? super E> consumer) { for(int i
= 0; i < array.length; i++) {
E element = array[i];
consumer.accept(element);
}
} next() peut être appelée sans
public Iterator<E> iterator() { return new Iterator<E>() { private appel à hasNext()
int i = 0;
public boolean hasNext() { return i < array.length;
}
public E next() {
if (!hasNext()) { throw new NoSuchElementException(); }
E element = array[i]; i++;
return e;
}
}
}
}
ENSAM 2020-2021- T.HAJJI- 339
Mutation de la collection et itération
Il est interdit d'effectuer une mutation sur la structure de
donnée durant l'itération !
Itération interne : Set<String> set = ... set.forEach(e ->
set.add(e));
// throws ConcurrentModificationException
Itération externe : Set<String set = ... for(String e: set) {
set.add(e);
}
// throws ConcurrentModificationException
ENSAM 2020-2021- T.HAJJI- 340
ConcurrentModificationException
Exception levée lorsque durant le parcours, on modifie la
structure de donnée sur laquelle on itére
Le nom de l'exception est vraiment pas terrible
Concurrent veut dire normalement accéder par différents fils
d'exécution (thread) mais ce n'est pas le cas ici :
La technique qui consiste lors du parcours à regarder si une
mutation a été effectuée ou pas est appelée fail-fast.
ENSAM 2020-2021- T.HAJJI- 341
Mutations lors du parcours
Il est possible d'éviter la lévée de
ConcurrentModificationException dans le cas d'une
itération externe, car il est possible de faire les mutations
directement sur l'itérateur
–Iterator possède une méthode remove()
–ListIterator qui est une version spécialiser de l'itérateur (et donc
hérite de Iterator) pour les listes possèdes en plus les méthodes
add() et set()
ENSAM 2020-2021- T.HAJJI- 342
Iterator.remove
Méthode par défaut dans Iterator
Supprime l'élement qui a été renvoyer
précédemment par next()
●
donc next() doit être appelé d'abord
●
Il n'est donc pas possible de faire 2 removes de suite sans
appeler next() entre les deux
●
Lève IllegalStateException si next pas appelé avant
L'implantation par défaut (Java8) dans Iterator lève
UnsupportedOperationException
ENSAM 2020-2021- T.HAJJI- 343
Exemples
Exemple qui ne marche pas (CME) :
LinkedList<String> list = ... for(String s: list) {
if (s.length() % 2 == 0) {
list.remove(s); // la complexité est affreuse aussi
}
}
Exemple qui marche : Iterator<String> it = list.iterator();
while(it.hasNext()) {
String s = it.next();
if (s.length() % 2 == 0) { it.remove();
}
}
ENSAM 2020-2021- T.HAJJI- 344
Iteration à l'envers
Sur une List, il est possible de parcourir celle-ci du dernier élement au
premier en utilisant le ListIterator
Exemple: List<String> list = ...
ListIterator<String> it = list.listIterator(list.size);
// on place l'itérateur à lafin
while(it.hasPrevious()) {
String s = it.previous();
...
}
ENSAM 2020-2021- T.HAJJI- 345
Parcours indexé
Attention, le parcours indexé peut être mortel
Exemple :
List<String> list = ...
for(int i = 0, i < list.size(); i++) { String s = list.get(i);
...
}
Si list.get() est pas en temps constant, alors la complexité est
O(n2)
L'itération external ce se fait avec l'Iterator à part si on est sûre
que la liste implante RandomAccess.
ENSAM 2020-2021- T.HAJJI- 346
Itération interne vs externe
L'itération interne est souvent plus efficace car
–Il n'y a pas deux appels (hasNext()/next())
–Les checks de mutation (failfast) peuvent être fait une seul fois à la fin
mais comme on envoie une lambda à iterable.forEach(), on est
limité par le fait qu'une lambda n'est pas une closure
il n'est pas possible d'effectuer des mutations sur les variables locales à
l'intérieur d'une lamnbda
L'itération externe permet aussi la composition d'itérateurs
ENSAM 2020-2021- T.HAJJI- 347
Ordre et trie
Pour pouvoir trier ou ordoner des objects, il faut une
fonction de comparaison
Il y a deux sortes de façon de specifier un ordre en Java
–Ordre des élements, l'ordre est définie sur les élements de la
collection grâce à l'interface Comparable
–Ordre
externe, l'ordre est spécifier par un objet externe un
Comparator
ENSAM 2020-2021- T.HAJJI- 348
Ordre des élements
Ordre des élements ou ordre naturel est spécifié en
implantant l'interface Comparable
public interface Comparable<E> {
int compareTo(E element);
}
a.compareTo(b) < 0 <=> a < b
a.compareTo(b) > 0 <=> a > b
a.compareTo(b) == 0 <=> a == b
CompareTo doit marcher avec equals,
a.compareTo(b) <=> a.equals(b) == true
ENSAM 2020-2021- T.HAJJI- 349
Ordre extérieur
Un ordre extérieur est une fonction de
comparaison externe. Cela permet de spécifier un
autre ordre que l'ordre naturel.
public interface Comparator<T> {
int compare(T t1, T t2);
}
compare doit aussi être compatible avec equals,
compare(a, b) == 0 <=> a.equals(b) == true
ENSAM 2020-2021- T.HAJJI- 350
Exemple
En utilisant l'ordre naturel, java.lang.String
implante l'interface Comparable
String[] array = new String[] { "foo", "bar" };
Arrays.sort(array);
En utilisant un ordre externe
List<String> list = Arrays.asList("foo", "bar");
list.sort((s1, s2) -> s1.trim().compare(s2.trim());
ENSAM 2020-2021- T.HAJJI- 351
Implanter un Comparable ou un
Comparator
Trouver les 2 bugs ?
public class Point implements Comparable<Point> {
private final int x;
private final int y;
public Point(int x, int y) { … }
public int compareTo(Point p) {
int dx = x – p.x;
if (dx != 0) { return dx; }
return y – p.y;
}
} ENSAM 2020-2021- T.HAJJI- 352
Implanter un Comparable ou un
Comparator
Il manque le equals() et il y a un problème d'overflow
public class Point implements Comparable<Point> {
private final int x;
private final int y;
public int compareTo(Point p) {
int dx = Integer.compare(x, p.x);
if (dx != 0) { return dx; }
return Integer.compare(y, p.y);
}
public boolean equals(Object o) {
if (!(o instanceof Point)) { return false; }
Point p = (Point)o;
return x == p.x && y == p.y;
}
}
ENSAM 2020-2021- T.HAJJI- 353
Comparaison par rapport à un champs
• Comparator possède des méthodes statique
comparing(), comparingInt(), etc. de comparaison
par rapport à un champ
• public class Author { private final String name;
• public String getName() { return name; }
• ...
• }
• List<Author> list = ...
list.sort(Comparator.comparing(Author::getName));
ENSAM 2020-2021- T.HAJJI- 354
Trier
Il y a deux solutions
Utiliser une collection qui garde un ordre new
TreeSet<E>(Comparator<? super E> c) new TreeMap<K,V>(Comparator<?
super K> c)
On a un ordre uniquement sur les clés
Trier une collection
list.sort(Comparator<? super E> c)
<T> Arrays.sort(T[], Comparator<? super T> c)
<T> Arrays.parallelSort(T[], Comparator<? super T> c)
Ils existent aussi des versions pour les types primitifs
ENSAM 2020-2021- T.HAJJI- 355
BinarySearch
Pour faire une recherche dichotomique (couper en deux)
sur un tableau
Arrays.binarySearch(T[] array, T element,
Comparator<? super T> c)
sur une liste
<T> Collections.binarySearch(List<? extends T> list,
T element, Comparator<? super T> c)
Il faut pas oublier que la structure doit être triée !
ENSAM 2020-2021- T.HAJJI- 356
NavigableSet
interfaces sous-type de Set qui permet d'obtenir pour une valeur
l'élement précédent ou suivant de la collection
Opérations
E floor(E) <=, lower(E) <, ceiling(E) >=, higher(E) >
●
Recherche d'un élement
NavigableSet<E> headSet(E element, boolean inclusive)
NavigableSet (vue) entre first() et element
NavigableSet<E> subSet(E from, boolean, E to, boolean)
NavigableSet (vue) entre from et to
NavigableSet<E> tailSet(E element, boolean inclusive)
NavigableSet (vue) entre element et last()
ENSAM 2020-2021- T.HAJJI- 357
NavigableMap
interfaces sous-type de Map,
permet d'obtenir pour une valeur (de type clé K) le couple
clé/valeur précédent/suivant
Opérations
Entry<K,V> floorEntry(K) <=, lowerEntry(K) <, ceilingEntry(K) >=,
higherEntry(K) >
NavigableMap<K,V> headMap, subMap, tailMap
NavigableKeySet<K> navigableKeySet()
Ensemble des clés sous forme d'un navigable set
NavigableMap<K,V> descendingMap()
this mais en ordre décroissant
ENSAM 2020-2021- T.HAJJI- 358
Legacy collection
java.util existe depuis 1.0 mais API des collections
existe que depuis la version 1.2
Vector, Stack, Hashtable et Enumeration sont des
anciennes classes
–qui ne doivent plus utilisé à part pour discuter avec du
code legacy
–Les 4 premières ont des problèmes de performance car
leurs méthodes sont synchronized
ENSAM 2020-2021- T.HAJJI- 359
Classe de remplacement
Les classes de remplacement ont la même API (ou
une API très similaire) que les classes legacy
Vector -> ArrayList
Stack -> ArrayDeque
Hashtable -> HashMap
Enumeration -> Iterator
plus la méthode Collections.list(enumeration) -> List
ENSAM 2020-2021- T.HAJJI- 360
Exemple
Récupérer l'ensemble des interfaces réseaux
Enumeration<NetworkInterface> enumeration =
NetworkInterface.getNetworkInterfaces();
List<NetworkInterface> interfaces =
Collections.list(enumeration); // hop on triche
for(NetworkInterface networkInterfaces: interfaces) {
System.out.println(networkInterfaces);
}
ENSAM 2020-2021- T.HAJJI- 361
Abstract helper
Si l'on veut créer sa propre collection pour par exemple
implanter une vue, il existe déjà des classes abstraites que
l'on peut redéfinir
–AbstractCollection,
–AbstractSet
–AbstractQueue
–AbstractMap
–AbstractList / AbstractSequentialList
Pour les listes AbstractSequentialList hérite de AbstractList ce qui est une
erreur de design !
ENSAM 2020-2021- T.HAJJI- 362
Abstract helper
Les classes abstraites
–Demande d'implanter une ou deux méthodes abstraites
–Fournissent une implantation des autres méthodes non
optionelles en utilisant l'implantation des méthodes abstraites
Comme les méthodes optionnels ne sont pas implanter,
la collection est par défaut non mutable
pour avoir une version mutable il faut aussi redéfinir les
méthodes mutables
ENSAM 2020-2021- T.HAJJI- 363
Methodes à implanter
Pour
– AbstractCollection/AbstractSet
● size() et iterator()
– AbstractMap
●
entrySet()
– Attention get sera en O(n) !
– AbstractList
● size() et get(int)
– AbstractSequentialList
● size() et listIterator(int)
ENSAM 2020-2021- T.HAJJI- 364
Exemple
Renvoie une liste dont chaque élement est calculé par la fonction de projection (mapper) à partir
de l'élement dans la liste initiale
public static <T,U> List<U> map(List<T> list, Function<? super T, ? extends U> mapper) {
class MappedList extends List<U> implements RandomAccess {
@Override
public int size() {
return list.size();
}
@Override
public U get(int index) {
return mapper.apply(list.get(index));
}
}
return new MappedList();
}
ENSAM 2020-2021- T.HAJJI- 365
ArrayList
Tableau dynamique (qui s'agrandit tout seul)
facteur à 1.5 par défaut
Complexité
– Insertion à la fin en 0(1) amortie
– Insertion en début ou au millieu en O(n)
– Accès au index-ième élement en O(1)
Parcours:
– Avec un itérateur O(n)
– Avec un index, O(n) mais plus rapide
ENSAM 2020-2021- T.HAJJI- 366
LinkedList
Liste doublement chaînée
Complexité
–Insertion au début ou fin en O(1)
–Insertion au millieu
● avec add O(n)
●
avec listIterator.add 0(1)
– Accès au index-ième élement en O(n)
Parcours:
– Avec un itérateur O(n)
– Avec un index, O(n2) !!!
ENSAM 2020-2021- T.HAJJI- 367
ArrayDeque
Buffer circulaire dynamique à deux pointeurs
–Taille en puissance de 2
Complexité
–Insertion/suppression au début/à la fin en 0(1) amortie
Parcours:
–Avec un itérateur O(n)
ENSAM 2020-2021- T.HAJJI- 368
HashMap
Table de hachage dynamique avec liste chainée ou arbre
pour gérer les collisions
–Agrandissement si plein a plus de 75%
–taille en puissance de 2
–Protection DDOS
Complexité
–Insertion/suppression/accès 0(1) amortie
Parcours:
–Avec un itérateur O(n)
sur le résultat de keySet(), entrySet() ou values()
ENSAM 2020-2021- T.HAJJI- 369
WeakHashMap
Table de hachage dynamique
– Les clés sont stockées avec des références faibles
(pas compté par le GC)
● Bug: marche pas si la clé est référencé par la valeur (pas
d'ephemeron :( )
peut être utilisé comme un cache qui se vide
automatiquement si il n'y a plus assez de mémoire
–Attention, à ne pas mettre trop d'élément !
–Attention, le cache doit se recréer incrémentalement !
ENSAM 2020-2021- T.HAJJI- 370
IdentityHashMap
Table de hachage dynamique fermée
–Utilise== et System.identityHashCode à la place de equals et
hashCode
–Gestion des collisions l'intérieur de la table
–Utiliseun seul tableau avec les clés et les valeurs à des index
consécutif
Même complexité que HashMap
–Plus lent si beaucoup d'élement
–Consomme moins de mémoire si peu d'élement
ENSAM 2020-2021- T.HAJJI- 371
TreeMap
Arbre rouge/noir, maintient l'ensemble des clés triés
–Auto-équilibrage donc hauteur en ln(n)
Complexité
–Insertion/suppression/accès 0(ln(n)) amortie
Parcours:
–Avec un itérateur O(n)
sur le résultat de keySet(), entrySet() ou values()
ENSAM 2020-2021- T.HAJJI- 372
EnumSet
Set spécialisée si les élements viennent tous du
même enum
–ordinal() est une fonction de hash parfait
–Deux implantations
Si enum.values().length <=64, utilise 1 long (regular) sinon utilise
un tableau de long (jumbo)
Même complexité que HashSet mais
représentation en mémoire très compacte
ENSAM 2020-2021- T.HAJJI- 373
EnumMap
Map spécialisée si les clés viennent tous du même
enum, utilise un tableau de taille fixe.
– ordinal() est une fonction de hash parfait
Même complexité que HashMap mais
représentation en mémoire très compacte
ENSAM 2020-2021- T.HAJJI- 374
Java MySQL
• ODBC JAVA MySQL
• Chargement du driver ,
• La classe Connexion,
• La classe DriverManager,
• La classe Statement et PreparStatment
• La classe ResultSet
• Le bloc static dans une classe
ENSAM 2020-2021- T.HAJJI- 375
Le problème de l’accès aux données sans
JDBC
• Java est un excellent candidat pour le
développement d’applications de bases de
données:
– robuste et sécurisé
– facile à comprendre
– automatiquement téléchargeable par le réseau
• mais avant JDBC, il était difficile d’accéder à
des bases de données SQL depuis Java :
– obligé d’utiliser des API natives comme ODBC
376
ENSAM 2020-2021- T.HAJJI- 376
Objectifs de JDBC
Permettre aux programmeurs Java d’écrire un
code indépendant de la base de données et du
moyen de connectivité utilisé
Réalisé par l’API JDBC :
une interface uniforme permettant un accès
homogène aux SGBD
simple à mettre en œuvre
indépendant de la SGBD cible
supportant les fonctionnalités de base du langage
SQL 377
ENSAM 2020-2021- T.HAJJI- 377
Avantages
Liés a Java :
• Portabilité sur de nombreux O.S. et sur de
nombreuses SGBDR (Oracle, Informix,
Sybase, ..)
• Uniformité du langage de description des
applications, des applets et des accès aux bases
de données
• Liberté totale vis a vis des constructeurs
378
ENSAM 2020-2021- T.HAJJI- 378
Qu’est ce que JDBC ?
• Java DataBase Connectivity
• API Java adaptée à la connexion avec les bases de
données relationnelles (SGBDR)
• Fournit un ensemble de classes et d’interfaces
permettant l’utilisation sur le réseau d’un ou
plusieurs SGBDR à partir d’un programme Java.
379
ENSAM 2020-2021- T.HAJJI- 379
L'architecture JDBC standard
• Tout programme comporte 3 composants :
– Le code de l'application (ou de l'applet)
• Les requêtes doivent être au standard JDBC
– Le JDBC Driver Manager
• Fourni par SUN
• Lien entre l'application et les pilotes (ou drivers) JDBC
– Le driver JDBC
• Fourni par le fournisseur de la base ou autre
• Adapté à la BD
• Convertit les requêtes JDBC en requêtes propres à la base
380
ENSAM 2020-2021- T.HAJJI- 380
Architecture JDBC
381
ENSAM 2020-2021- T.HAJJI- 381
Les types de pilotes JDBC
• 4 types de drivers (taxonomie de JavaSoft) :
– Type I : JDBC-ODBC bridge driver
– Type II : Native-API, partly-Java driver
– Type III : Net-protocol, all-Java driver
– Type IV : Native-protocol, all-Java driver
• Tous les drivers :
– http://www.javasoft.com/products/jdbc/drivers.html
382
ENSAM 2020-2021- T.HAJJI- 382
Les types de pilotes JDBC
• Type 1 ( JDBC-ODBC bridge ) : le pont JDBC-ODBC qui s'utilise avec
ODBC et un pilote ODBC spécifique pour la base à accéder. C'est la
solution idéale pour des développements avec exécution sous Windows
d'une application locale.
• inconvénients :
– la multiplication du nombre de couches rend complexe l'architecture
(bien que transparent pour le développeur) et détériore un peu les
performances
– lors du déploiement, ODBC et son pilote doivent être installé sur
tous les postes ou l'application va fonctionner.
– la partie native (ODBC et son pilote) rend l'application moins
portable et dépendant d'une plateforme.
383
ENSAM 2020-2021- T.HAJJI- 383
Les types de pilotes JDBC
• Type 2 ( Driver d’API natif) :
– fait appel à des fonctions natives (non Java) de l
’API du SGBDR
• gère des appels C/C++ directement avec la base
– fourni par les éditeurs de SGBD et généralement
payant
– ne convient pas aux applets (sécurité)
• interdiction de charger du code natif dans la mémoire vive de la
plateforme d’exécution
384
ENSAM 2020-2021- T.HAJJI- 384
Les types de pilotes JDBC
• Type 3 : un driver écrit en Java utilisant le protocole
natif de la base de données.
– Ce type de driver utilise un protocole réseau propriétaire
spécifique à une base de données.
– Un serveur dédié reçoit les messages par ce protocole et
dialogue directement avec la base de données.
– Ce type de driver peut être facilement utilisé par une
applet mais dans ce cas le serveur intermédiaire doit
obligatoirement être installé sur la machine contenant le
serveur web.
385
ENSAM 2020-2021- T.HAJJI- 385
Les types de pilotes JDBC
• Type 4 : un driver Java natif
– Ce type de driver, écrit en java, appelle directement le
SGBD par le réseau. Ils sont fournis par l'éditeur de la
base de données.
– Avantage : tout le code est chargé sur le poste client
sous forme de classes java.
– Inconvénient : La base de données reste sur le serveur
d’applets.
386
ENSAM 2020-2021- T.HAJJI- 386
Modèles de connexion en Java
2-tiers : 2 entités interviennent
une application Java ou une applet
le SGBDR
Modèle 3-tiers : 3 entités interviennent
une application Java ou une applet
un serveur middleware installé sur le réseau
le SGBDR
387
ENSAM 2020-2021- T.HAJJI- 387
Modèle 2-tiers
Principe :
l’application (ou l’applet) cliente utilise JDBC pour parler
directement avec le SGBD qui gère la base de données
Avantages :
simple à mettre en œuvre
bon choix pour des applications clientes peu évoluées, à
livrer rapidement et n’exigeant que peu de maintenance
Inconvénients :
dépendance forte entre le client et la structure du SGBDR
modification du client si l’environnement serveur change
tendance à avoir des clients « graisseux »
tout le traitement est du côté client
388
ENSAM 2020-2021- T.HAJJI- 388
Architecture 2-tiers
389
ENSAM 2020-2021- T.HAJJI- 389
Modèle 3-tiers
Principes :
le serveur middleware est l’interlocuteur direct du code Java
client; c’est lui qui échange des données avec le SGBDR
pas forcément écrit en Java
si c’est le cas : utilise souvent JDBC pour accéder au SGBDR
Avantages:
le middleware peut ajouter un niveau de sécurité
plusieurs supports pour les échanges avec le client :
sockets, RMI Java, CORBA, …
applets : le SGBDR peut se trouver sur une autre machine:
mais serveur Web et middleware au même endroit
facilite l’utilisation de clients « légers » 390
ENSAM 2020-2021- T.HAJJI- 390
Modèle 3-tiers
391
ENSAM 2020-2021- T.HAJJI- 391
Scénarios d’utilisation
• Scénario 1 :
– architecture 2-tiers avec une application Java
392
ENSAM 2020-2021- T.HAJJI- 392
Scénarios d’utilisation
• Scénario 2 :
– architecture 2-tiers avec une applet Java
393
ENSAM 2020-2021- T.HAJJI- 393
Scénarios d’utilisation
• Scénario 3 :
– architecture 3-tiers et applet/application Java
394
ENSAM 2020-2021- T.HAJJI- 394
L’API JDBC
Est fournie par le package java(x).sql
• permet de formuler et gérer les requêtes aux bases
de données relationnelles
• supporte le standard « SQL-3 Entry Level »
• Classes/interfaces définissant les objets
nécessaires:
– à la connexion à une base éloignée
– et à la création et exécution de requêtes SQL
395
ENSAM 2020-2021- T.HAJJI- 395
L’API JDBC
Disponible en plusieurs versions
JDBC 1 :
Livrée avec JDK 1.1
Tout se trouve dans le package java.sql
JDBC 2 :
2 packages
J2SE contient java.sql (SDK 1.2+)
J2EE contient javax.sql
JDBC 3 :
Avec J2SE, SDK 1.4+
Le cours est basé sur JDBC 1 et 2
396
ENSAM 2020-2021- T.HAJJI- 396
Les classes de l'API JDBC
• Les classes de java.sql :
– Statement
– CallableStatement, PreparedStatement
– DatabaseMetaData, ResultSetMetaData
– ResultSet,
– Connection
– Driver
• De javax.sql :
– RowSet
– Datasource 397
ENSAM 2020-2021- T.HAJJI- 397
LES CLASSES DE L'API JDBC
• Toutes les classes de JDBC sont dans le
package java.sql
Classe Role
DriverManager Charge et configure le driver de la base de données.
Connection Réalise la connexion et l'authentification à la base de
données.
Statement ( et Contient la requête SQL et la transmet à la base de données.
PreparedStatement )
ResultSet Permet de parcourir les informations retournées par la base de
données
dans le cas d'une sélection de données 398
ENSAM 2020-2021- T.HAJJI- 398
La connexion à une base de données
1. Chargement d’un pilote JDBC
2. Définition de l’URL de connexion
3. Etablissement de la connexion
4. Création d’une instruction
5. Exécution de la requête
6. Traitement des résultats
7. Fermeture de la connexion
399
ENSAM 2020-2021- T.HAJJI- 399
Chargement d’un pilote JDBC
• Pour se connecter à une base de données via ODBC, il faut tout d'abord
charger le pilote JDBC-ODBC qui fait le lien entre les deux.
• L’utilisation de la méthode Class.forName() peut lever une exception
de type ClassNotFoundException, il convient donc de placer le
chargement du pilote dans un bloc sécurisé.
• Exemple :
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
}
catch(ClassNotFoundException e) {
System.err.println("Erreur de chargement du driver :" + e);
}
400
ENSAM 2020-2021- T.HAJJI- 400
Définition de l’URL de connexion
Afin de localiser votre serveur ou votre base de
données, il est indispensable de spécifier une
adresse sous forme d’URL de type « jdbc: ».
Pour une connexion à une base de données en
utilisant un driver JDBC, l’URL se compose
comme suit: jdbc:<sous-protocole>:<nom-
BD>?param=valeur, ...
Exemple:
String url=jdbc:odbc:Mabase
ou String jdbcUri =
"jdbc:oracle:thin:@localhost:1521:orcl"; 401
ENSAM 2020-2021- T.HAJJI- 401
Etablissement de la connexion
• La classe DriverManager dispose d’une méthode statique permettant
d’obtenir une connexion à l’URL, la méthode getConnection() qui
retourne un objet de type Connexion.
• Cette méthode peut, si la connexion échoue ou si aucun pilote ne prend en
charge l’URL spécifiée, une exception de type SQLException.
• Exemple :
Import java.sql.* ;
...
try {
Connection con = DriverManager.getConnection(url,userId,password) ;
}
catch(SQLException sqle) {
System.err.println("Erreur lors de la connexion : " + sqle) ;
}
402
ENSAM 2020-2021- T.HAJJI- 402
Les transactions
• Une transaction est un ensemble de traitements qui sont regroupés pour être
validés ou invalidés simultanément
• Par défaut une nouvelle connexion fonctionne en validation automatique «
auto-commit ».
• Chaque opération est validée individuellement.
• Pour gérer des transactions il faut donc modifier l’état par défaut.
• Les transactions seront ensuite validées ou invalidées par l’appel des
méthodes commit() ou rollback().
public abstract void setAutoCommit(boolean autoCommit) throws
SQLException
403
ENSAM 2020-2021- T.HAJJI- 403
Création d’une instruction.
• Afin d’accéder ou de modifier les informations
contenues dans la base de données, il convient
d’utiliser un objet de type Statement.
• Une instance de cet objet est retournée par la
méthode
Statement statement = con.createStatement() ;
Connexion.createStatement() comme
ceci :
404
• 3 types de Statement :
ENSAM 2020-2021- T.HAJJI- 404
– Statement : requêtes statiques simples
Exécution d’une requête
Pour une requête de type interrogation (SELECT),
la méthode à utiliser de la classe Statement est
executeQuery().
Retourne un ResultSet (tuples résultants)
String query = "SELECT * FROM Employés";
ResultSet resultset = statement.executeQuery(query);
Pour des traitements de mise à jour, il faut utiliser
la méthode executeUpdate().
String query = "DELETE FROM Employés WHERE Région = ‘WA’";
Retournent un entier (nombre de tuples traités)
int result = statement.executeUpdate(query) ;
405
ENSAM 2020-2021- T.HAJJI- 405
Traitement du résultat
• Le résultat d'une requête d'interrogation est renvoyé dans un objet de la
classe ResultSet par la méthode executeQuery().
• En ce qui concerne la numérotation des colonnes, elle ne commence pas à 0
comme pour les tableaux JAVA, mais à 1.
• lors de l’exécution de la requête, l’objet ResultSet ne semble pas
positionné sur le premier enregistrement mais avant, dans une zone que l’on
nomme le GAP.
• L’objet ResultSet dispose aussi d’un certain nombre de méthodes
permettant de naviguer d’un enregistrement à un autre.
while(resultset.next()) {
System.out.println(resultset.getString(1)) ;
} 406
ENSAM 2020-2021- T.HAJJI- 406
la classe ResultSet
Les principales méthodes pour obtenir des données sont :
Méthode Rôle
getInt(int) retourne le contenu de la colonne dont le numéro est passé en paramètre sous forme
d'entier.
getInt(String) retourne le contenu de la colonne dont le nom est passé en paramètre sous forme
d'entier.
getFloat(int) retourne le contenu de la colonne dont le numéro est passé en paramètre sous forme
de nombre flottant.
getFloat(String)
getDate(int) retourne le contenu de la colonne dont le numéro est passé en paramètre sous forme
de date.
getDate(String)
next() se déplace sur le prochain enregistrement : retourne false si la fin est atteinte
Close() ferme le ResultSet
407
getMetaData() retourne un objet ResultSetMetaData associé au ResultSet.
ENSAM 2020-2021- T.HAJJI- 407
Fermeture de la connexion
Pour terminer proprement un traitement, il faut
fermer les différents espaces ouverts
sinon le garbage collector s’en occupera mais moins
efficace
Chaque objet possède une méthode close() :
resultset.close();
statement.close();
connection.close();
408
ENSAM 2020-2021- T.HAJJI- 408
Exemple de consultation
import java.sql.*;
public class AffichageBaseDeDonnees {
public static void main(String args[]) {
String n,p,uid;
String url = "jdbc:odbc:BDMUS";
Statement req = null;
Connection con=null;
ResultSet res;
try { Class.forName("Sun.jdbc.odbc.JdbcOdbcDriver");}
catch (Exception e) {
System.out.println("Erreur de chargement du pilote JDBC/ODBD.");
}
try { con = DriverManager.getConnection (url);
req = con.createStatement();
}
catch (Exception e) {
System.err.println("Erreur de connexion à jdbc:odbc:BDID"); }
try { System.out.println("\n >> résultat obtenu : \n");
res= req.executeQuery("select * from identite");
while (res.next()) {
uid = res.getString("id_user");
n = res.getString("Nom");
p = res.getString("Prenom");
System.out.println("\n" + uid + " - " + n + " - " + p + "\n");
}
}
catch (Exception e) { e.printStackTrace(); }
finally { try {con.close();}
catch(SQLException e){e.printStackTrace();} 409
} // fin finally
}//Fin de la fonction main
}//Fin
ENSAMdu programme
2020-2021- T.HAJJI- 409
Exemple de remplissage
import java.sql.*;
import java.io.*;
public class RemplBaseDeDonnees {
public static void main(String args[]) {
DataInputStream din = new DataInputStream(System.in);
String id,N,P,str="";
String url = "jdbc:odbc:BDMUS";
Connection con=null;
PreparedStatement req = null;
try { con = DriverManager.getConnection(url);}
catch (Exception e) { System.err.println("Erreur de connexion à jdbc:odbc:BDID"); }
try { System.out.print("\n\n Debut d'enregistrement : \n");
System.out.print("\nUser_id = ");
id = din.readLine();
System.out.print("\nNom : ");
N = din.readLine();
System.out.print("\nPrenom : ");
P = din.readLine();
str="'"+id+"','"+N+"','"+P+"'";
str="INSERT INTO identite(id_user,Nom,Prenom) values ("+str+")";
req=con.prepareStatement(str);
req.executeUpdate();
}
catch (Exception e) { e.printStackTrace(); }
finally { try {con.close();}
catch(SQLException e) {e.printStackTrace();}
}
}//Fin de la méthode main 410
}//Fin du programme
ENSAM 2020-2021- T.HAJJI- 410
Exemple de suppression
import java.sql.*;
import java.io.*;
public class SuppressionBaseDeDonnees {
public static void main(String args[]) {
DataInputStream din = new DataInputStream(System.in);
String url = "jdbc:odbc:BDMUS",N;
PreparedStatement req=null;
Connection con=null;
try { con = DriverManager.getConnection (url);}
catch (Exception e) { System.err.println("Erreur de connexion à jdbc:odbc:BDID");}
try {
System.out.print("\nNom : ");
N = din.readLine();
req=con.prepareStatement("delete from identite where Nom='"+N+"'");
req.executeUpdate();
}
catch (Exception e) { e.printStackTrace(); }
finally { try {con.close();}
catch(SQLException e) {e.printStackTrace();}
}
} //Fin de la methode main
}//Fin du programme
411
ENSAM 2020-2021- T.HAJJI- 411
Exemple plus pratique
import java.sql.*;
public class Connexion {
private Connection connexion;
private Statement instruction;
protected ResultSet résultat;
String DB="mabase";
public Connexion() { //constructeur
try {
Class.forName("com.mysql.jdbc.Driver");
connexion = DriverManager.getConnection("jdbc:mysql://localhost:3333/mabase",
"root", "");
instruction = connexion.createStatement();
}
catch (ClassNotFoundException ex) {
System.err.println("Problème de pilote");
}
catch (SQLException ex) {
System.err.println("Base de données non trouvée ou requête incorrecte");
}
}
public void lire(String requête) { //--- }
public void miseAJour(String requête) { //--- }
public boolean suivant() { //--- }
} 412
ENSAM 2020-2021- T.HAJJI- 412
Exemple plus pratique
import java.sql.SQLException;
public class ListePersonne extends Connexion {
public ListePersonne(int id) {
lire("SELECT * FROM personne WHERE id="+id+"");
}
public ListePersonne() {
lire("SELECT * FROM personne");
}
public String nom() {
try {
return résultat.getString("nom");
} catch (SQLException ex) {
return "";
}
}
public String prenom() { //-- }
public String email() { //-- }
public int id() { //-- } ListePersonne
}
public ListePersonne()
public ListePersonne(int id)
public String nom()
public String prenom() 413
public String email()
public int id()
ENSAM 2020-2021- T.HAJJI- 413
Exemple plus pratique
public class Main {
public static void main(String[] args) {
ListePersonne LP = new ListePersonne();
while (LP.suivant()) {
System.out.println(LP.prenom());
System.out.println(LP.nom());
System.out.println(LP.email());
}
LP.arrêt();
}
}
ListePersonne
public ListePersonne()
public ListePersonne(int id)
public String nom()
public String prenom() 414
public String email()
public int id()
ENSAM 2020-2021- T.HAJJI- 414
PreparedStatement
Lors de l'envoi d'une requête pour exécution 4 étapes doivent
être faites :
analyse de la requête
compilation de la requête
optimisation de la requête
exécution de la requête
et ceci même si cette requête est la même que la précédente!!
Or les 3 premières étapes ont déjà été effectuées dans ce cas.
Les bases de données définissent la notion de requête
préparée, requête où les 3 premières étapes ne sont
effectuées qu'une seule fois.
Modéliser cette notion: l'interface PreparedStatement
Interface dérive de l'interface Statement.
415
ENSAM 2020-2021- T.HAJJI- 415
PreparedStatement
On ne peut pas avec un Statement construire des requêtes paramétrées.
Il faut pour cela utiliser un PreparedStatement.
Syntaxe:
PreparedStatement pSmt =
conX.prepareStatement("SELECT * FROM Livres" );
ResultSet rs = pSmt.executeQuery();
Exemple :
PreparedStatement pSmt = conX.prepareStatement("SELECT
nom FROM Personnes WHERE age > ? AND adresse = ?" );
pSmt.setInt(1, 22); setType(numéroDeLArgument, valeur)
pSmt.setString(2, "Turin");
ResultSet rs = smt.executeQuery();
416
ENSAM 2020-2021- T.HAJJI- 416
CallableStatement
L'interface CallableStatement définit les
méthodes pour un objet qui va permettre
d'appeler une procédure stockée.
Cette interface hérite de l'interface
PreparedStatement.
Un objet qui implémente l'interface
CallableStatement est obtenu en utilisant
la méthode prepareCall() d'un objet de type
Connection. 417
ENSAM 2020-2021- T.HAJJI- 417
Exécution
On lance l’exécution d’une procédure stockée à l'aide de la syntaxe :
{call nom_procedure_stockees} : cette forme la plus simple
permet l'appel d'une procédure stockée sans paramètre ni valeur de retour
{call nom_procedure_stockees(?, ?, ...)} : cette forme
permet l'appel d'une procédure stockée avec des paramètres
{? = call nom_procedure_stockees(?, ?, ...)} : cette
forme permet l'appel d'une procédure stockée avec des paramètre et une
valeur de retour·
418
ENSAM 2020-2021- T.HAJJI- 418
Exécution
Pour exécuter la requête, l'interface
PreparedStatement propose deux méthodes:
executeQuery() : cette méthode permet d'exécuter
une requête de type interrogation et renvoie un objet de
type ResultSet qui contient les données issues de
l'exécution de la requête·
executeUpdate() : cette méthode permet d'exécuter
une requête de type mise à jour et renvoie un entier qui
contient le nombre d'occurrences impactées par la mise à
jour.
419
ENSAM 2020-2021- T.HAJJI- 419
JDBC 2.0
• La version 2.0 de l'API JDBC a été intégrée au JDK 1.2. Cette
nouvelle version apporte plusieurs fonctionnalités très intéressantes
dont les principales sont :
– support du parcours dans les deux sens des résultats.
– support de la mise à jour des résultats.
– possibilité de faire des mises à jour de masse (Batch Updates)
– prise en compte des champs définis par SQL-3 dont BLOB
(BinaryLargeOBject ) et CLOB (CharacterLargeOBject)
• L'API JDBC 2.0 est séparée en deux parties :
– la partie principale (core API) contient les classes et interfaces nécessaires à
l'utilisation de bases de données : elles sont regroupées dans le package
java.sql·
– la seconde partie est une extension utilisée dans J2EE qui permet de gérer les
transactions distribuées, les pools de connection, la connection avec un objet
DataSource ... Les classes et interfaces sont regroupées dans le package 420
javax.sql·
ENSAM 2020-2021- T.HAJJI- 420
JDBC 2.0
Méthode Rôle
boolean isAfterLast() renvoie un booleen qui indique si la position courante du curseur se trouve
après la dernière ligne.
boolean isFirst() renvoie un booleen qui indique si le curseur est positionné sur la première
ligne
boolean isLast() renvoie un booleen qui indique si le curseur est positionné sur la dernière
ligne
boolean first() déplace le curseur sur la première ligne
boolean last() déplace le curseur sur la dernière ligne
boolean absolute() déplace le curseur sur la ligne dont le numéro est fournie en paramètre à partir
du début si il est positif et à partir de la fin si il est négatif. 1 déplace sur la
première ligne, 1 sur la dernière, -2 sur l'avant dernière ...
boolean relative(int) déplace le curseur du nombre de lignes fourni en paramètre par rapport à la
position courante du curseur. Le paramètre doit être négatif pour se déplacer
vers le début et positif pur se déplacer vers la fin. Avant l'appel de cette
méthode, il faut obligatoirement que le curseur soit positionné sur une ligne.
boolean previous() déplace le curseur sur la ligne précédente. Le booleen indique si la première
occurrence est dépassée.
421
ENSAM 2020-2021- T.HAJJI- 421
JDBC 2.0
void afterLast()
déplace le curseur après la dernière ligne
void beforeFirst()
déplace le curseur avant la première ligne
int getRow()
renvoie le numéro de la ligne courante
422
ENSAM 2020-2021- T.HAJJI- 422
JDBC 2.0
Méthode Rôle
updateXXX(String, XXX) permet de mettre à jour la colonne dont le
nom est fourni en paramètre. Le type Java
de cette colonne est XXX
updateXXX(int, XXX) permet de mettre à jour la colonne dont
l'index est fourni en paramètre. Le type
Java de cette colonne est XXX
updateRow() permet d'actualiser les modifications
réalisées avec des appels à updateXXX()
boolean rowsUpdated() indique si la ligne courante a été modifiée
deleteRow() supprime la ligne courante
rowDeleted() indique si la ligne courante est supprimée
moveToInsertRow() permet de créer une nouvelle ligne dans
l'ensemble de résultat 423
inserRow()
ENSAM 2020-2021- T.HAJJI- 423
permet de valider la création de la ligne
Accès aux méta-données
La méthode getMetaData () permet d’obtenir
des informations sur les types de données du
ResultSet
elle renvoie des ResultSetMetaData
on peut connaître entre autres :
le nombre de colonne : getColumnCount()
le nom d’une colonne : getColumnName(int
col)
le nom de la table : getTableName(int col)
si un NULL SQL peut être stocké dans une colonne :
isNullable() 424
ENSAM 2020-2021- T.HAJJI- 424
ResultSetMetaData
ResultSet rs = stmt.executeQuery("SELECT * FROM emp");
ResultSetMetaData rsmd = rs.getMetatData();
int nbColonnes = rsmd.getColumnCount();
for(int i = 1; i <= nbColonnes; i++) {
// colonnes numerotées à partir de 1 (et non 0)
String nomCol = rsmd.getColumnName(i);
425
ENSAM 2020-2021- T.HAJJI- 425
DatabaseMetaData
• Pour récupérer des informations sur la base de
données elle-même, utiliser la méthode
getMetaData() de l’objet Connection
– dépend du SGBD avec lequel on travaille
– elle renvoie des DatabaseMetaData
– on peut connaître entre autres :
• getDriverVersion(), getDriverName(),
• getDatabaseProductName(), getDatabaseProductVersion()
• les tables de la base : getTables()
• le nom de l’utilisateur : getUserName()
426
ENSAM 2020-2021- T.HAJJI- 426
Le composant RowSet
A démarré avec JDBC 2
Interfacé vers un containeur pour tabuler les données
Souvent associé avec un ResultSet
Peut être n'importe quelle donnée, tableau, fichier à
plat, etc.
Étend l'interface de ResultSet
Contient un ensemble complet de propriétés
Supporte les interactions basées sur les évènements
427
ENSAM 2020-2021- T.HAJJI- 427
Le composant RowSet (2)
Il a la possibilité de se remplir lui-même avec des
données
Définition de l'emplacement de la base de données dans
une propriété principale
Peut aussi être rempli à partir d'un ResultSet
existant
428
ENSAM 2020-2021- T.HAJJI- 428
RowSetReader et RowSetWriter
Chaque classe qui implémente une RowSet
dispose d'une lecture et d'une écriture associées :
La lecture extrait les données à partir de la source de
données pour remplir le RowSet : appliquer l'interface
RowsetReader
L'écriture extrait les données du RowSet et les envoie
dans la source de données : appliquer RowSetWriter
429
ENSAM 2020-2021- T.HAJJI- 429
Types de RowSet
Les types définis par les fournisseurs :
• CachedRowSet :
– Déconnecte le RowSet qui stocke ces données
dans la mémoire
– Ne convient pas à des données volumineuses
– Idéal pour les client java légers ou les PDA
• JDBCRowSet :
– Sert de fin emballage autour du ResultSet
– Utilise un driver JDBC
• WebRowSet : 430
– Connecte le RowSet qui utilise le protocole HTTP
ENSAM 2020-2021- T.HAJJI- 430
Connexion/déconnexion du RowSet
Les RowSet peuvent être utilisés de la même
manière que les ResultSet standards
Peuvent être reliés à une source de données en
permanence
Peuvent aussi être déconnectés de la source de
données
Se comportent comme source de données pour le
client (données cachées)
Peuvent être séralisés et transmis à travers le réseau 431
Utile pour les clients légers (PDAs, les périphériques non connectés)
ENSAM 2020-2021- T.HAJJI- 431
Programmation orientée objet
Les threads
ENSAM 2020-2021- T.HAJJI- 432
Muti-tâches
• Multi-tâches : exécution de plusieurs processus
simultanément.
– Un processus est un programme en cours d'exécution.
– Le système d’exploitation distribue le temps CPU entre les
processus
• Un processus peut être dans différents états.
– En exécution (running) : il utilise le processeur
– Prêt : le processus est prêt à s'exécuter, mais n'a pas le processeur
(occupé par un autre processus en exécution)
– Bloqué
ENSAM 2020-2021- T.HAJJI- 433
433
Parallélisme
• Parallélisme : pouvoir faire exécuter plusieurs tâches à
un ordinateur avec plusieurs processeurs.
• Si l’ordinateur possède moins de processeurs que de
processus à exécuter :
– division du temps d’utilisation du processeur en tranches de
temps (time slice en anglais)
– attribution des tranches de temps à chacune des tâches de
façon telle qu’on ait l’impression que les tâches se d&roulent
en parallèle.
– on parle de pseudo-parallélisme
• Les systèmes d’exploitation modernes gèrent le muti-
tâches et le parallélisme
ENSAM 2020-2021- T.HAJJI- 434
434
Qu’est-ce qu’un Thread ?
• les threads sont différents des processus :
– ils partagent code, données et ressources : « processus légers »
– mais peuvent disposer de leurs propres données.
– ils peuvent s’exécuter en "parallèle"
• Avantages :
– légèreté grâce au partage des données
– meilleures performances au lancement et en exécution
– partage les ressources système (pratique pour les I/O)
• Utilité :
– puissance de la modélisation : un monde multithread
– puissance d’exécution : paralèllisme
– simplicité d’utilisation : c’est un objet Java (java.lang)
ENSAM 2020-2021- T.HAJJI- 435
435
Création
• La classe java.lang.Thread permet de créer de nouveaux
threads
• Un thread doit implémenter obligatoirement l’interface
Runnable
– le code exécuté se situe dans sa méthode run()
• 2 méthodes pour créer un Thread :
– 1) une classe qui dérive de java.lang.Thread
• java.lang.Thread implémente Runnable
• il faut redéfinir la méthode run()
– 2) une classe qui implémente l’interface Runnable
• il faut implémenter la méthode run()
ENSAM 2020-2021- T.HAJJI- 436
436
Methode 1 : Sous-classer Thread
class Proc1 extends Thread {
Proc1() {...} // Le constructeur
...
public void run() {
... // Ici ce que fait le processus : boucle infinie
}
}
...
Proc1 p1 = new Proc1(); // Création du processus p1
p1.start(); // Demarre le processus et execute p1.run()
ENSAM 2020-2021- T.HAJJI- 437
437
Méthode 2 :
une classe qui implémente Runnable
class Proc2 implements Runnable {
Proc2() { ...} // Constructeur
...
public void run() {
... // Ici ce que fait le processus
}
}
...
Proc2 p = new Proc2();
Thread p2 = new Thread(p);
...
p2.start(); // Démarre un processus qui execute p.run()
ENSAM 2020-2021- T.HAJJI- 438
438
Quelle solution choisir ?
• Méthode 1 : sous-classer Thread
– lorsqu’on désire paralléliser une classe qui n’hérite pas déjà d’une autre
classe (attention : héritage simple)
– cas des applications autonomes
• Méthode 2 : implémenter Runnable
– lorsqu’une super-classe est imposée
– cas des applets
public class MyThreadApplet
extends Applet implements Runnable {}
• Distinguer la méthode run (qui est le code exécuté par l’activité) et la méthode
start (méthode de la classe Thread qui rend l’activité exécutable) ;
• Dans la première méthode de création, attention à définir la méthode run avec
strictement le prototype indiqué (il faut redéfinir Thread.run et non pas la
surcharger).
ENSAM 2020-2021- T.HAJJI- 439
439
Le cycle de vie
ENSAM 2020-2021- T.HAJJI- 440
440
Les états d’un thread
• Créé :
- comme n’importe quel objet Java
- ... mais n’est pas encore actif
- Actif :
- après la création, il est activé par start() qui lance run().
- il est alors ajouté dans la liste des threads actifs pour être exécuté
par l’OS en temps partagé
- peut revenir dans cet état après un resume() ou un notify()
ENSAM 2020-2021- T.HAJJI- 441
441
Exemple
class ThreadCompteur extends Thread {
int no_fin;
ThreadCompteur (int fin) {no_fin = fin;} // Constructeur
// On redéfinit la méthode run()
public void run () {
for (int i=1; i<=no_fin ; i++) {
System.out.println(this.getName()+":"+i);} }
public static void main (String args[]) {
// On instancie les threads
ThreadCompteur cp1 = new ThreadCompteur (100);
ThreadCompteur cp2 = new ThreadCompteur (50);
cp1.start();
cp2.start();
} }
ENSAM 2020-2021- T.HAJJI- 442
442
Les états d’un Thread (suite)
• Endormi ou bloqué :
– après sleep() : endormi pendant un intervalle de temps (ms)
– suspend() endort le Thread mais resume() le réactive
– une entrée/sortie bloquante (ouverture de fichier, entrée clavier)
endort et réveille un Thread
• Mort :
– si stop() est appelé explicitement
– quand run() a terminé son exécution
ENSAM 2020-2021- T.HAJJI- 443
443
Exemple d’utilisation de sleep
class ThreadCompteur extends Thread {
int no_fin; int attente;
ThreadCompteur (int fin,int att) {
no_fin = fin; attente=att;}
// On redéfinit la méthode run()
public void run () {
for (int i=1; i<=no_fin ; i++) {
System.out.println(this.getName()+":"+i);
try {sleep(attente);}
catch(InterruptedException e) {};}
}
public static void main (String args[]) {
// On instancie les threads
ThreadCompteur cp1 = new ThreadCompteur (100,100);
ThreadCompteur cp2 = new ThreadCompteur (50,200);
cp1.start();
cp2.start();
} }
ENSAM 2020-2021- T.HAJJI- 444
444
Les priorités
• Principes :
– Java permet de modifier les priorités (niveaux absolus) desThreads par la
méthode setPriority()
– Par défaut, chaque nouveau Thread a la même priorité que le Thread qui l’a
crée
– Rappel : seuls les Threads actifs peuvent être exécutés et donc accéder au
CPU
– La JVM choisit d’exécuter le Thread actif qui a la plus haute priorité :
priority-based scheduling
– si plusieurs Threads ont la même priorité, la JVM répartit équitablement le
temps CPU (time slicing) entre tous : round-robin scheduling
ENSAM 2020-2021- T.HAJJI- 445
445
Les priorités (suite)
• Les méthodes :
– setPriority(int) : fixe la priorité du receveur.
• le paramètre doit appartenir à :
[MIN_PRIORITY, MAX_PRIORITY]
• sinon IllegalArgumentException est levée
– int getPriority() : pour connaître la priorité d’un Thread
NORM_PRIORITY : donne le niveau de priorité
"normal"
ENSAM 2020-2021- T.HAJJI- 446
446
La gestion du CPU
• Time-slicing (ou round-robin scheduling) :
– La JVM répartit de manière
équitable le CPU entre
tous les threads de même priorité.
Ils s’exécutent en "parallèle".
• Préemption (ou priority-based scheduling) :
– Le premier thread du groupe des threads à priorité égale monopolise le
CPU. Il peut le céder :
• involontairement : sur entrée/sortie
• volontairement : appel à la méthode statique yield()
Attention : ne permet pas à un thread de priorité inférieure de s’exécuter
(seulement de priorité égale)
• implicitement en passant à l’état endormi (wait(), sleep() ou suspend())
ENSAM 2020-2021- T.HAJJI- 447
447
La concurrence d’accès
• Le problème : espace de travail commun, pas de
"mémoire privée" pour chaque thread :
– inconvénient : accès simultané à une même ressource
Il faut garantir l’accès exclusif à un objet pendant l’exécution
d’une ou plusieurs instructions
• Pour se faire : le mot-clé synchronized permet de gérer les
concurrence d’accès :
– d’une méthode
– d’un objet
– ou d’une instruction (ou d’un bloc)
ENSAM 2020-2021- T.HAJJI- 448
448
La synchronisation
• Basée sur la technique de l’exclusion mutuelle :
– à chaque objet Java est associé un « verrou » géré par le thread
quand une méthode (ou un objet) synchronized est accédée.
– garantit l’accès exclusif à une ressource (la section critique)
pendant l’exécution d’une portion de code.
• Une section critique :
– une méthode : déclaration précédée de synchronized
– une instruction (ou un bloc) : précédée de synchronized
– un objet : le déclarer synchronized
• Attention à l’inter-blocage !!
(problème du dîner des philosophes)
ENSAM 2020-2021- T.HAJJI- 449
449
Utiliser synchronized
• Pour gérer la concurrence d’accès à une méthode :
– si un thread exécute cette méthode sur un objet, un autre thread ne peut pas l’exécuter
pour le même objet
– en revanche, il peut exécuter cette méthode pour un autre objet
public synchronized void maMethode() {...}
• Pour Contrôler l’accès à un objet :
public void maMethode() { ...
synchronized(objet) {
objet.methode();}}
– l’accès à l’objet passé en paramètre de synchronized(Object) est réservé à un unique
thread.
ENSAM 2020-2021- T.HAJJI- 450
450
Exemple de synchronisation
class Impression {
synchronized public void imprime(String t) {
for (int i=0; i<t.length(); i++) { System.out.print(t.charAt(i));
} } }
class TPrint extends Thread {
static Impression mImp = new Impression();
String txt;
public TPrint(String t) {txt = t;}
public void run() {
for (int j=0; j<3; j++) {mImp.imprime(txt);}}
static public void main(String args[]) {
TPrint a = new TPrint("bonjour ");
TPrint b = new TPrint("au revoir ");
a.start();
b.start();
}}
ENSAM 2020-2021- T.HAJJI- 451
451
Daemons
• Un thread peut être déclarer comme daemon :
– comme le "garbage collector", l’"afficheur d’images",
...
– en général de faible priorité, il "tourne" dans une boucle
infinie
– arrêt implicite dès que le programme se termine
• Les méthodes :
– setDaemon() : déclare un thread daemon
– isDaemon() : ce thread est-il un daemon ?
ENSAM 2020-2021- T.HAJJI- 452
452
Les « ThreadGroup »
Pour contrôler plusieurs threads
• Plusieurs processus (Thread) peuvent s’éxécuter en même temps, il
serait utile de pouvoir les manipuler comme une seule entité
– pour les suspendre
– pour les arrêter, ...
Java offre cette possibilité via l’utilisation des groupes de threads :
java.lang.ThreadGroup
• on groupe un ensemble nommé de threads
• ils sont contrôlés comme une seule unité
ENSAM 2020-2021- T.HAJJI- 453
453
Les groupes de threads
• Une arborescence :
– la classe ThreadGroup permet de constituer une arborescence
de Threads et de ThreadGroups
– elle donne des méthodes classiques de manipulation récursives
d’un ensemble de threads : suspend(), stop(),
resume(), ...
– et des méthodes spécifiques : setMaxPriority(), ...
• Fonctionnement :
– la JVM crée au minimum un groupe de threads nommé main
– par défaut, un thread appartient au même groupe que celui qui l’a
crée (son père)
– getThreadGroup() : pour connaître son groupe
ENSAM 2020-2021- T.HAJJI- 454
454
Création d’un groupe de threads
• Pour créer un groupe de processus :
ThreadGroup groupe = new ThreadGroup("Mon groupe");
Thread p1 = new Thread(groupe, "P1");
Thread p2 = new Thread(groupe, "P2");
Thread p3 = new Thread(groupe, "P3");
• On peut créer des sous-groupes de threads pour la
création d’arbres sophistiqués de processus
– des ThreadGroup contiennent des ThreadGroup
– des threads peuvent être au même niveau que des
ThreadGroup
ENSAM 2020-2021- T.HAJJI- 455
455
Création de groupe de threads (suite)
ThreadGroup groupe1 = new ThreadGroup("GP1");
Thread p1 = new Thread(groupe1, "P1");
Thread p2 = new Thread(groupe1, "P2");
Thread p3 = new Thread(groupe1, "P3");
ThreadGroup groupe11 = new ThreadGroup(groupe1, "GP11");
Thread p4 = new Thread(groupe11, "P4");
Thread p5 = new Thread(groupe11, "P5");
ENSAM 2020-2021- T.HAJJI- 456
456
Contrôler les ThreadGroup
• Le contrôle des ThreadGroup passe par l’utilisation
des méthodes standards qui sont partagées avec
Thread :
resume(), suspend(), stop(), ...
– Par exemple : appliquer la méthode stop() à un
ThreadGroup revient à invoquer pour chaque Thread du
groupe cette même méthode
– ce sont des méthodes de manipulation récursive
ENSAM 2020-2021- T.HAJJI- 457
457
Avantages / Inconvénients des threads
• Programmer facilement des applications où des
traitements se résolvent de façon concurrente
(applications réseaux, par exemple)
• Améliorer les performances en optimisant
l'utilisation des ressources
• Code plus difficile à comprendre, peu réutilisable
et difficile à débuguer
ENSAM 2020-2021- T.HAJJI- 458
458
JAVA Sérialisation
HAJJI TARIK
Docteur en Informatique
ENSAM 2020-2021- T.HAJJI- 459
Sérialisation
La sérialisation est un procédé introduit dans le JDK
version 1.1 qui permet de rendre un objet persistant.
Cet objet est mis sous une forme sous laquelle il pourra
être reconstitué à l'identique.
Ainsi il pourra être stocké sur un disque dur ou transmis
au travers d'un réseau pour le créer dans une autre JVM.
C'est le procédé qui est utilisé par RMI.
La sérialisation est aussi utilisée par les beans pour
sauvegarder leurs états.
ENSAM 2020-2021- T.HAJJI- 460
Sérialisation
Au travers de ce mécanisme, Java fournit une façon
facile, transparente et standard de réaliser cette opération :
ceci permet de facilement mettre en place un mécanisme
de persistance.
Il est de ce fait inutile de créer un format particulier
pour sauvegarder et relire un objet.
Le format utilisé est indépendant du système
d'exploitation. Ainsi, un objet sérialisé sur un système
peut être réutilisé par un autre système pour récréer
l'objet.
ENSAM 2020-2021- T.HAJJI- 461
Sérialisation
L'ajout d'un attribut à l'objet est automatiquement pris
en compte lors de la sérialisation.
Attention toutefois, la désérialisation de l'objet doit se
faire avec la classe qui a été utilisée pour la
sérialisation.
La sérialisation peut s'appliquer facilement à tous les
objets.
ENSAM 2020-2021- T.HAJJI- 462
Les classes et les interfaces de la
sérialisation
La sérialisation utilise:
I. L'interface:
Serializable
II. Les classes:
ObjectOutputStream
ObjectInputStream
ENSAM 2020-2021- T.HAJJI- 463
L'interface Serializable
Cette interface ne définit aucune méthode mais permet
simplement de marquer une classe comme pouvant être
sérialisée.
Tout objet qui doit être sérialisé doit implémenter cette
interface ou une de ses classes mères doit l'implémenter.
Si l'on tente de sérialiser un objet qui n'implémente pas
l'interface Serializable, une exception
NotSerializableException est levée.
ENSAM 2020-2021- T.HAJJI- 464
public class Personne implements java.io.Serializable {
private String nom = "";
private String prenom = "";
private int taille = 0;
public Personne(String nom, String prenom, int taille) {
this.nom = nom;
this.taille = taille;
this.prenom = prenom;
}
public String getNom() { return nom;}
public void setNom(String nom) { this.nom = nom;}
public int getTaille() { return taille;}
public void setTaille(int taille) { this.taille = taille;}
public String getPrenom() { return prenom;}
public void setPrenom(String prenom) {this.prenom = prenom;}
}
ENSAM 2020-2021- T.HAJJI- 465
La classe ObjectOuputStream
Cette classe permet de sérialiser un objet.
import java.io.*;
public class SerializerPersonne {
public static void main(String argv[]) {
Personne personne = new Personne("Dupond","Jean",175);
try {
FileOutputStream fichier = new FileOutputStream("personne.ser");
ObjectOutputStream oos = new ObjectOutputStream(fichier);
oos.writeObject(personne);
oos.flush();
oos.close();
}catch (java.io.IOException e) {
e.printStackTrace();
}
}
}
ENSAM 2020-2021- T.HAJJI- 466
On définit un fichier avec la classe FileOutputStream.
On instancie un objet de classe ObjectOutputStream en
lui fournissant en paramètre le fichier : ainsi, le résultat de
la sérialisation sera envoyé dans le fichier.
On appelle la méthode writeObject() en lui passant en
paramètre l'objet à sérialiser.
On appelle la méthode flush() pour vider le tampon dans
le fichier et la méthode close() pour terminer l'opération.
Lors de ces opérations une exception de type
IOException peut être levée si un problème intervient avec
le fichier.
ENSAM 2020-2021- T.HAJJI- 467
Après l'exécution de cet exemple, un fichier nommé
« personne.ser » est créé.
On peut visualiser son contenu mais surtout pas le modifier car
sinon il serait corrompu.
En effet, les données contenues dans ce fichier ne sont pas toutes
au format caractères.
La classe ObjectOutputStream contient aussi plusieurs
méthodes qui permettent de sérialiser des types élémentaires et non
des objets : writeInt(), writeDouble(), writeFloat(), ...
Il est possible dans un même flux d'écrire plusieurs objets les uns
à la suite des autres.
Ainsi plusieurs objets peuvent être sauvegardés.
Dans ce cas, il faut faire attention de relire les objets dans leur
ordre d'écriture.
ENSAM 2020-2021- T.HAJJI- 468
La classe ObjectInputStream
Cette classe permet de désérialiser un objet.
import java.io.*;
public class DeSerializerPersonne {
public static void main(String argv[]) {
try {
FileInputStream fichier = new FileInputStream("personne.ser");
ObjectInputStream ois = new ObjectInputStream(fichier);
Personne personne = (Personne) ois.readObject();
System.out.println("Personne : ");
System.out.println("nom : "+personne.getNom());
System.out.println("prenom : "+personne.getPrenom());
System.out.println("taille : "+personne.getTaille());
}catch (java.io.IOException e) {
e.printStackTrace();
C:\dej>java DeSerializerPersonne
}catch (ClassNotFoundException e) {
Personne :
e.printStackTrace();
} nom : Dupond
} prenom : Jean
} taille : 175
ENSAM 2020-2021- T.HAJJI- 469
On crée un objet de la classe FileInputStream qui
représente le fichier contenant l'objet sérialisé puis un objet
de type ObjectInputStream en lui passant le fichier en
paramètre.
Un appel à la méthode readObject() retourne l'objet avec
un type Object.
Un cast est nécessaire pour obtenir le type de l'objet.
La méthode close() permet de terminer l'opération.
Si la classe a changé entre le moment où elle a été
sérialisée et le moment où elle est désérialisée, une
exception est levée.
ENSAM 2020-2021- T.HAJJI- 470
C:\temp>java DeSerializerPersonne
java.io.InvalidClassException: Personne; Local class not compatible: stream
class
desc serialVersionUID=-2739669178469387642 local class
serialVersionUID=39870587
36962107851
atjava.io.ObjectStreamClass.validateLocalClass(ObjectStreamClass.java:4
38)
at java.io.ObjectStreamClass.setClass(ObjectStreamClass.java:482)
at java.io.ObjectInputStream.inputClassDescriptor(ObjectInputStream.java
:785)
ENSAM 2020-2021- T.HAJJI- 471
Une exception de type StreamCorruptedException peut
être levée si le fichier a été corrompu par exemple en le
modifiant avec un éditeur.
Exemple : les 2 premiers octets du fichier personne.ser
ont été modifiés avec un éditeur.
C:\temp>java DeSerializerPersonne
java.io.StreamCorruptedException: InputStream does not contain a serialized object at
java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:731)
At java.io.ObjectInpDutStream.<init>(ObjectInputStream.java:165) at
eSerializerPersonne.main(DeSerializerPersonne.java:8)
ENSAM 2020-2021- T.HAJJI- 472
Une exception de type ClassNotFoundException peut
être levée si l'objet est transtypé vers une classe qui n'existe
plus ou pas au moment de l'exécution.
Exemple ( code Java 1.1 ) :
C:\temp>rename Personne.class Personne2.class
C:\temp>java DeSerializerPersonne
java.lang.ClassNotFoundException: Personne
at java.io.ObjectInputStream.inputObject(ObjectInputStream.java:981)
at java.io.ObjectInputStream.readObject(ObjectInputStream
ENSAM 2020-2021- T.HAJJI- 473
La classe ObjectInputStream possède de la
même façon que la classe
ObjectOutputStream des méthodes pour lire
des données de type primitives :
readInt(),
readDouble(),
readFloat(), ...
Lors de la désérialisation, le constructeur de
l'objet n'est jamais utilisé.
ENSAM 2020-2021- T.HAJJI- 474
Le mot clé transient
Le contenu des attributs est visible dans le flux dans lequel est sérialisé
l'objet. Il est ainsi possible pour toute personne ayant accès au flux de voir le
contenu de chaque attribut même si ceux-ci sont private, ce qui peut poser des
problèmes de sécurité surtout si les données sont sensibles.
Java introduit le mot clé transient qui précise que l'attribut qu'il qualifie ne
doit pas être inclus dans un processus de sérialisation et donc de
désérialisation.
Exemple ( code Java 1.1 ) :
...
private transient String codeSecret;
Lors de la désérialisation, les champs transient sont initialisés avec la valeur
null. L'objet recréé doit donc gérer cet état pour éviter d'avoir des exceptions
de type NullPointerException.
ENSAM 2020-2021- T.HAJJI- 475
La sérialisation personnalisée
Il est possible de personnaliser la sérialisation d'un objet.
Dans ce cas, la classe doit implémenter l'interface
Externalizable qui hérite de l'interface Serializable.
ENSAM 2020-2021- T.HAJJI- 476
L'interface Externalizable
Cette interface définit deux méthode :
readExternal()
writeExternal().
Par défaut, la sérialisation d'un objet qui implémente cette
interface ne prend en compte aucun attribut de l'objet.
Remarque : le mot clé transient est donc inutile avec une classe
qui implémente l'interface Externalisable
ENSAM 2020-2021- T.HAJJI- 477
TP-1
On vous demande dans ce TP de implémenter
un programme JAVA composé par une classe
Rectangle, qui est composée par 4 Points.
On vous demande d’exécuter ce programme
4 fois, telle que dans chaque exécution on
rajoute la définition d’un point du rectangle.
ENSAM 2020-2021- T.HAJJI- 478