Creazione di oggetti (ripasso)

Il processo di creazione di un oggetto consiste in
  • Allocare lo spazio di memoria necessario a contenere l'oggetto.
  • Inizializzare le variabili di istanza dell'oggetto.
La quantità di memoria necessaria per contenere un oggetto è determinata univocamente dalla dichiarazione della classe (più precisamente, dal numero e dal tipo delle variabili di istanza).
[Naturalmente lo stesso vale anche per gli array].

Lo spazio necessario viene allocato in una zona di memoria chiamato heap.

Come sappiamo, un oggetto viene creato con la primitiva new:

new <nomeClasse>(<parametri>)
Ad esempio:
new BankAccount(1000) // crea un nuovo conto con saldo 1000
La primitiva new fa concettualmente tre cose:
  • Crea l'oggetto allocando la memoria necessaria;
  • Invoca sull'oggetto un costruttore della classe <nomeClasse>, passandogli i <parametri>;
  • restituisce un puntatore all'oggetto creato.
Vediamo più in dettaglio cosa sono i costruttori.



Costruttori

I costruttori si dichiarano all'interno di una classe essenzialmente come i metodi, ma rispettando le seguenti regole:
  • il nome del costruttore deve coincidere con quello della classe;
  • un costruttore non può avere un tipo (nemmeno void)

  • (concettualmente, il costruttore restituisce sempre l'oggetto appena creato);
  • non può avere static come modificatore.
Come per i metodi, anche ai costruttori si applica l'overloading: una classe può avere più costruttori purché abbiano firma diversa (cioè i parametri formali devono differire nel tipo e/o nel numero).
 
 
Esempio: costruttori della classe BankAccount
public BankAccount (int initialBalance)
public BankAccount ()

Se in una classe è definito almeno un costruttore, l'operatore new  chiama sull'oggetto appena creato il costruttore determinato dalla lista dei parametri attuali (se non esiste, il compilatore segnala un errore del tipo "cannot resolve symbol").
 




Il costruttore default

Ogni classe ha un costruttore default che inizializza le variabili d'istanza con il corrispondente valore default. Questo costruttore non ha parametri, ed è disponibile solo se nella classe non è definito nessun costruttore.
 
 
Uso corretto del costruttore default
class Punto{
   public double x, y;
   <metodi>
}
Punto p = new Punto();
// Corretto. Avremo:
// p.x == 0; p.y == 0;
Uso errato del costruttore default
class Punto{
  public double x, y;
  public Punto(double vx, double vy)
     { x = vx;
       y = vy;
     }
  <metodi>
}
Punto p = new Punto();
// Errore: costruttore default
// non più disponibile.

Punto p = new Punto(4.3, 3.7);
// ok
 

In questo caso, se il programmatore vuole continuare a rendere disponibile un costruttore senza parametri, deve dichiararlo esplicitamente (come abbiamo fatto nella classe BankAccount).




this...

Nel corpo di un metodo o di un costruttore, la variabile predefinita this denota l'oggetto che sta eseguendo il medodo (o costruttore).

In generale non si usa this, perché si può fare riferimento a variabili e metodi d'istanza direttamente con il loro nome.
 

Senza this
Con this (sintassi più pesante)
public class Punto{
   double x, y;
   public String toString(){
      return "(" + x + "," + y + ")";
   }
   public void print(){
      System.out.println(toString());
   }
}
public class Punto{
   double x, y;
   public String toString(){
      return "("+ this.x +","+ this.y +")";
   }
   public void print(){
      System.out.println(this.toString());
   }
}

Ma in alcuni casi può essere utile, ad esempio per usare come parametri di un costruttore gli stessi nomi delle variabili d'istanza:
 

Senza this
Con this (costruttore più chiaro)
public class Punto{
   double x, y;
   public Punto(double vx, double vy)
      {x = vx;
       y = vy;
      }
   <metodi>
}
public class Punto{
   double x, y;
   public Punto(double x, double y)
      {this.x = x;
       this.y = y;
      }
   <metodi>
}



... e this()

L'identificatore this può essere usato anche come nome di un metodo. In questo caso invoca un costruttore della classe, e può essere invocato solo come prima istruzione di un altro costruttore.
 
Senza this()
Con this() (meno ridondante)
public class Punto{
  double x, y;
  public Punto(){
     x=0;
     y=0;
     System.out.println("ho costruito" +
     " un punto di coordinate " +
     x + "," + y);
  }

  public Punto(double x,double y){
     this.x=x;
     this.y=y;
     System.out.println("ho costruito" + 
     " un punto di coordinate " +
     x + "," + y);
  }
  <metodi>
}

public class Punto{
  double x, y;
  public Punto(){
     this(0,0);
  }

  public Punto(double x,double y){
     this.x=x;
     this.y=y;
     System.out.println("ho costruito" +
     " un punto di coordinate " +
     x + "," + y);
  }
  <metodi>
}




Inizializzazione di variabili locali

Ci sono due tipi di variabili in Java, che sono trattate in modo diverso:
  • variabili dichiarate localmente nei metodi (o costruttori)
  • variabili dichiarate in una classe (all'esterno dei metodi), cioè le variabili statiche o di istanza


Variabili locali dei metodi
  • devono essere inizializzate esplicitamente, altrimenti il compilatore segnala un errore.
Programma non corretto: num non inizializzata Errore segnalato dal compilatore
public class Tabellina{
 public static void main(String [] args){
  ConsoleReader console =
       new ConsoleReader(System.in);
  int num;  // int num = 0
  while (num <= 0){
  System.out.print("Dammi un numero >0: ");
  num = console.readInt();
  }
  for (int i=1; i<=10; i++)
        System.out.println(i * num);
 }
}
> javac Tabellina.java
Tabellina.java:5: variable num might not have been initialized
        while (num < 0){
               ^
1 error



Iniz. di variabili statiche e di istanza

Variabili statiche e di istanza
  • vengono sempre inizializzate, anche se non viene fatto esplicitamente. I valori default sono 0 per variabili numeriche, false per boolean, null per variabili oggetto (inlcusi array, stringhe,...)
Ci sono tre modi per inizializzare una variabile d'istanza o statica:
  • Con assegnamento esplicito all'interno di un costruttore;
  • Con inizializzazione esplicita nella dichiarazione;
  • Con inizializzazione default.
Classe che usa i tre modi di inizializzare
Inizializzazione
public class Punto3d{
   double x;
   double y = 3.0;
   double z;
   public Punto3d(double z){
      this.z = z;
   }
   <metodi>
}
Se eseguiamo
...
Punto3d p = new Punto3d(5.0);
...

otteniamo:
p.x == 0.0;
p.y == 3.0;
p.z == 5.0.