Classes e objetos em Java devem ser inicializados antes de serem usados. Você aprendeu anteriormente que os campos de classe são inicializados com valores padrão quando as classes são carregadas e que os objetos são inicializados por meio de construtores — mas ainda há mais na inicialização. Este tutorial apresenta todos os recursos do Java para inicializar classes e objetos.

O que você aprenderá neste tutorial Java

  • Como inicializar uma classe Java
  • Como trabalhar com blocos de inicialização de classe
  • Como inicializar objetos Java
download

Baixe o código-fonte de aplicativos de exemplo neste tutorial. Criado por Jeff Friesen para JavaWorld.

Como inicializar uma classe Java

Antes de explorarmos o suporte do Java para inicialização de classes, vamos recapitular as etapas de inicialização de uma classe Java. Considere a Listagem 1.

Listagem 1. Inicializando campos de classe com valores padrão

class SomeClass
{
   static boolean b;
   static byte by;
   static char c;
   static double d;
   static float f;
   static int i;
   static long l;
   static short s;
   static String st;
}

A Listagem 1 declara a classe SomeClass. Esta classe declara nove campos de tipos boolean, byte, char, double, float, int, long, shorte String. Quando SomeClass é carregado, os bits de cada campo são definidos como zero, o que você interpreta da seguinte forma:

false
0
u0000
0.0
0.0
0
0
0
null

Os campos da classe anterior foram inicializados implicitamente em zero. No entanto, você também pode inicializar explicitamente campos de classe atribuindo valores diretamente a eles, conforme mostrado na Listagem 2.

Listagem 2. Inicializando campos de classe para valores explícitos

class SomeClass
{
   static boolean b = true;
   static byte by = 1;
   static char c="A";
   static double d = 2.0;
   static float f = 3.0f;
   static int i = 4;
   static long l = 5000000000L;
   static short s = 20000;
   static String st = "abc";
}

O valor de cada atribuição deve ser compatível com o tipo do campo de classe. Cada variável armazena o valor diretamente, com exceção de st. Variável st armazena uma referência a um String objeto que contém abc.

Referenciando campos de classe

Ao inicializar um campo de classe, é legal inicializá-lo com o valor de um campo de classe inicializado anteriormente. Por exemplo, a Listagem 3 inicializa y para xo valor. Ambos os campos são inicializados para 2.

Listagem 3. Referenciando um campo declarado anteriormente

class SomeClass
{
   static int x = 2;
   static int y = x;

   public static void main(String() args)
   {
      System.out.println(x);
      System.out.println(y);
   }
}

Entretanto, o inverso não é legal: você não pode inicializar um campo de classe com o valor de um campo de classe declarado posteriormente. As saídas do compilador Java illegal forward reference quando se depara com esse cenário. Considere a Listagem 4.

Listagem 4. Tentativa de fazer referência a um campo declarado posteriormente

class SomeClass
{
   static int x = y;
   static int y = 2;

   public static void main(String() args)
   {
      System.out.println(x);
      System.out.println(y);
   }
}

O compilador reportará illegal forward reference quando ele se depara static int x = y;. Isso ocorre porque o código-fonte é compilado de cima para baixo e o compilador ainda não viu y. (Também geraria esta mensagem se y não foi explicitamente inicializado.)

Como trabalhar com blocos de inicialização de classe

Em alguns casos, você pode querer executar inicializações complexas baseadas em classes. Você fará isso depois que uma classe for carregada e antes que qualquer objeto seja criado a partir dessa classe (assumindo que a classe não seja uma classe utilitária). Você pode usar um bloco de inicialização de classe para esta tarefa.

A bloco de inicialização de classe é um bloco de declarações precedido pelo static palavra-chave introduzida no corpo da classe. Quando a classe é carregada, essas instruções são executadas. Considere a Listagem 5.

Listagem 5. Inicializando matrizes de valores de seno e cosseno

class Graphics
{
   static double() sines, cosines;
   static
   {
      sines = new double(360);
      cosines = new double(360);
      for (int i = 0; i < sines.length; i++)
      {
         sines(i) = Math.sin(Math.toRadians(i));
         cosines(i) = Math.cos(Math.toRadians(i));
      }
   }
}

A Listagem 5 declara um Graphics classe que declara sines e cosines variáveis ​​de matriz. Ele também declara um bloco de inicialização de classe que cria arrays de 360 ​​elementos cujas referências são atribuídas a sines e cosines. Em seguida, ele usa um for instrução para inicializar esses elementos da matriz com os valores apropriados de seno e cosseno, chamando o método Math aula sin() e cos() métodos. (Math faz parte da biblioteca de classes padrão do Java. Discutirei essa classe e esses métodos em um artigo futuro.)

Combinando inicializadores de campo de classe e blocos de inicialização de classe

Você pode combinar vários inicializadores de campo de classe e blocos de inicialização de classe em um aplicativo. A Listagem 6 fornece um exemplo.

Listagem 6. Executando a inicialização da classe em ordem descendente

class MCFICIB
{
   static int x = 10;

   static double temp = 98.6;

   static
   {
      System.out.println("x = " + x);
      temp = (temp - 32) * 5.0/9.0; // convert to Celsius
      System.out.println("temp = " + temp);
   }

   static int y = x + 5;

   static
   {
      System.out.println("y = " + y);
   }

   public static void main(String() args)
   {
   }
}

A Listagem 6 declara e inicializa um par de campos de classe (x e y) e declara um par de static inicializadores. Compile esta listagem conforme mostrado:

javac MCFICIB.java

Em seguida, execute o aplicativo resultante:

java MCFICIB

Você deve observar a seguinte saída:

x = 10
temp = 37.0
y = 15

Esta saída revela que a inicialização da classe é executada de cima para baixo.

() métodos

Ao compilar inicializadores de classe e blocos de inicialização de classe, o compilador Java armazena o bytecode compilado (em ordem descendente) em um método especial denominado <clinit>(). Os colchetes angulares evitam conflito de nomes: você não pode declarar um <clinit>() método no código-fonte porque o < e > caracteres são ilegais em um contexto de identificador.

Depois de carregar uma classe, a JVM chama esse método antes de chamar main() (quando main() é presente).

Vamos dar uma olhada por dentro MCFICIB.class. A seguinte desmontagem parcial revela as informações armazenadas para o x, tempe y Campos:

Field #1

00000290        Access Flags                          ACC_STATIC
00000292        Name                                  x
00000294        Descriptor                            I
00000296        Attributes Count                      0

Field #2

00000298        Access Flags                          ACC_STATIC
0000029a        Name                                  temp
0000029c        Descriptor                            D
0000029e        Attributes Count                      0

Field #3

000002a0        Access Flags                          ACC_STATIC
000002a2        Name                                  y
000002a4        Descriptor                            I
000002a6        Attributes Count                      0

O Descriptor linha identifica a JVM descritor de tipo para o campo. O tipo é representado por uma única letra: I para int e D para double.

A seguinte desmontagem parcial revela a sequência de instruções de bytecode para o <clinit>() método. Cada linha começa com um número decimal que identifica o endereço de deslocamento baseado em zero da instrução subsequente:

  0        bipush 10
  2        putstatic MCFICIB/x I
  5        ldc2_w #98.6
  8        putstatic MCFICIB/temp D
 11        getstatic java/lang/System/out Ljava/io/PrintStream;
 14        new java/lang/StringBuilder
 17        dup
 18        invokespecial java/lang/StringBuilder/<init>()V
 21        ldc "x = "
 23        invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
 26        getstatic MCFICIB/x I
 29        invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
 32        invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
 35        invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
 38        getstatic MCFICIB/temp D
 41        ldc2_w #32
 44        dsub
 45        ldc2_w #5
 48        dmul
 49        ldc2_w #9
 52        ddiv
 53        putstatic MCFICIB/temp D
 56        getstatic java/lang/System/out Ljava/io/PrintStream;
 59        new java/lang/StringBuilder
 62        dup
 63        invokespecial java/lang/StringBuilder/<init>()V
 66        ldc "temp = "
 68        invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
 71        getstatic MCFICIB/temp D
 74        invokevirtual java/lang/StringBuilder/append(D)Ljava/lang/StringBuilder;
 77        invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
 80        invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
 83        getstatic MCFICIB/x I
 86        iconst_5
 87        iadd
 88        putstatic MCFICIB/y I
 91        getstatic java/lang/System/out Ljava/io/PrintStream;
 94        new java/lang/StringBuilder
 97        dup
 98        invokespecial java/lang/StringBuilder/<init>()V
101        ldc "y = "
103        invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
106        getstatic MCFICIB/y I
109        invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
112        invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
115        invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
118        return

A sequência de instruções do deslocamento 0 ao deslocamento 2 é equivalente ao seguinte inicializador de campo de classe:

static int x = 10;

A sequência de instruções do deslocamento 5 ao deslocamento 8 é equivalente ao seguinte inicializador de campo de classe:

static double temp = 98.6;

A sequência de instruções do deslocamento 11 ao deslocamento 80 é equivalente ao seguinte bloco de inicialização de classe:

static
{
   System.out.println("x = " + x);
   temp = (temp - 32) * 5.0/9.0; // convert to Celsius
   System.out.println("temp = " + temp);
}

A sequência de instruções do deslocamento 83 ao deslocamento 88 é equivalente ao seguinte inicializador de campo de classe:

static int y = x + 5;

A sequência de instruções do deslocamento 91 ao deslocamento 115 é equivalente ao seguinte bloco de inicialização de classe:

static
{
   System.out.println("y = " + y);
}

finalmente, o return instrução no deslocamento 118 retorna a execução de <clinit>() para aquela parte da JVM que chamou esse método.

Como inicializar objetos Java

Após uma classe ter sido carregada e inicializada, muitas vezes você desejará criar objetos a partir da classe. Como você aprendeu em minha introdução recente à programação com classes e objetos, você inicializa um objeto por meio do código colocado no construtor de uma classe. Considere a Listagem 7.