Construção, Iniciação e Destruição de Objectos/Construção de Objectos em Java

From Wiki**3

< Construção, Iniciação e Destruição de Objectos

A construção de objectos em Java segue a modalidade genérica da iniciação de objectos, i.e., primeiro iniciam-se os sub-objectos correspondentes a definições da superclasse e só depois os próprios. Este processo é recursivo, o que significa que a superclasse mais antiga na hierarquia é a que inicia primeiro, sendo seguida pela classe logo abaixo, etc., até chegar à classe cuja instância está a ser criada.

Como já se referiu, a linguagem Java procura esconder do programador pormenores como as especificadades de reserva de memória. No entanto, o programador ainda fica com algumas responsabilidades, nomeadamente na definição e chamadas dos construtores. Esta secção apresenta então os seguintes aspectos relacionados com a construção e iniciação de objectos em Java:

  • Definição de construtores e ordem de invocação;
  • Ordem de iniciação de atributos de classes (static) e de objectos.

Definição de Construtores

Usos Especiais de "this" e "super"

As palavras chave this e super, além das utilizações como auto-referência, permitem a chamada explícita a construtores.

"this"

Num construtor, esta palavra reservada pode ser utilizada para chamar explicitamente outro construtor da mesma classe. Quando é utilizado desta forma, this pode ocorrer apenas uma vez e deve aparecer como a primeira instrução do construtor.

 class A {
   A(int i) { /* qualquer coisa */ }
   A(float f, int i) {
     this(i);               // chamada ao outro construtor
     /* resto do segundo construtor */
   }
 }

"super"

Num construtor, esta palavra reservada pode ser utilizada para chamar explicitamente outro construtor de uma superclasse. Quando é utilizado desta forma, super pode ocorrer apenas uma vez e deve aparecer como a primeira instrução do construtor.

Definição de uma classe A:

  class A {
    A(int i) { /* qualquer coisa */ }
    A(String etiqueta, int i) {
      this(i);               // chamada ao outro construtor
      /* resto do segundo construtor */
    }
  }

Definição de uma classe B (subclasse de A):

  class B extends A {
    B() {
      this("etiqueta arbitrária", 123);  // chama outro construtor de B
      /* uma iniciação qualquer */
    }
    B(String etiqueta, int quantidade) {
      super(etiqueta, quantidade);  // chama um construtor de A
      /* resto do construtor */
    }
  }

Ordem de Iniciação de Atributos

Quando um objecto é criado, várias acções são realizadas em sequência, algumas das quais apenas uma vez durante a vida de uma aplicação.

As seguintes acções são executadas apenas uma vez:

  • Carregamento da classe pela máquina virtual;
  • Iniciação de variáveis "static";
  • Execução sequencial de blocos anónimos "static".

As seguintes acções são executadas cada vez que um objecto é criado:

  • Iniciação por omissão de atributos (não "static");
  • Execução sequencial de blocos anónimos (não "static");
  • Execução do constructor utilizado com o operador "new" (em classes não anónimas).

Os seguintes exemplos ilustram a ordem de iniciação.

Exemplo 1

Neste exemplo a classe Numero não é carregada (o método "main" de Order1 não a usa). Assim, não há produção de caracteres para a saída.

class Numero {
  int x;
  static int a = 7;
  {
    int b = 1;
    System.out.println("non-static block: a = " + a + " b = " + b);
  }
  static {
    int b = 4;
    System.out.println("static block: a = " + a + " b = " + b);
  }
  {
    int b = 3;
    System.out.println("non-static block: a = " + a + " b = " + b + " x = " + x);
  }
}
public class Order1 {
  public static void main(String[] args) {}
}
//output:

Exemplo 2

Neste exemplo, a classe Order2 utiliza um atributo "static" da class Numero, provocando o carregamento da classe e a iniciação e execução de componentes "static".

class Numero {
  int x;
  static int a = 7;
  {
    int b = 1;
    System.out.println("non-static block: a = " + a + " b = " + b);
  }
  static {
    int b = 4;
    System.out.println("static block: a = " + a + " b = " + b);
  }
  {
    int b = 3;
    System.out.println("non-static block: a = " + a + " b = " + b + " x = " + x);
  }
}
public class Order2 {
  public static void main(String[] args) {
    Numero.a = 2;
  }
}
//output:
//static block: a = 7 b = 4

Exemplo 3

Semelhante ao exemplo 2, mas utilizando o valor alterado.

class Numero {
  int x;
  static int a = 7;
  {
    int b = 1;
    System.out.println("non-static block: a = " + a + " b = " + b);
  }
  static {
    int b = 4;
    System.out.println("static block: a = " + a + " b = " + b);
  }
  {
    int b = 3;
    System.out.println("non-static block: a = " + a + " b = " + b + " x = " + x);
  }
}
public class Order3 {
  public static void main(String[] args) {
    Numero.a = 2;
    System.out.println("Numero.a = " + Numero.a);
  }
}
//output:
//static block: a = 7 b = 4
//Numero.a = 2

Exemplo 4

É criada uma instância da classe Numero. Todas as fases descritas nos exemplos anteriores são executadas. Depois, são executados os blocos anónimos e o contrutor.

class Numero {
  int x = 93;
  Numero() { System.out.println("Numero.Numero"); }
  static int a = 7;
  {
    int b = 1;
    System.out.println("non-static block: a = " + a + " b = " + b);
  }
  static {
    int b = 4;
    System.out.println("static block: a = " + a + " b = " + b);
  }
  {
    int b = 3;
    System.out.println("non-static block: a = " + a + " b = " + b + " x = " + x);
  }
}
public class Order4 {
  public static void main(String[] args) {
    Numero n = new Numero();
    System.out.println("Numero.a = " + Numero.a + " n.x = " + n.x);
  }
}
 //static block: a = 7 b = 4
 //non-static block: a = 7 b = 1
 //non-static block: a = 7 b = 3 x = 93
 //Numero.Numero
 //Numero.a = 7 n.x = 93