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
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
, short
e 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 x
o 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
, temp
e 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.