Constructeurs, instanciation, opérateur new

Quelques rappels

Ainsi qu'il a été dit précédemment, la déclaration d'une variable de référence (c'est à dire correspondant à un type non primitif) ne réalise aucune allocation d'objet correspondant à ce type. Une variable d'instance d'un tel type a comme valeur par défaut null.
On peut lui affecter la référence d'un objet de type «compatible» avec le type auquel elle a été associée lors de sa définition (typiquement le type correspondant à la variable ou une classe dérivée) et en particulier le résultat d'une instanciation d'un objet (compatible) réalisée avec l'opérateur new appliqué à un constructeur de ce type.



Les constructeurs

Le constructeur par défaut

Le langage fournit pour chaque classe un mécanisme d'instanciation par défaut qui, outre la création de l'objet (instance) attribue une valeur par défaut à chaque variable d'instance, qu'elle soit d'un type primitif (voir les valeurs par défaut) ou de référence (sa valeur est alors null).
Cette instanciation par défaut est réalisée explicitement pour une classe de nom C (si le constructeur par défaut y a été redéfini, c'est cette nouvelle définition qui sera utilisée, comme nous le verrons dans la suite) par l'expression
        new C( )
dont la valeur est donc une référence sur la nouvelle instance créée.
Il faut noter dès maintenant que si la classe est sous-classe directe d'une classe SurC, c'est-à-dire dont la définition correspond à :
          class C extends SurC
le constructeur par défaut de cette sur-classe sera préalablement appelé
.
Ainsi que nous le verrons, il est possible de définir et invoquer d'autres constructeurs que celui-ci, voire le redéfinir lui-même, mais


   --> cat Construct1.java
   class X1 {
      int a;
      int b = 10;
      }
   class X2 extends X1{
      int c;
      X1 x1;
      }
   class Construct1 {
      public static void main(String[ ] arg){
      X1 x1 = new X1( );  // constructeur par défaut de X1
      System.out.println(x1.a + " " + x1.b);
      X2 x2 = new X2( );  // constructeur par défaut de X2
      System.out.println(x2.a + " " + x2.b + " " + x2.c + " " + x2.x1);    
      }
   }             
   --> javac Construct1.java
   --> java Construct1
   0 10
   0 10 0 null
   -->

Les constructeurs spécifiques illustrés par des exemples


Le type d'instanciation minimale fournie au travers du constructeur par défaut ne convient bien évidemment pas nécessairement aux besoins spécifiques de nombreuses classes qui supposent par exemple la possibilité de paramétrer l'initialisation des variables d'instance. Aussi est-il possible de définir dans une classe des constructeurs appropriés.
Un constructeur a les caractéristiques suivantes : Ces deux caractéristiques font de la séquence correspondante un constructeur qui ne sera appelable qu'au travers de l'opérateur new pour instancier la classe correspondante.
Il est important de noter que dès qu'un constructeur est défini par l'utilisateur, le constructeur par défaut n'est plus utilisable explicitement (il est néanmoins appelé implicitement avant le constructuer explicitement spécifié).

Dans le premier exemple donné ci-après


   --> cat Construct2.java
   class X1 {
      int a, b;
      X1(int a, int b){
         this.a = a; // this fait référence à l'objet en construction
         this.b = b; // pour distinguer la variable a du paramètre a
      }
   }
   class X2 extends X1{
      int c;
      X1 x1;
      }
   class Construct2 {
      public static void main(String[ ] arg){
      X1 x1 = new X1(10, 20);  // constructeur spécifique X1(int,int)
      System.out.println(x1.a + " " + x1.b);
      X2 x2 = new X2();  // constructeur par défaut de X2
      System.out.println(x2.a + " " + x2.b + " " + x2.c + " " + x2.x1);    
      }
   }      
   --> javac Construct2.java
   Construct2.java:8: cannot resolve symbol
   symbol  : constructor X1  ()
   location: class X1
   class X2 extends X1{
   ^
   1 error
   --> 

on notera Dans ce second exemple, nous avons défini un constructeur pour la classe X2 et avons ajouté un second constructeur (de profil semblable à celui d'un constructeur par défaut, c'est-à-dire sans paramètre) pour la classe X1. Cette possibilité de donner plusieurs définitions d'une fonction, chacune avec un profil différent est ce qu'on appelle la surcharge (ici on peut éventuellement considérer qu'on est également dans un cas de redéfinition du constructeur par défaut, encore que sa définition ne soit en fait que masquée) :


   --> cat Construct3.java
   class X1 {
      int a, b;
      X1( ){ }   // constructeur vide ==> a = 0 et b = 0
      X1(int a, int b){
         this.a = a;
         this.b = b;
      }
   }
   class X2 extends X1{
      int c;
      X1 x1;
      X2(int c) {this.c = c; }
      }
   class Construct3 {
      public static void main(String[ ] arg){
      X1 x1 = new X1(10, 20);  // constructeur spécifique X1(int,int)
      System.out.println(x1.a + " " + x1.b);
      X2 x2 = new X2(30);   // constructeur spécifique X2(int)
      System.out.println(x2.a + " " + x2.b + " " + x2.c + " " + x2.x1);    
      }
   }                    
   --> javac Construct3.java   
   --> java Construct3
   10 20
   0 0 30 null     // le constructeur X1() a été appelé
   -->


Dans la dernière variation, le constructeur de la classe X2 qui y est défini reçoît en paramètres trois entiers :

   --> cat Construct4.java
   class X1 {
      int a, b;
      X1(int a, int b){
         this.a = a;
         this.b = b;
      }
   }
   class X2 extends X1{
      int c;
      X1 x1;
      X2(int a, int b, int c) {
            super(a,b); this.c = c; x1 = new X1(a+1,b+1); }    
      }
   class Construct4 {
      public static void main(String[ ] arg){
      X2 x2 = new X2(10, 20, 30); // constructeur spécifique X2(int,int,int)   
      System.out.println(x2.a + " " + x2.b + " " + x2.c + " " + x2.x1);   
      if (x2.x1 != null)
           System.out.println(x2.x1.a + " " + x2.x1.b);
      }
   }
   --> javac Construct4.java             
   --> java Construct4
   10 20 30 X1@ea2dfe
   11 21
   -->


On notera au passage la forme sous laquelle l'affichage de la valeur de la variable x1 est réalisée : il s'agit d'une forme «brute de fonderie» (X1@ea2dfe) contenant la classe de l'objet référencé et sa référence : toutes les variables de type référence ont des valeurs de cette forme correspondant à la définition de la méthode d'instance toString dans la classe Object, classe dont toutes les classes descendent plus ou moins directement.
Un affichage non générique reflétant la structure d'une classe particulière suppose de redéfinir effectivement la méthode publique toString pour cette classe concernée.
Pour la classe X1 une redéfinition possible de la méthode toString serait la suivante qui visualise entre crochets les valeurs des deux variables a et b de l'objet concerné :


   class X1 {
      int a, b;
        .......
      String toString(){
         return "[" + a + "," + b +"]";    
      } 
        ........ 
   }    


l'exécution donnant alors les résultats suivants :


   --> java Construct4    
   10 20 30 [11,21]    
   11 21
   -->


Dernière mise à jour : 18 juin 2005

Valid XHTML 1.0! Valid CSS!