Vous êtes sur la page 1sur 84

Introduction à la Programmation Orientée Objet

1re partie : Les Objets et les Classes


Outline

1 Introduction aux concepts d’objet et de classe


2 Créer des objets, le constructeur et l’opérateur new
3 Objets, références et mémoire
4 Le mot-clé this
5 Visibilité des membres et les getter/setter
6 La composition
7 Le modificateur static
8 Généralités et conventions
9 Les classes internes
Qu’es qu’un objet ?

• L’orienté objet (OO) est un paradigme de programmation

• Dans les langages OO comme Java, le "monde" est modélisé


en utilisant des objets

• Un objet représente une entité matérielle (une personne, une


voiture) ou virtuelle (une date, un compte bancaire)

• Un programme est donc un ensemble d’objets qui


interagissent entre eux

• La notion d’objet et fortement liée à celle d’encapsulation

3 / 81
Les objets en Java

En java les objets sont définis par leurs attributs et leurs méthodes

Attributs (ou champs, ou variables d’instance) : caractérisent


l’état de l’objet
• Ensemble de variables (avec leur type)

Méthodes : caractérisent le comportement de l’objet, les actions


que l’on peut entreprendre sur l’objet
• Ensemble de procédures et fonctions agissant sur les
attributs

Le mot membre qualifie les attributs et les méthodes

4 / 81
Les classes

• Les classes sont des modèles (des moules) pour créer des
objets

• Une classe déclare les caractéristiques communes à un


ensemble d’objets de même nature, c’est une descriptions
générique

• Ainsi un objet est une instance d’une classe, c’est à dire un


exemplaire d’une classe

• En Java une classe est un type, un objet est donc une variable
qui a comme type une classe

5 / 81
Exemple avec des rectangles - Objets vs Classe

3 rectangles spécifiques
⇒ 3 objects ou instances

• Caractéristiques que tous les rectangles possèdent


• la hauteur, la largeur, la couleur, ...

• Actions que l’on peut faire sur n’importe quel rectangle


• calculer la surface, le périmètre, ...

6 / 81
Exemple avec des rectangles - Objets vs Classe

3 rectangles spécifiques
⇒ 3 objects ou instances

• Caractéristiques que tous les rectangles possèdent


• la hauteur, la largeur, la couleur, ...

• Actions que l’on peut faire sur n’importe quel rectangle


• calculer la surface, le périmètre, ...

7 / 81
Exemple avec des rectangles - Objets vs Classe

3 rectangles spécifiques
⇒ 3 objects ou instances

• Caractéristiques que tous les rectangles possèdent


• la hauteur, la largeur, la couleur, ...

• Actions que l’on peut faire sur n’importe quel rectangle


• calculer la surface, le périmètre, ...

8 / 81
Exemple avec des rectangles - Objets vs Classe

3 rectangles spécifiques
⇒ 3 objects ou instances

Existence concrète (à l’exécution)


Existence conceptuelle

Représentation générique
Attribut d’un rectangle par une
classe
Méthode

9 / 81
L’encapsulation I

L’exemple précédent illustre 2 facettes de l’encapsulation

Regroupement des données et des actions les manipulant au sein


d’un même objet
• La décomposition du problème en modules facilite la
conception du problème (découpage en plus petit morceaux)
• La modularisation permet de mieux structurer les programmes
• Meilleure lisibilité
• Meilleure réutilisabilité
• Le découplage facilite l’entretien, la correction et l’amélioration
Abstraction du "comment" pour se concentrer sur le "quoi" (les
fonctionnalités)
• Abstraire les détails d’implémentation facilite la conception du
problème
10 / 81
Le constructeur

• Méthode spéciale appelée lors de la création de l’objet en


mémoire

• Toujours appelé en premier, avant n’importe quelle autre


méthode d’instance

• Permet d’initialiser l’objet (les attributs)

• Nom du constructeur = nom de la classe

• Pas de valeur de retour

11 / 81
Exemple avec des rectangles - Le constructeur

1 public class Rectangle {


2 /* Attributs */
3 private double hauteur;
4 private double largeur;
5
6 /* Constructeur */
7 public Rectangle ( double h , double l ) {
8 hauteur = h ;
9 largeur = l ;
10 }
11
12 /* Methodes */
13 public double calculerSurface () {
14 return hauteur*largeur;
15 }
16 }

12 / 81
Plus sur les constructeurs

Le constructeur par défaut :


• Défini tacitement par le compilateur si la classe n’en possède
pas
• Constructeur sans argument (pas de donnée à initialiser)

Plusieurs constructeur :
• Il est possible d’avoir plusieurs constructeurs par classe
• Les paramètres du constructeur doivent être différents
(sucharge de méthode)

13 / 81
L’opérateur new

Permet d’instancier une classe, c’est-à-dire de créer un objet


• Alloue la mémoire nécessaire à l’objet
• Appelle le constructeur associé
• Retourne une référence sur l’objet créé

Exemple :
// Cree un rectangle unitaire
Rectangle rectangle = new Rectangle (1 ,1) ;

La variable rectangle est une référence, c’est à dire une variable


contenant l’adresse mémoire de l’objet rectangle fraichement créé !

14 / 81
Les références java

Rectangle rectangle = new Rectangle (12 ,4) ;

Un objet Rectangle
rectangle
hauteur 12
largeur 4

• Les objets sont toujours manipulés via des références

• Une variable est soit un type primitif soit une référence

• En java les paramètres sont toujours passé par valeur (copié)

15 / 81
Les valeurs par défaut

Les attributs qui ne sont pas explicitement initialisés reçoivent


automatiquement une valeur par défaut en fonction de leur type

• Numérique (int, double, ...) : 0, 0.0, ...

• boolean : false

• Objet : null

Attention, les variables locales (déclarées dans une


méthode) ne sont pas automatiquement initialisées

16 / 81
L’affichage d’un objet : toString()

• Par défaut la méthode System.out.println() affiche la


valeur de la référence (une sorte d’adresse, par exemple :
Rectangle@1242719c)

• Pour afficher le contenu de l’objet il faut implémenter


(redéfinir) la méthode : String toString()

• toString() donne la représentation textuelle de l’objet

• toString() est automatiquement invoquée par


System.out.println()

La classe String redéfini toString() pour afficher la


valeur de l’objet et non une adresse !

17 / 81
toString() - Exemple

1 public class Rectangle {


2 private double hauteur;
3 private double largeur;
4
5 ... // Autres constructeurs et methodes
6
7 public String toString() {
8 return "(h=" + hauteur + ";l=" + largeur + ")";
9 }
10 }

1 public class ProgPrincipal {


2 public static void main(String[] args) {
3 Rectangle rectangle = new Rectangle(2,3);
4
5 System.out.println(rectangle); // Affiche : (h=2;l=3)
6 }
7 }

18 / 81
toString() - Exemple

1 public class Rectangle {


2 private double hauteur;
3 private double largeur;
4
5 ... // Autres constructeurs et methodes
6
7 public String toString() {
8 return "(h=" + hauteur + ";l=" + largeur + ")";
9 }
10 }

1 public class ProgPrincipal {


2 public static void main(String[] args) {
3 Rectangle rectangle = new Rectangle(2,3);
4
5 System.out.println(rectangle); // Affiche : (h=2;l=3)
6 }
7 }

19 / 81
La comparaison d’objets : equals()

L’opérateur == appliqué à 2 objets compare leurs références !

1 public class ProgPrincipal {


2 public static void main(String[] args) {
3 Rectangle r1 = new Rectangle(2,3);
4 Rectangle r2 = new Rectangle(2,3);
5
6 // Affiche : ?
7 System.out.println("Equals ? " + (r1 == r2));
8 }
9 }

• Pour tester l’égalité sémantique des objets il faut implémenter


(redéfinir) la méthode : boolean equals(Object o)

• equals() indique si l’objet passé en paramètre est égal à


l’objet courant
20 / 81
La comparaison d’objets : equals()

L’opérateur == appliqué à 2 objets compare leurs références !

1 public class ProgPrincipal {


2 public static void main(String[] args) {
3 Rectangle r1 = new Rectangle(2,3);
4 Rectangle r2 = new Rectangle(2,3);
5
6 // Affiche : Equals ? false
7 System.out.println("Equals ? " + (r1 == r2));
8 }
9 }

• Pour tester l’égalité sémantique des objets il faut implémenter


(redéfinir) la méthode : boolean equals(Object o)

• equals() indique si l’objet passé en paramètre est égal à


l’objet courant
21 / 81
equals() - Exemple

1 public class Rectangle {


2 private double hauteur;
3 private double largeur;
4
5 ... // Autres constructeurs
6 // et methodes
7 1 public class ProgPrincipal {
8 public boolean equals(Object o) { 2 public static void main(String[] args) {
9 if (o == null) { 3 Rectangle r1 = new Rectangle(2,3);
10 return false; 4 Rectangle r2 = new Rectangle(2,3);
11 } 5
12 6 // Affiche : ?
13 if (this == o) { 7 System.out.println(
14 return true; 8 "Equals ? " + r1.equals(r2));
15 } 9 }
16 10 }
17 // Casting
18 Rectangle r = (Rectangle)o;
19 return hauteur == r.hauteur &&
20 largeur == r.largeur;
21 }
22 }
22 / 81
equals() - Exemple

1 public class Rectangle {


2 private double hauteur;
3 private double largeur;
4
5 ... // Autres constructeurs
6 // et methodes
7 1 public class ProgPrincipal {
8 public boolean equals(Object o) { 2 public static void main(String[] args) {
9 if (o == null) { 3 Rectangle r1 = new Rectangle(2,3);
10 return false; 4 Rectangle r2 = new Rectangle(2,3);
11 } 5
12 6 // Affiche : ?
13 if (this == o) { 7 System.out.println(
14 return true; 8 "Equals ? " + r1.equals(r2));
15 } 9 }
16 10 }
17 // Casting
18 Rectangle r = (Rectangle)o;
19 return hauteur == r.hauteur &&
20 largeur == r.largeur;
21 }
22 }
23 / 81
equals() - Exemple

1 public class Rectangle {


2 private double hauteur;
3 private double largeur;
4
5 ... // Autres constructeurs
6 // et methodes
7 1 public class ProgPrincipal {
8 public boolean equals(Object o) { 2 public static void main(String[] args) {
9 if (o == null) { 3 Rectangle r1 = new Rectangle(2,3);
10 return false; 4 Rectangle r2 = new Rectangle(2,3);
11 } 5
12 6 // Affiche : Equals ? true
13 if (this == o) { 7 System.out.println(
14 return true; 8 "Equals ? " + r1.equals(r2));
15 } 9 }
16 10 }
17 // Casting
18 Rectangle r = (Rectangle)o;
19 return hauteur == r.hauteur &&
20 largeur == r.largeur;
21 }
22 }
24 / 81
L’affectation de type primitifs

1 int i = 1;
2 int j = 2;
3 System.out.println("i = " + i + " ; j = " + j); Console :
4
i = 1 ; j = 2
5 j = i; i = 1 ; j = 1
6 System.out.println("i = " + i + " ; j = " + j); i = 3 ; j = 1
7
8 i = 3;
9 System.out.println("i = " + i + " ; j = " + j);

Memory :

i 1 1 1
j=i i=3
copie de valeur
j 2 1 3
25 / 81
L’affectation d’objets

1 Rectangle r1 = new Rectangle(1,1);


2 Rectangle r2 = new Rectangle(2,2);
3 System.out.println("h1 = " + r1.getHauteur() +
4 "; h2 = " + r2.getHauteur());
5
6 r2 = r1;
7 System.out.println("h1 = " + r1.getHauteur() +
8 "; h2 = " + r2.getHauteur());
9
10 r1.setHauteur(3);
11 System.out.println("h1 = " + r1.getHauteur() +
12 "; h2 = " + r2.getHauteur());

Laquelle de ces 2 sorties est correcte ?


h1 = 1.0 ; h2 = 2.0 h1 = 1.0 ; h2 = 2.0
h1 = 1.0 ; h2 = 1.0 h1 = 1.0 ; h2 = 1.0
h1 = 3.0 ; h2 = 1.0 h1 = 3.0 ; h2 = 3.0
26 / 81
L’affectation d’objets

1 Rectangle r1 = new Rectangle(1,1);


2 Rectangle r2 = new Rectangle(2,2);
3 System.out.println("h1 = " + r1.getHauteur() +
4 "; h2 = " + r2.getHauteur());
5
6 r2 = r1;
7 System.out.println("h1 = " + r1.getHauteur() +
8 "; h2 = " + r2.getHauteur());
9
10 r1.setHauteur(3);
11 System.out.println("h1 = " + r1.getHauteur() +
12 "; h2 = " + r2.getHauteur());

Laquelle de ces 2 sorties est correcte ?


h1 = 1.0 ; h2 = 2.0 h1 = 1.0 ; h2 = 2.0
h1 = 1.0 ; h2 = 1.0 h1 = 1.0 ; h2 = 1.0
h1 = 3.0 ; h2 = 1.0 h1 = 3.0 ; h2 = 3.0
27 / 81
L’affectation d’objets

r1 copie de référence
hauteur 1
largeur 1
r2 = r1

r2 1
hauteur 2 r1
hauteur 1
largeur 2
largeur 1

r2
hauteur 2
r1 largeur 2
2
hauteur 3 r1.setHauteur(3)

r2 largeur 1

28 / 81
Références et passage par valeur

1 public class ReferencePiege {


2 public static void modifier ( Rectangle r2 ) {
3 r2 = new Rectangle (2 ,2) ;
4 }
5
6 public static void main ( String [] args ) {
7 Rectangle r1 = new Rectangle (1 ,1) ;
8 modifier ( r1 ) ;
9
10 System . out . println ( " r1 = " + r1 ) ;
11 }
12 }

Que s’affiche t-il dans la console ?


B r1 = (h=1.0;l=1.0)
B r1 = (h=2.0;l=2.0)
29 / 81
Références et passage par valeur

1 public class ReferencePiege {


2 public static void modifier ( Rectangle r2 ) {
3 r2 = new Rectangle (2 ,2) ;
4 }
5
6 public static void main ( String [] args ) {
7 Rectangle r1 = new Rectangle (1 ,1) ;
8 modifier ( r1 ) ;
9
10 System . out . println ( " r1 = " + r1 ) ;
11 }
12 }

Que s’affiche t-il dans la console ?


B r1 = (h=1.0;l=1.0) X
B r1 = (h=2.0;l=2.0)
30 / 81
Références et passage par valeur

Dans la méthode modifier()


r1
r1 hauteur 1
largeur 1
r2 = new Rectangle(2,2)
hauteur 1

r2 largeur 1 r2
hauteur 2
largeur 2

Après la méthode modifier()

r1
hauteur 1
largeur 1

31 / 81
La copie d’objets

• L’opérateur = ne peut être utilisé pour copier des objets

• Pour copier ou cloner un objet on doit utiliser la méthode :


Object clone()

• Cependant, vous n’avez pas encore le bagage technique pour


comprendre son fonctionnement. Si vous avez besoin de cloner
un objet souvenez vous que :
1 Il faut indiquer au compilateur qu’un objet peut être cloné.
Pour cela il faut implémenter l’interface Cloneable

2 Tout objet possède la méthode clone() (elle est héritée de


Object)

3 La méthode clone() lance une exception. Il faut soit


l’attraper ou la la propager
32 / 81
clone() - Exemple

1 public class Rectangle implements Cloneable {


2 private double hauteur;
3 private double largeur;
4
5 ... // Autres constructeurs et methodes
6 }

1 public class ProgPrincipal {


2 public static void main(String[] args) throws
CloneNotSupportedException {
3 Rectangle r1 = new Rectangle(2,3);
4 Rectangle r2 = (Rectangle)r1.clone();
5
6 // Affiche : ?
7 System.out.println("Equals ? " + (r1 == r2));
8 // Affiche : ?
9 System.out.println("Equals ? " + r1.equals(r2));
10 }
11 }
33 / 81
clone() - Exemple

1 public class Rectangle implements Cloneable { 1


2 private double hauteur;
3 private double largeur;
4
5 ... // Autres constructeurs et methodes
6 }

1 public class ProgPrincipal {


2 public static void main(String[] args) throws
CloneNotSupportedException { 3
3 Rectangle r1 = new Rectangle(2,3);
4 Rectangle r2 = (Rectangle)r1.clone(); 2
5
6 // Affiche : ?
7 System.out.println("Equals ? " + (r1 == r2));
8 // Affiche : ?
9 System.out.println("Equals ? " + r1.equals(r2));
10 }
11 }
34 / 81
clone() - Exemple

1 public class Rectangle implements Cloneable {


2 private double hauteur;
3 private double largeur;
4
5 ... // Autres constructeurs et methodes
6 }

1 public class ProgPrincipal {


2 public static void main(String[] args) throws
CloneNotSupportedException {
3 Rectangle r1 = new Rectangle(2,3);
4 Rectangle r2 = (Rectangle)r1.clone();
5
6 // Affiche : Equals ? false
7 System.out.println("Equals ? " + (r1 == r2));
8 // Affiche : Equals ? true
9 System.out.println("Equals ? " + r1.equals(r2));
10 }
11 }
35 / 81
clone() - Exemple

r1 != r2 mais r1.equals(r2) est true

r1
hauteur 2
largeur 3
r1
hauteur 2 r2 = r1.clone()

largeur 3 r2
hauteur 2
largeur 3

36 / 81
Le ramasse-miette ou garbage collector

• En java il n’est pas nécessaire de gérer soit même la


mémoire, c’est à dire de récupérer la mémoire allouée

• C’est le ramasse-miette qui s’en occupe, il libère la mémoire


des objets qui ne sont plus utilisés

• Un objet inutilisé est un objet qui n’est plus référencé par


aucune variable

• Le ramasse-miette se lance automatiquement, lorsque c’est


nécessaire

37 / 81
Le mot-clé this

• Le mot-clé this se réfère à l’objet courant (soi-même)

• Il est utile lors qu’un autre identificateur masque un attribut

• Les méthodes d’une classe ont un accès direct aux attributs et


aux autres méthodes de la classe, l’utilisation de this est
donc optionnelle

38 / 81
Le mot-clé this - Exemple

1 public class Rectangle {


2 private double hauteur;
3 private double largeur;
4
5 public Rectangle ( double hauteur , double largeur ) {
6 this .hauteur = hauteur ;
7 this .largeur = largeur ;
8 }
9
10 public double calculerSurface () {
11 return this .hauteur * this .largeur;
12 }
13
14 public void afficherSurface () {
15 System . out . println ( " Surface : " +
this . calculerSurface () ) ;
16 }
17 }

39 / 81
Le mot-clé this - Exemple

1 public class Rectangle {


2 private double hauteur;
3 private double largeur;
4
5 public Rectangle ( double hauteur , double largeur ) {
6 this .hauteur = hauteur ;
7 this .largeur = largeur ; Obligatoire
8 }
9
10 public double calculerSurface () {
11 return this .hauteur * this .largeur;
12 } Optionnel
13
14 public void afficherSurface () {
15 System . out . println ( " Surface : " +
this . calculerSurface () ) ;
16 }
17 }

40 / 81
La visibilité des membres

Il est possible de qualifier dans quel contexte les membres d’une


classe (attributs et méthodes) sont accessibles

Il existe 4 niveaux de visibilité


• public : visible de tous
• protected : visible des classes du même package
• pas spécifié : visible des classes du même package et des
sous-classes
• private : visible seulement des instances de la classe

Attention, une classe a accès à tous ces membres, même


ceux qui sont spécifiés private

41 / 81
La visibilité des membres

Il est possible de qualifier dans quel contexte les membres d’une


classe (attributs et méthodes) sont accessibles

Il existe 4 niveaux de visibilité


• public : visible de tous
• protected : visible des classes du même package
• pas spécifié : visible des classes du même package et des
sous-classes
• private : visible seulement des instances de la classe

Attention, une classe a accès à tous ces membres, même


ceux qui sont spécifiés private

42 / 81
La visibilité des membres - Exemple

1 public class Rectangle {


2 private double hauteur;
3 private double largeur;
4
5 public Rectangle ( double hauteur , double largeur ) {
6 this .hauteur = hauteur ;
7 this .largeur = largeur ;
8 }
9
10 public double calculerSurface () {
11 return hauteur * largeur;
12 }
13 }

1 public class ProgPrincipal {


2 public static void main ( String [] args ) {
3 Rectangle rectangle = new Rectangle (2 ,3) ;
4
5 double hauteur = rectangle . hauteur ;
6
7 double airRectangle = rectangle . calculerSurface () ;
8 }
9 }
43 / 81
La visibilité des membres - Exemple

1 public class Rectangle {


2 private double hauteur;
3 private double largeur;
4
5 public Rectangle ( double hauteur , double largeur ) {
6 this .hauteur = hauteur ;
7 this .largeur = largeur ;
8 }
9
10 public double calculerSurface () {
11 return hauteur * largeur;
12 }
13 }

1 public class ProgPrincipal {


2 public static void main ( String [] args ) {
3 Rectangle rectangle = new Rectangle (2 ,3) ;
4
5 double hauteur = rectangle . hauteur ;
6
7 double airRectangle = rectangle . calculerSurface () ;
8 }
9 }
44 / 81
Les accesseurs ou getter/setter

Les accesseurs ou getter/setter sont des méthodes public qui


permettent d’accéder aux attributs private de l’objet

Convention de nommage :
• getXxx() pour consulter la valeur de l’attribut xxx ;
• setXxx() pour modifier la valeur de l’attribut xxx ;

Exemple (avec l’attribut hauteur de la classe Rectangle) :


1 public double getHauteur () {
2 return hauteur;
3 }
4
5 public void setHauteur ( double hauteur ) {
6 this .hauteur = hauteur ;
7 }

45 / 81
La visibilité des membres - Règles générales

• Mettre tous les attributs en private et définir des


getter/setter

• Choisir soigneusement quelles méthodes d’instance exposer à


l’extérieur (rendre public)

• Ne surtout pas tout mettre en public


⇒ principe d’encapsulation

46 / 81
L’encapsulation II

L’utilisation des modificateurs de visibilité permettent de satisfaire


2 autres principes de l’encapsulation

L’isolement et la dissimulation des détails internes


d’implémentation (boîte noire). Tout ce qui n’est pas nécessaire de
connaître depuis l’extérieur est caché (mis en private). Ainsi les
modifications de la structure interne sont invisible de l’extérieur, il
y a peu ou pas de répercution sur les classes externes

Le contrôle de l’accès aux données (attributs) via des


getter/setter permet de garantir leur intégrité, c’est à dire de
vérifier les éventuelles contraintes qui leur sont liées

47 / 81
La composition

• Manière d’exprimer une relation entre 2 classes

• Permet de modéliser des objets qui contiennent des objets

• Permet de réutiliser du code existant (par exemple créé par


quelqu’un d’autre)
• L’objet "contenu" est référencé par un attribut de l’objet
"conteneur"

Exemple : Un Rectangle a une origine qui est un Point


origine
Rectangle Point

48 / 81
Exemple de composition - Définition de la classe Point

1 public class Point { 16 /* Methodes d’instance */


2 /* Attributs */ 17 public double getX() {
3 private double x; 18 return x;
4 private double y; 19 }
5
20
6 /* Constructeurs */ 21 public void setX(double x) {
7 public Point(int x, int y) { 22 this.x = x;
8 this.x = x; 23 }
9 this.y = y; 24
10 } 25 public double getY() {
11
26 return y;
12 public Point() { 27 }
13 x = 0; 28
14 y = 0; 29 public void setY(double y) {
15 } 30 this.y = y;
31 }
32 }

49 / 81
Exemple de composition - Utilisation de Point
1 public class Rectangle {
2 /* Attributs */
3 private double hauteur;
4 private double largeur;
5 private Point origine;
6
7 /* Constructeurs */
8 public Rectangle(double hauteur, double largeur) {
9 // Appelle le constructeur de dessous
10 this(new Point(),hauteur,largeur);
11 }
12
13 public Rectangle(Point origine, double hauteur, double largeur) {
14 this.hauteur = hauteur;
15 this.largeur = largeur;
16 this.origine = origine;
17 }
18 ...

1 public static void main(String[] args) {


2 // Un rectangle avec une origine a (2,2)
3 Rectangle rectangle = new Rectangle(new Point(2,2),20,30);
4 System.out.println(rectangle);
5 }
50 / 81
Exemple de composition - Utilisation de Point
1 public class Rectangle {
2 /* Attributs */
3 private double hauteur;
4 private double largeur;
5 private Point origine;
6
7 /* Constructeurs */
8 public Rectangle(double hauteur, double largeur) {
9 // Appelle le constructeur de dessous
10 this(new Point(),hauteur,largeur);
11 }
12
13 public Rectangle(Point origine, double hauteur, double largeur) {
14 this.hauteur = hauteur;
15 this.largeur = largeur;
16 this.origine = origine;
17 }
18 ...

1 public static void main(String[] args) {


2 // Un rectangle avec une origine a (2,2)
3 Rectangle rectangle = new Rectangle(new Point(2,2),20,30);
4 System.out.println(rectangle);
5 }
51 / 81
toString() et equals() dans la composition

• La classe composante (ici Point) doit également redéfinir les


méthodes toString() et equals()

• La classe composée (ici Rectangle) peut alors les utiliser afin


d’implémenter les siennes

52 / 81
toString() et equals() de Point

1 public class Point {


2 private double x;
3 private double y;
4
5 ... // comme avant
6
7 public String toString() {
8 return "(x=" + x + ";y=" + y + ")";
9 }
10
11 public boolean equals(Object o) {
12 if (o == null) {
13 return false;
14 }
15
16 if (this == o) {
17 return true;
18 }
19
20 Point p = (Point)o;
21 return x == p.x && y == p.y;
22 }
23 }
53 / 81
toString() et equals() de Rectangle

1 public class Rectangle {


2 private double hauteur;
3 private double largeur;
4 private Point origine;
5
6 ... // comme avant
7
8 public String toString() {
9 return "(origine" + origine +
10 ";h=" + hauteur + ";l=" + largeur + ")";
11 }
12
13 public boolean equals(Object o) {
14 ... // comme avant
15
16 Rectangle r = (Rectangle)o; // Casting
17 return hauteur == r.hauteur && largeur == r.largeur &&
origine.equals(r.origine);
18 }
19 }
54 / 81
toString() et equals() de Rectangle

1 public class ProgPrincipal {


2 public static void main(String[] args) {
3 Rectangle r1 = new Rectangle(new Point(2,2),20,30);
4 Rectangle r2 = new Rectangle(new Point(2,2),20,30);
5
6 System.out.println("r1 = " + r1);
7 System.out.println("Equals ? " + (r1 == r2));
8 System.out.println("Equals ? " + r1.equals(r2));
9 }
10 }

Console :
r1 = (origine(x=2.0;y=2.0);h=20.0;l=30.0)
Equals ? false
Equals ? true

55 / 81
Copie de surface et copie en profondeur

• La méthode clone() par défaut fait une copie de surface

• Une copie de surface ne copie pas les objets référencés, mais


uniquement les références (voir affectation)

• Pour copier également le contenu de ces objets il faut faire


une une copie en profondeur

• La copie en profondeur est obtenue en redéfinissant la


méthode clone() de toutes les classes, à l’instar des
méthodes toString() et equals()

56 / 81
clone() de Point

• La classe Point ne contient pas de référence (elle est


immutable)

• La copie par défaut de surface est suffisante

1 public class Point implements Cloneable {


2 private double x;
3 private double y;
4
5 ... // comme avant
6
7 // Comportement par defaut de clone
8 // (vous allez comprendre plus tard)
9 public Point clone() throws CloneNotSupportedException {
10 return (Point)super.clone();
11 }
12 }

57 / 81
clone() de Rectangle

• La classe Rectangle contient des références (elle est


mutable)
• La copie par défaut de surface n’est pas suffisante
1 public class Rectangle implements Cloneable {
2 private double hauteur;
3 private double largeur;
4 private Point origine;
5
6 ... // comme avant
7
8 public Rectangle clone() throws CloneNotSupportedException {
9 // Copie de surface
10 Rectangle r = (Rectangle)super.clone();
11
12 // Copie les objets references
13 r.origine = origine.clone();
14 return r;
15 }
16 }
58 / 81
Copie en profondeur - Exemple

1 public class ProgPrincipal {


2 public static void main(String[] args) throws
CloneNotSupportedException {
3 Rectangle r1 = new Rectangle(new Point(2,2),20,30);
4 Rectangle r2 = r1.clone();
5
6 System.out.println("Equals ? " +
7 (r1.getOrigine() == r2.getOrigine()));
8 System.out.println("Equals ? " +
9 r1.getOrigine().equals(r2.getOrigine()));
10
11 System.out.println("Equals ? " + (r1 == r2));
12 System.out.println("Equals ? " + r1.equals(r2));
13 }
14 }

59 / 81
Copie en profondeur - Exemple

1 public class ProgPrincipal {


2 public static void main(String[] args) throws
CloneNotSupportedException {
3 Rectangle r1 = new Rectangle(new Point(2,2),20,30);
4 Rectangle r2 = r1.clone();
5
6 System.out.println("Equals ? " +
7 (r1.getOrigine() == r2.getOrigine()));
8 System.out.println("Equals ? " +
9 r1.getOrigine().equals(r2.getOrigine()));
10
11 System.out.println("Equals ? " + (r1 == r2));
12 System.out.println("Equals ? " + r1.equals(r2));
13 }
14 }

Equals ? false
Equals ? true
Console :
Equals ? false
Equals ? true
60 / 81
Le modificateur static

• Peut notamment qualifier des attributs ou des méthodes

• L’attribut ou la méthode static n’existe qu’en un seul


exemplaire, et non un exemplaire par objet. Il est partagé
par toutes les instances de la classe.

• Un attribut/méthode static est appelé un attribut/méthode


de classe.

• Un membre static peut être utilisé sans créer d’instance, on


y accède en le préfixant par le nom de la classe qui le
contient : Math.PI ou Integer.parseInt("25")

• Étant indépendant de toute instance, les méthodes static


n’ont pas accès aux membres non-statiques
61 / 81
Le modificateur static - Exemple

1 public class CompteurInstance { 1 public static void main(String[] args) {


2 private static int nbInstance; 2 System.out.println(new CompteurInstance());
3 private int id; 3 System.out.println(new CompteurInstance());
4 4 System.out.println(new CompteurInstance());
5 public CompteurInstance() { 5
6 id = nbInstance++; 6 System.out.println("Prochain numero : " +
7 } CompteurInstance.getNbInstance ());
8 7 }
9 public int getId() {
10 return id; Objet numero : 0
11 } Objet numero : 1
12
Objet numero : 2
13 public static int getNbInstance() { Prochain numero : 3
14 return nbInstance;
15 }
16
17 public String toString() {
18 return "Objet numero : " + id;
19 }
20 }

62 / 81
Le modificateur static - Exemple

1 public class CompteurInstance { 1 public static void main(String[] args) {


2 private static int nbInstance; 2 System.out.println(new CompteurInstance());
3 private int id; 3 System.out.println(new CompteurInstance());
4 4 System.out.println(new CompteurInstance());
5 public CompteurInstance() { 5
6 id = nbInstance++; 6 System.out.println("Prochain numero : " +
7 } CompteurInstance.getNbInstance ());
8 7 }
9 public int getId() {
10 return id; Objet numero : 0
11 } Objet numero : 1
12
Objet numero : 2
13 public static int getNbInstance() { Prochain numero : 3
14 return nbInstance;
15 }
16
17 public String toString() {
18 return "Objet numero : " + id;
19 }
20 }

63 / 81
Le modificateur static - Exemple

Classe

nbInstance
0

64 / 81
Le modificateur static - Exemple

Classe
Instances

nbInstance
new CompteurInstance() id 0
1

65 / 81
Le modificateur static - Exemple

Instances
Classe
new CompteurInstance() id 0
nbInstance
2
new CompteurInstance() id 1

66 / 81
Le modificateur static - Exemple

Instances

new CompteurInstance() id 0
Classe

nbInstance
new CompteurInstance() id 1
3

new CompteurInstance() id 2

67 / 81
Le modificateur static - Divers

• Si un attribut est constant (final), c’est une bonne pratique


de aussi le déclarer static ⇒ économie de place mémoire
public static final double PI = 3.14;

• Les méthodes static sont généralement des méthodes


utilitaires
public static double toRadians ( double angleDeg ) {
return angleDeg / 180.0 * PI ;
}

La méthode main() est static

• Mis à part les attributs static final, l’utilisation de


membres static est plutôt rare, leur prolifération est souvent
synonyme d’une mauvaise programmation OO
68 / 81
Généralités et conventions

• Généralement ont ne défini qu’une classe par fichier

• Le nom du fichier doit correspondre au nom de la classe (ex :


Rectangle.java)

• Évitez l’utilisation d’accents dans le code ⇒ problèmes


d’encodage

• Respectez les conventions de codage, n’inventez pas votre


style ⇒ lecture du code et maintenance facilitée

• Les conventions de codage Java traduites de l’anglais sont


disponible ici :
ftp-developpez.com/cyberzoide/java/JavaStyle.pdf

69 / 81
Conventions de nommage

De manière générale il faut donner des noms simples mais parlants


et éviter les caractères spéciaux

Variables : 1ère lettre en minuscule, puis la 1ère lettre de chaque


mot en majuscule. Ex : maVariable

Constante : Tout en majuscule, séparer les mots par un ’_’


Ex : MA_CONSTANTE

Classes et Interfaces : 1ère lettre en majuscule, puis la 1ère lettre


de chaque mot en majuscule. Ex : MaClasse

Paquetage : Tout en minuscule, séparer les mots par des ’.’


Ex : bj.cotonou.monpackage

70 / 81
Les classes internes - Nested class

• Une classe interne est une classe déclarée à l’intérieur d’une


autre classe
• C’est un membre de sa classe englobante, elle se déclare
donc au même niveau que les attributs et méthodes
• En tant que membre de sa classe englobante, les modificateurs
de visibilités s’appliquent (public, private, ...)
• Ainsi que le modificateur static, la classe interne est alors
une static nested class
• Tous les membres de la classe interne (même ceux qui sont
private) sont accessibles par la classe englobante (ou
externe)
• Une classe externe doit être déclarée public
71 / 81
Les classes internes - Exemple

public class classeExterne {


classeExterne // Definition d’attributs
// et de methodes ...

private class classeInterne {


classeInterne // Definition d’attributs
// et de methodes ...
}

public static class classeInterneStatic {


classeInterneStatic // Definition d’attributs
// et de methodes ...
}
}

72 / 81
Les classes internes non static - Inner class

Une classe interne est liée à une instance de la classe externe

• La classe interne peut accéder à tous les membres de la


classe englobante
• La classe englobante peut instancier des objets de la classe
interne (en dehors d’un contexte static)
• Une inner class ne peut définir d’attribut ou de méthodes
static
• En dehors de la classe externe, et à condition qu’elle soit
visible (ex : public), une classe interne ne peut être instancié
qu’à partir d’une instance de la classe externe
• Depuis une classe interne, l’instance de la classe externe est
obtenue en préfixant this du nom de la classe externe (ex :
ClasseExterne.this)
73 / 81
Inner class - Exemple
1 public class Mouse { 24 private class MouseSelection {
2 private Point pos; 25 private Point topLeft;
3 private boolean pressed; 26 private Point bottomRight;
4 private MouseSelection selection; 27
5 28 public MouseSelection() {
6 public Mouse() { 29 topLeft = Mouse.this.pos;
7 pos = new Point(); 30 bottomRight = pos;
8 selection = new MouseSelection(); 31 }
9 } 32
10 33 public void trackPosition() {
11 public void move(int x, int y) { 34 if (pressed) {
12 pos.setXY(x,y); 35 topLeft = pos.clone();
13 } 36 bottomRight = topLeft;
14 37 } else {
15 public void clic() { 38 bottomRight = pos.clone();
16 pressed = true; 39 }
17 selection.trackPosition(); 40 }
18 } 41 }
19 42
20 public void release() { 43 public double getSelectionArea() {
21 pressed = false; 44 double dx = selection.topLeft.getX() ...
22 selection.trackPosition(); 45 double dy = selection.topLeft.getY() ...
23 } 46 return Math.abs (dx*dy);
47 }
48 }
74 / 81
Inner class - Exemple
1 public class Mouse { 24 private class MouseSelection {
2 private Point pos; 25 private Point topLeft;
3 private boolean pressed; 26 private Point bottomRight;
4 private MouseSelection selection; 27
5 28 public MouseSelection() {
6 public Mouse() { 29 topLeft = Mouse.this.pos;
7 pos = new Point(); 30 bottomRight = pos;
8 selection = new MouseSelection(); 31 }
9 } 32

Instanciation de la
x, classe
int y) interne
public void trackPosition() {
10 33
11 public void move(int { 34 if (pressed) {
12 depuis la classe externe
pos.setXY(x,y); 35 topLeft = pos.clone();
13 } 36 bottomRight = topLeft;
14 37 } else {
15 public void clic() { 38 bottomRight = pos.clone();
16 pressed = true; 39 }
17 selection.trackPosition(); 40 }
18 } 41 }
19 42
20 public void release() { 43 public double getSelectionArea() {
21 pressed = false; 44 double dx = selection.topLeft.getX() ...
22 selection.trackPosition(); 45 double dy = selection.topLeft.getY() ...
23 } 46 return Math.abs (dx*dy);
47 }
48 }
75 / 81
Inner class - Exemple
1 public class Mouse { 24 private class MouseSelection {
2 private Point pos; 25 private Point topLeft;
3 private boolean pressed; 26 private Point bottomRight;
4 private MouseSelection selection; 27
5 28 public MouseSelection() {
6 public Mouse() { 29 topLeft = Mouse.this.pos;
7 pos = new Point(); 30 bottomRight = pos;
8
Accèsselection
aux membres de la classe
= new MouseSelection(); 31 }
9 } 32
10 interne depuis la classe externe 33 public void trackPosition() {
11 public void move(int x, int y) { 34 if (pressed) {
12 pos.setXY(x,y); 35 topLeft = pos.clone();
13 } 36 bottomRight = topLeft;
14 37 } else {
15 public void clic() { 38 bottomRight = pos.clone();
16 pressed = true; 39 }
17 selection.trackPosition(); 40 }
18 } 41 }
19 42
20 public void release() { 43 public double getSelectionArea() {
21 pressed = false; 44 double dx = selection.topLeft.getX() ...
22 selection.trackPosition(); 45 double dy = selection.topLeft.getY() ...
23 } 46 return Math.abs (dx*dy);
47 }
48 }
76 / 81
Inner class - Exemple
1 public class Mouse { 24 private class MouseSelection {
2 private Point pos; 25 private Point topLeft;
3 private boolean pressed; 26 private Point bottomRight;
4 private MouseSelection selection; 27
5 28 public MouseSelection() {
6 public Mouse() { 29 topLeft = Mouse.this.pos;
7 pos = new Point(); 30 bottomRight = pos;
8
Accès aux membres de la classe31
selection = new MouseSelection(); }
9 } 32
10 externe depuis la classe interne 33 public void trackPosition() {
11 public void move(int x, int y) { 34 if (pressed) {
12 pos.setXY(x,y); 35 topLeft = pos.clone();
13 } 36 bottomRight = topLeft;
14 37 } else {
15 public void clic() { 38 bottomRight = pos.clone();
16 pressed = true; 39 }
17 selection.trackPosition(); 40 }
18 } 41 }
19 42
20 public void release() { 43 public double getSelectionArea() {
21 pressed = false; 44 double dx = selection.topLeft.getX() ...
22 selection.trackPosition(); 45 double dy = selection.topLeft.getY() ...
23 } 46 return Math.abs (dx*dy);
47 }
48 }
77 / 81
Inner class - Exemple
public class Main {
public static void main(String[] args) {
Mouse mouse = new Mouse();

mouse.move(10,20);
mouse.clic();
mouse.move(20,30);
mouse.release();

// Affiche : Sel. area : 100.0


System.out.println("Sel. area : " + mouse.getSelectionArea());
}
}

Si la classe interne MouseSelection était public alors on pourrait


l’instancier depuis l’extérieur de sa classe englobante
public static void main(String[] args) {
// comme avant ...

Mouse.MouseSelection sel = mouse.new MouseSelection();


...
}
78 / 81
Les classes internes static - Static nested class

Une static nested class peut exister sans instance de la classe


externe

• Tout comme les méthodes static, une classe interne static


ne peut accéder qu’aux membres static de sa classe externe

• L’accès à la classe interne se fait par le biais de sa classe


externe (ex : ClasseExterne.ClasseInterneStatic)

• De même, en dehors de sa classe englobante, une nested class


peut être instanciée via le nom sa classe externe (ex :
ClasseExterne.ClasseInterneStatic maClasseInterneStatic =
new ClasseExterne.ClasseInterne())

79 / 81
Les classes locales Avancé

• Une classe locale est une classe interne définie dans une
méthode ou un bloc de code

• Sa portée est donc limitée au bloc dans lequel elle est définie

• C’est pourquoi elle ne peut être qualifié de public, private


ou static

• Seuls les variables final (attributs de la classe externe,


paramètres et variables locales à la méthode) sont accessibles
par la classe locale

• Très peu utilisée !

80 / 81
Les classes anonymes Avancé

• Une classe anonyme est une classe qui n’as pas de nom

• C’est une classe locale car elle est nécessairement déclarée


dans un bloc de code

• N’ayant pas d’identificateur elle est instanciée au moment de


sa déclaration (usage unique, implicitement final)

• Ne possède pas de constructeur

• Une classe anonyme est utilisée pour implémenter une


interface ou étendre une classe

• Elle est souvent employée dans la gestion des événements


d’entrée-sortie (listener)
81 / 81
Avec les classes anonymes, on peut placer directement l’instance de la
classe d’écoute comme paramètre de la méthode d’ajout d’une écoute à un
élément d’interface. (voir programme TestAnonymousAction.java)
class TestAnonymousActionListener extends JPanel {
public TestAnonymousActionListener() {
txt1 = new JTextField(10);
btn1 = new JButton("Button1");
btn2 = new JButton("Button2");
add( txt1); add(btn1); add(btn2);

// Créer classe interne anonyme pour chaque ActionListener


btn1.addActionListener (new ActionListener() {
public void actionPerformed ( ActionEvent e) {
txt1. setText("Button1 clicked"); }
});
btn2. addActionListener (new ActionListener() {
public void actionPerformed(ActionEvent e) {
txt1. setText("Button2 clicked"); }
});
} 1
Avec les classes anonymes, on peut placer directement l’instance de la
classe d’écoute comme paramètre de la méthode d’ajout d’une écoute à un
élément d’interface. (voir programme TestAnonymousAction.java)
class TestAnonymousActionListener extends JPanel {
public TestAnonymousActionListener() {
txt1 = new JTextField(10);
btn1 = new JButton("Button1");
btn2 = new JButton("Button2");
add( txt1); add(btn1); add(btn2);

// Créer classe interne anonyme pour chaque ActionListener


btn1.addActionListener (new ActionListener() {
public void actionPerformed ( ActionEvent e) {
txt1. setText("Button1 clicked"); }
});
btn2. addActionListener (new ActionListener() {
public void actionPerformed(ActionEvent e) {
txt1. setText("Button2 clicked"); }
});
} 1
Avec les classes anonymes, on peut placer directement l’instance de la
classe d’écoute comme paramètre de la méthode d’ajout d’une écoute à un
élément d’interface. (voir programme TestAnonymousAction.java)
class TestAnonymousActionListener extends JPanel {
public TestAnonymousActionListener() {
txt1 = new JTextField(10);
btn1 = new JButton("Button1");
btn2 = new JButton("Button2");
add( txt1); add(btn1); add(btn2);

// Créer classe interne anonyme pour chaque ActionListener


btn1.addActionListener (new ActionListener() {
public void actionPerformed ( ActionEvent e) {
txt1. setText("Button1 clicked"); }
});
btn2. addActionListener (new ActionListener() {
public void actionPerformed(ActionEvent e) {
txt1. setText("Button2 clicked"); }
});
} 81