mercredi 6 février 2013

Références et types primitifs

I. Notion de référence

I.1 Variable de type référence

Supposons que l'on dispose d'une classe Cercle.
public class Cercle 
{
  private int aDiametre;
  
  public Cercle( final int pDiametre )
  {  
    this.aDiametre = pDiametre;  
  }
  
  public void doubleTaille()
  {  
    this.aDiametre = this.aDiametre * 2;  
  }
  
  public int getDiametre()
  {   
    return this.aDiametre; 
  }
} // Cercle

  • Lorsque l'on déclare un objet Cercle vC; on déclare une variable qui pourra contenir une référence (un pointeur) vers un objet de type Cercle (donc un exemplaire de la classe Cercle).
    Lorsqu'on écrit new Cercle(12) la machine virtuelle Java cherche un emplacement mémoire suffisant pour stocker les attributs d'un cercle (de diamètre 12), réserve cet espace mémoire, appelle le constructeur (en fonction des paramètres) pour initialiser les attributs, puis retourne l'adresse de l'emplacement mémoire.


  • Si on ne stocke pas cette adresse (ou référence), on ne pourra pas manipuler l'objet qu'on vient de créer !
    On peut résumer tout ça en une seule instruction : Cercle vC = new Cercle(12);
I.2 Références particulières
  • Nous en connaissons déjà une : this. Elle désigne toujours l'objet courant (c'est-à-dire l'objet sur lequel a été appelé la méthode qu'on est en train d'écrire).
  • Mais il y en une autre très importante : null. C'est d'abord la valeur par défaut des attributs de type objet. Mais surtout, cette valeur est reconnue comme signifiant que la référence ne pointe vers aucun objet.

I.3 Copie de références

Considérons l'exemple suivant :

Cercle vC1 = new Cercle(12);
Cercle vC2 = vC1;
vC2.doubleTaille();
System.out.println("Taille de vC1 : " + vC1.getTaille());
System.out.println("Taille de vC2 : " + vC2.getTaille());


Après avoir écrit la déclaration Cercle vC2; lorqu'on écrit l'affectation vC2 = vC1;  on ne recopie pas l'objet, mais seulement la référence, ce qui fait que vC1 et vC2 désignent (pointent vers) le même objet !

Donc, lorqu'on agrandit le cercle vC2 par vC2.doubleTaille(); puis que l'on consulte la taille de vC1 par vC1.getDiametre() on constate que le diamètre de vC1 a effectivement doublé.

Le programme affiche donc le résultat:

Taille de vC1 : 24
Taille de vC2 : 24
 
I.4 Référence et passage de paramètre

Soit la procédure :
  
  public void afficheDiametrefinal Cercle pC 
 
    System.out.printlnpC.getDiametre() )
  }


Lorsqu'on appelle une telle procédure en écrivant  :

Cercle vC1 = new Cercle(12);
afficheDiametre(vC1);

 

cela ne semble pas dangereux. Mais on pourrait avoir la mauvaise idée d'ajouter pC.doubleTaille(); juste après l'affichage dans la procédure afficheDiametre. Dans ce cas, la fonction qui appelle afficheDiametre constatera que l'objet référencé par vC1 a doublé de taille. Le mot final ne protège que la référence pC, pas l'objet pointé par cette référence !

I.5 Classe et type référence

Tous les types de données qui  sont des classes (du JDK ou inventées par le programmeur) sont appelés types objet (reference types en anglais).




II. Les types primitifs

II.1 Liste des types primitifs

Les types boolean, int, long, et double (mais aussi char, byte, short, et float) ne sont pas des classes. On les appelle des types primitifs (primitive types en anglais). On s'intéresse directement à la valeur contenue dans une variable de type primitif, alors qu'on ne s'intéresse pas à l'adresse contenue dans une variable de type référence mais à l'objet qui est à cette adresse.

II.2 Copie de type primitf

On peut constater les conséquences en remplaçant le type objet Cercle par le type primitif int dans les exemples du I.3, ce qui donne :

int vI1 = 12
int vI2 = vI1;
vi2 *= 2;
System.out.println("Valeur de vI1 : " + vI1)
System.out.println("Valeur de vI2 : " + vI2)
   

Cette fois le programme affiche le résultat

Valeur de vI1 : 12
Valeur de vI2 : 24


La modification de vI2 n'a donc pas affecté vI1.

II.3 Type primitif et passage de paramètre

Reprenons l'exemple du I.4 en utilisant cette fois un type primitif. Soit la procédure :
 
  public void afficheEntierfinal int pI 
  
    System.out.printlnpI )
  }


Lorsqu'on appelle une telle procédure en écrivant int vI3 = 33; afficheEntier( vI3 ); le comportement est identique à celui du I.4.

Par contre, si on ajoute pI = pI * 2; à la fin de la procédure, elle ne compile plus, car le mot final protège la modification du paramètre pI.

Si on supprime ce mot, ça compile, mais bien sûr vI3 n'est pas modifié et vaut toujours 33 !

Aucun commentaire:

Enregistrer un commentaire