← (précédent) C.2 Les contructeurs
C.4 Exercices corrigés de P.O.O
Accueil S O M M A I R E

C.3 La nature profonde de l'objet.

C.3.1 La référence à un objet.

Tout programmeur (en P.O.O) se doit de comprendre la nature profonde des objets. Sinon, il va devant des catastrophes. Vous pouvez concevoir un programme qui n' affiche aucune erreur ni à la compilation, ni à l' exécution. Mais le résultat escompté ne se produira jamais. Ce chapitre est donc un véritable tournant. Soyez encore plus attentifs que d' habitude.
Rappel : les types primitifs (ou type de base) en java sont : byte, short, int, long, float, double, boolean et char.

L' instruction byte b = 40; est en fait une somme de deux intructions : byte b; et b = 40;

Lorsqu' on écrit : byte b; à la compilation, une case mémoire est créée . Cette case sera prête à accueillir une valeur comprise entre -128 et + 127. Et cela se fait grâce l' instruction (par exemple) b = 40;
Voyons les choses de façon plus imagée.

Les cases mémoires de type byte, chort, int et long se ressemblent. Normal, ces cases sont toutes prêtes à accueillir des valeurs entières. La différence, c' est la taille. Par exemple, ces cases pourraient être des carrés. Le byte, un carré de 1 cm. de côté. Le short, un carré de 2 cm de côté. Le int, un carré de 4 cm de côté et le long, un carré de 8 cm de côté.

Les cases de type float et double se resseblent. Normal, ces cases sont toutes prêtes à accueillir des valeurs réelles. Par exemple, ces cases pourraient être des rectangles. Dans chaque rectangle, un trait le divisant en 2. Une partie ayant la forme d' un carré prêt à accueillir la partie entière de la valeur réelle. Et une partie rectangulaire pouvant accueillir la partie décimale. Pour float, le rectangle pourrait être de 16cm de largeur et de longueur 17 cm.La partie entière étant un carré de 16cm de côté et la partie décimale, étant un rectangle de longueur 16cm et de largeur 1cm. Les cases de type double étant des rectangles de 32 cm de largeur et de18cm de longueur. La partie entière étant un carré de 32 cm. La partie décimale, un rectangle de 32 cm de longueur et une largeur de 2cm.

Avant de continuer, je le répète encore, ce n' est qu' une image. Dans la mémoire, tout est codé binaire (des 1 et des 0). Afin de mieux visualiser, vous pouvez prendre un crayon ordinaire et une règle, et pourquoi pas, une gomme. Vous dessinez ce que je dis et vous voyez ce que cela pourrait être.

Pour les variables de type boolean. Ce serait plutôt un cercle d' un diamètre quelconque. Ce cercle est prêt à accueillir une pièce. L' une des faces de la pièce continet l' inscription true et l' autre face, l' inscription false.

Les variables de type char seraient plutôt représentés par un losange de 2 cm de côté. Les angles pouvant être de 90 dégrés. Dans ce cas, le type devient un nombre entier. Nous avons vu qu' on peut transformer un type char en type short (ou int, ou long). La taille dy type char est celle du type short. Ce losange peut aussi avoir des angles différents de 90 dégrés. Dans ce cas, le type char n' est pas un nombre entier. Mais plutôt un caractère.

Avant de continuer, tentons un exemple.

 EXEMPLE zéro : 

1.    public class Primitifs
2.    {
3.        public static void main (String [] args)
4.        {
5.            byte b1 = 40; byte b2 = 50;
6.            System.out.println("valeur de b1 = " + b1 + " et valeur de b2 = " + b2);
7.            b2 = b1;
8.            System.out.println("valeur de b1 = " + b1 + " et valeur de b2 = " + b2);
9.            b2 += 1;
10.           System.out.println("valeur de b1 = " + b1 + " et valeur de b2 = " + b2);
11.       }
12.   }

compilez puis exécutez : il sera affiché :

valeur de b1 = 40 et valeur de b2 = 50
valeur de b1 = 40 et valeur de b2 = 40
valeur de b1 = 40 et valeur de b2 = 41

Ligne 5 : on déclare 2 variables b1 et b2 de type byte. En même temps, on affecte à b1 la valeur 40 et à b2, la valeur 50. C' est pourquoi, à l' affichage, on voit bien valeur de b1 = 40 et valeur de b2 = 50. Normal.
Ligne 7 : b2 = b1. Cela veut dire qu' on prend une copie de la valeur de b1 qu' on affecte à b2. Donc, b2 a maintenant la valeur 40. Et b1 a toujours la valeur 40. Normal, on n' a pas toujours à la valeur de b1. On a seulement copié la valeur de b1 et on l' a mise dans la case mémoire de b2. Les cases b1 et b2 ont donc chacune la même valeur 40. D' où l' affichage : valeur de b1 = 40 et valeur de b2 = 40
Ligne 9 : b2 += 1; On incrément la valeur de b2. Autrement dit, on ajoute 1 à la valeur de b2. Donc b2 = 40 + 1 = 41. On n' a pas touché la valeur de b1. Donc, b1 = 40 et b2 = 41. D' où l' affichage : valeur de b1 = 40 et valeur de b2 = 41

Ceci est une révision des opérations sur les variables. C' est indispensable de le rappeler pour la suite des explications.
Une classe qui sert à fabriquer des cercles dans l' espace dimension 2

1.    public class Cercle
2.    {
3.        public double centreX;
4.        public double centreY;
5.        public double rayon;
6.        
7.        
8.        public Cercle(double x, double y, double r)
9.        {
10.           centreX = x; centreY = y; rayon = r; 
11.           
12.       }
13.       public void afficherCoord()
14.       {
15.           System.out.println("coordonnées du cercle : " + centreX + ", " + centreY + " et " + rayon);
16.       }
17.   }
Pas trop d' explications ici. Vous connaissez déjà la classe Cercle. LEs 3 champs de cette classe : centreX, centreY et rayon. Un constructeur déclaré en ligne 8. et une méthode permettant d' afficher les coordonnées du cercle déclarée en ligne 13.

Ici, j' ai déclaré les champs en mode public. Parce que je veux les manipuler hors de la classe Cercle afin de vous montrer que les variables de type objet ne réagissent pas comme les variables de type primitif.

 EXEMPLE 1 : 

1.    public class Objets1
2.    {
3.        public static void main (String [] args)
4.        {
5.            Cercle A = new Cercle(1, 11, 111);
6.            Cercle B = new Cercle(2, 22, 222);
7.            A.afficherCoord();
8.            B.afficherCoord();
9.            System.out.println();
10.           A = B; 
11.           A.afficherCoord();
12.           B.afficherCoord();
13.           System.out.println();
14.           A.centreX = 3;
15.           A.afficherCoord();
16.           B.afficherCoord();
17.       }
18.   }
compilez puis exécutez. Il sera affiché :

coordonnées du cercle : 1.0, 11.0 et 111.0
coordonnées du cercle : 2.0, 22.0 et 222.0

coordonnées du cercle : 2.0, 22.0 et 222.0
coordonnées du cercle : 2.0, 22.0 et 222.0

coordonnées du cercle : 3.0, 22.0 et 222.0
coordonnées du cercle : 3.0, 22.0 et 222.0

Lignes 5 et 6 : instanciation d' un objet Cercle A de coordonnées 1, 11 et 11. L' objet B instancié a pour coordonnées 2, 22, 222.
Lignes 7 et 8 : on demande à afficher les coordonnées de A et de B. Et les deux premières lignes d' affichages sont correctes.
coordonnées du cercle : 1.0, 11.0 et 111.0
coordonnées du cercle : 2.0, 22.0 et 222.0

La ligne 9 System.out.println(); nous permet simplement de sauter une ligne.
Ligne 10 : A = B. Donc, on prend la valeur de B qu' on met dans A. Lignes 11 et 12, on demande l' affichage des coordonnées de A et de B. Et les deux lignes suivantes affichent les coordonnées de B et de B. Normal. A = B.
coordonnées du cercle : 2.0, 22.0 et 222.0
coordonnées du cercle : 2.0, 22.0 et 222.0

Ligne 13 nous permet simplement de sauter une ligne.
Ligne 14 : On change la valeur du champ centreX de l' objet A. Lignes 15 et 16, on demande à afficher les coordonnées de A et de B. Et là, surprise, A et B ont mêmes coordonnées. centreX n' est pourtant pas un champ statique. C' est un champ qui n' est pas déclaré avec le mot clé static (Voir exemple P4 du chapitre C1). Donc, chaque objet possède son propre champ centreX. Changer la valeur de ce champ pour un objet, ne doit pas changer la valeur de ce champ pour l' autre objet. Que s' est-il passé ? ...
Les variables de type Objet ne réagissent pas comme les variables de type primitif. De même qu' on s' est donné des images de variables de type primitif, de même, nous allons nous donner une image d' un Objet afin de comprendre ce qui s' est passé.
Les objets sont des variables de toute autre nature.

L' instruction Cercle A = new Cercle(1, 11, 111); est en fait la somme de 2 instructions qui sont : Cercle A; et A = new Cercle(1, 11, 111);

Lorsqu' on écrit : Cercle A; à la compilation, une case mémoire est créée ? Mais attention, cette case mémoire n' est pas sensée accueillir une objet de type Cercle. Mais plutôt une référence à un objet de type Cercle.

Une référence ? ... Qu' est ce que c' est ? ... C' est une adresse dans la mémoire. C' est une chaine de caractères que vous n' avez pas besoin de connaître. Nous l' afficherons dans un exemple. Mais ce n' est vraiment pas indispensable. Cette chaine de caractères est un code caractérisant un objet de type Cercle.
En écrivant A = new Cercle(1, 11, 111); on crée l' objet. En fait, new Cercle(1, 11, 111) est le nouvel objet de type Cercle. Cet objet n' est pas mis dans une case mémoire comme avec une variable de type int. Mais cet objet possède sa propre case mémoire dont la forme dépend du nombre et de la nature des membres de la classe Cercle. J' ai comparé les variables de type byte, short, int et long à des carrés dont le côté varie selon le type. Pour un objet, je dirais que sa forme est la somme des formes de chaque champ et de chaque méthode. La case mémoire A (et non pas l' objet) est contient donc l' adresse de cet objet new Cercle(1, 11, 111). On dit que A pointe vers l' objet new Cercle(1, 2, 3). C' est donc un abus de langage lorsqu' on dit que A est l' objet. En termes plus simples, on peut dire que A est un représentant de l' objet new Cercle(1, 11, 111). Pour modifier cet objet, on manipule plutôt son représentant.

En écrivant Cercle B = new Cercle(2, 22, 222); on écrit en fait 2 instructions : Cercle B; et B = new Cercle(2, 22, 222);
B est une référence à l' objet new Cercle(2, 22, 222). Autrement dit : B contient l' adresse de l' objet new Cercle(2, 22, 222). En écrivant A = B, On copie la référence de B et on la met dans A. Ce n' est pas l' objet représenté par B qu' on met dans A. Non, mais plutôt l' adresse decet objet qu' on met dans A. On dit alors que A pointe maintenant vers l' objet new Cercle(2, 22, 222). Ou plus simplement que A représente maintenant l' objet new Cercle(2, 22, 222). Donc, maintenant, A et B représentent le même Objet new Cercle(2, 22, 222).

C' est pourquoi les instructions des lignes 11 et 12 entrainent l' affichage de ceci :
coordonnées du cercle : 2.0, 22.0 et 222.0
coordonnées du cercle : 2.0, 22.0 et 222.0

On peut modifier cet objet new Cercle(2, 22, 222) en utilisant le représentant A ou le représentant B. En POO, on dit, la référence A ou la référence B. Habitué vous à dire référence. Représentant est un mot que j' ai choisi pour faire plus simple. En écrivant (ligne 14) A.centreX = 3; on a modifié l' abscisse du centre du cercle A. C' est à dire qu' on a modifié l' abscisse de l' objet new Cercle(2, 22, 222). cet objet devient donc new Cercle(3, 22, 222). Puisque B fait aussi référence au même objet, on a donc aussi modifié le cercle représenté par B. C' est évident. C' est pourquoi les instructions des lignes 15 et 16 entrainent les affichages
coordonnées du cercle : 3.0, 22.0 et 222.0
coordonnées du cercle : 3.0, 22.0 et 222.0

Si ces explications vous paraissent un tantinet ambigues, c' est un peu normal. Mais sachez que ce n' est pas aussi compliqué que ça.

Relisez 2, 3 4 ou 5 fois. Soyez attentifs et ça ira.

Vous devez retenir que les variables de type primitifs sont des cases mémoires qui contienent des valeurs. Les objets, quant à eux induisent 2 cases mémoires. L' une contenant la référence à l' objet. Et l' autre contenant l' objet. On manipule plutôt la référence.

C.3.2 La destruction d' un objet.

De même qu' un objet peut-être construit, de même, il peut être détruit. Un objet se détruit de 2 façons : implicitement et explicitement.

En écrivant : Cercle A = new Cercle(1, 11, 111);, on instancie un objet de type Cercle.
En écrivant : Cercle B = new Cercle(2, 22, 222);, on instancie un autre objet de type Cercle.
En écrivant : A = B; on met la référence de de l' objet new Cercle(2, 22, 222) dans A. Du coup, l' objet new Cercle(2, 22, 222) est référencé par la variable A. Conséquence : l' objet new Cercle(1, 11, 111) n' est plus référencé.
Lorsqu' un objet n' est plus référencé, il est détruit automatiquement. C' est la destruction implicite de l' objet. En java, cette destruction est assurée par ce qu' on appelle le ramasse-miettes. En Java, ça s' appelle le garbage collector.

En toute rigueur, cette destruction n' est pas si automatique que ça. Ainsi, il est possible qu' un programme s' arrête avant même le début de la destruction de l' objet. Par exemple si le temps qui reste avant la fin du programme est très court au moment où un objet n' est plus référencé.
Pour éviter de saturer la mémoire avec ces objets non référencés, on procède à la destruction explicite des objets. La destruction explicite se fait par des méthodes spécifiques. Nous n' allons pas en donner des exmples ici. Nous le ferons ultérieurement.

C.3.3 La comparaison des objets.

Une classe qui sert à fabriquer des cercles dans l' espace dimension 2

1.    public class Cercle
2.    {
3.        private double centreX;
4.        private double centreY;
5.        private double rayon;
6.        
7.        
8.        public Cercle(double x, double y, double r)
9.        {
10.           centreX = x; centreY = y; rayon = r; 
11.           
12.       }
13.       public void afficherCoord()
14.       {
15.           System.out.println("coordonnées du cercle : " + centreX + ", " + centreY + " et " + rayon);
16.       }
17.   }
Revoici la classe Cercle avec les données encapsulées. C a d déclarés en private.

 EXEMPLE 2 : 

1.    public class Objets2
2.    {
3.        public static void main (String [] args)
4.        {
5.            Cercle A = new Cercle(1, 11, 111);
6.            Cercle B = new Cercle(1, 11, 111);
7.            
8.            if( A == B )
9.            {
10.                System.out.println("A est égale à B");
11.           }
12.           else
13.           {
14.                System.out.println("A n' est pas égale à B");
15.           }
16.
17.       }
18.   }
Compilez, puis exécutez, il sera affiché : A n' est pas égale à B

Etonnant ? ... Pas vraiment. Même si les objets créés ont les mêmes coordonnées, ils sont différents.
Ligne 5 : on crée l' objet new Cercle(1, 11, 111); et on place sa référence dans la variable A.
Ligne 6 : on crée un autre objet new Cercle(1, 11, 111); et on place sa référence dans la variable B.
Ce sont deux objets. Donc, deux adresses différentes. A et B sont des adresses.


 EXEMPLE 3 : 

1.    public class Objets3
2.    {
3.        public static void main (String [] args)
4.        {
5.            Cercle A = new Cercle(1, 11, 111);
6.            Cercle B = new Cercle(1, 22, 222);
7.            A = B;
8.            if( A == B )
9.            {
10.                System.out.println("A est égale à B");
11.           }
12.           else
13.           {
14.                System.out.println("A n' est pas égale à B");
15.           }
16.
17.       }
18.   }
Compilez, puis exécutez, il sera affiché : A est égale à B

Cette fois, il y a égalité, bien que les objets créés soient différents. En effet, en ligne 7, on a l' instruction A = B; on a pris l' adresse du deuxième objet qu' on a mis dans la variable A. Les deux variables ont maintenant le même contenu. D' où l' égalité.
Dans un programme, vous aurez besoin de comparer les objets au moins de 3 façons différentes :
1. comparaison des adresses. 2. comparaisons des champs équivalents. 3. comparaison de leurs origines : savoir si 2 objets appartiennent à la même classe.

Comparer les références : c' est l' exemple 2. Lorsque ces 2 références représentes deux objets, il n ' y a pas égalité. Parce que en créant 2 objets, on crée forcément 2 références.
Lorsqu' on procède d' abord à l' égalité des références (exemple 3), il y a forcément égalité.

Comparer les champs équivalents : on le fait en écrivant une méthode spécifique dans cette classe. Cette méthode sera forcément de type bolléenne. Dans la définition de cette méthode, vous écrivez que si chaque champ d' un objet a la même valeur que le même champ de l' autre objet, alors, la méthode doit retourner la valeur true. sinon, c' est false. On le verra dans l' exemple suivant.

comparer les origines des objets : il s' agit de savoir si 2 objets sont des instances d' une même classe. Exemple ci-dessous!
Une classe qui sert à fabriquer des cercles dans l' espace dimension 2

1.    public class Cercle
2.    {
3.        private double centreX;
4.        private double centreY;
5.        private double rayon;
6.        
7.        
8.        public Cercle(double x, double y, double r)
9.        {
10.           centreX = x; centreY = y; rayon = r; 
11.           
12.       }
13.       public void afficherCoord()
14.       {
15.           System.out.println("coordonnées du cercle : " + centreX + ", " + centreY + " et " + rayon);
16.       }
17.
18.       public boolean compareObjets(Cercle M)
19.       {
20.            boolean verite = false;
21.            if(this.centreX == M.centreX && this.centreY == M.centreY && this.rayon == M.rayon)
22.            {
23.                 verite = true;
24.            }
25.            return verite;
26.       }
27.   }
J' ai ajouté une méthode à la classe Cercle. C' est la méthode compareObjets. Cette méthode est déclarée sans le mot clé static. On doit donc d' abord créer un objet sur lequel on applique la méthode. Cette méthode requiert un argument de type Cercle. Ce qui signifie qu' on doit avoir 2 objets pour utiliser la méthode. Normal, il s' agit de comparer 2 objets. La méthode est forcément de type boolean. Puisque une comparaison doit toujours fournir la valeur true ou false. Dans la définition de la méthode, on crée une variable booléenne. c'est elle qui sera retournée. Cette variable est initialisée à false (ligne 20). this représente l' objet sur lequel on applique la méthode. En ligne 21, on dit alors que si le champ centreX de l' objet sur lequel la méthode est appliquée a la même valeur que celui de l' objet entre parenthèses et en même temps, le champ centreY de cet objet a la même valeur que le centreY de l' objet en paramètre, et en même temps, les champs rayon des deux objets ont la même valeur, alors, verite = true. Sinon, verite = false. Et naturellement, on retourne la variable booléenne verite.
 EXEMPLE 4 : 

1.    public class Objets4
2.    {
3.        public static void main (String [] args)
4.        {
5.            Cercle A = new Cercle(1, 11, 111);
6.            Cercle B = new Cercle(1, 11, 111);
7.            
8.            if( A.compareObjets(B) )
9.            {
10.                System.out.println("A est égale à B");
11.           }
12.           else
13.           {
14.                System.out.println("A n' est pas égale à B");
15.           }
16.
17.       }
18.   }
Compilez puis exécutez. Il sera affiché : A est égale à B

Normal : la comparaison se fait sur les valeurs des champs. Donc, bien que A et B soient des références différentes, il y a égalité.
 EXEMPLE 5 : 

1.    public class Objets5
2.    {
3.        public static void main (String [] args)
4.        {
5.            Cercle A = new Cercle(1, 11, 7);
6.            Cercle B = new Cercle(1, 35, 111);
7.            
8.            if( A.getClass() == B.getClass() )
9.            {
10.                System.out.println("A et B sont issues de la même classe");
11.           }
12.           else
13.           {
14.                System.out.println("A et B ne sont pas issues de la même classe");
15.           }
16.
17.       }
18.   }
Compilez puis exécutez. Il sera affiché : A et B sont issues de la même classe

Normal : A et B appartiennent à la même classe. La méthode getClass() appartient à la classe Object (remarquez le c entre e et t). La classe Object est une classe prédéfinie par le langage Java. Toutes les classes, prédéfinies ou créées par le programmeur, peuvent utiliser toutes les méthodes de cette classe. Nous verrons pourquoi dans le chapitre consacré à l' héritage et au polymorphisme.

Exercice : Mettez dans le même répertoire la classe cercle et la classe Points2. Puis, dans un programme, instanciez un objet Points2 et un objet Cercle. Comparer avec la méthode getClass() et voyez le résultat édifiant.

C.3.4 La copie d' un objet.

Soit la classe Cercle avec ses trois champs centreX, centreY et rayon.
L' instruction Cercle A = new Cercle(1, 2, 3); permet d' instancier un objet de type Cercle. Copier cet objet, c' est copier les champs de cet objet pour les mettre dans un autre objet de type Cercle.
Cercle B = new Cercle(1, 2, 3);
Ici, c' est très simple. Parce qu' on connait les valeurs des champs de A. Mais si les valeurs de ces champs sont obtenues au cours du programme, alors il y a utre façon de faire.
B.centreX = A.centreX; B.centreY = A.centreY; B.rayon = A.rayon;
On pourra même écrire une méthode qui permet de copier un objet. Nous verrons des exemples dans le chapitre consacré aux exercices corrigés.

Attention : Si un champ de la classe est déjà de type Objet. La façon de faire ci-dessus permet seulement de copier les références.

C.3.5 La conversion des objets.

De même qu' on peut par exemple convertir un type byte en type int. On peut convertir un objet en un objet d' un autre type. Mais là, c' est une opération très délicate. Il faut déjà comprendre les notions d' héritage et de polymorphisme. Donc, nous verrons des exemples seulement après avoir étudié ce chapitre.

Ce chapitre a été avare en exemples. C' est un peu normal. Nous nous rattraperons dans les chapitres réservés aux exercices corrigés de P.O.O A ce moment là, les explications des exercices nous amèneront à revoir les explications de ce chapitre.
La connaissance, c'est bien. La partager, c'est mieux
Conseiller ce site à un(e) ami(e):

Son e-mail est :       
Une suggestion à faire pour ce site ? ... Contact : webmaster@debutantprog.com
← (précédent) C.2 Les contructeurs
C.4 Exercices corrigés de P.O.O
Accueil S O M M A I R E

C.3 La nature profonde de l'objet.