1. Inicializando variáveis
Como você já sabe, você pode declarar diversas variáveis em sua classe, e não apenas declará-las, mas também inicializá-las imediatamente com seus valores iniciais.
E essas mesmas variáveis também podem ser inicializadas em um construtor. Isso significa que, em teoria, essas variáveis podem receber valores duas vezes. Exemplo
Código | Observação |
---|---|
|
A age variável recebe um valor inicial O valor inicial é substituído A variável idade armazena seu valor inicial. |
|
Isso é permitido: o primeiro construtor será chamado |
|
Isso é permitido: o segundo construtor será chamado |
Isto é o que acontece quando Cat cat = new Cat("Whiskers", 2);
é executado:
- Um
Cat
objeto é criado - Todas as variáveis de instância são inicializadas com seus valores iniciais
- O construtor é chamado e seu código é executado.
Ou seja, primeiro as variáveis recebem seus valores iniciais, e só então o código do construtor é executado.
2. Ordem de inicialização das variáveis em uma classe
As variáveis não são apenas inicializadas antes da execução do construtor — elas são inicializadas em uma ordem bem definida: a ordem em que são declaradas na classe.
Vejamos um código interessante:
Código | Observação |
---|---|
|
Este código não irá compilar, pois no momento em que a a
variável é criada ainda não existem variáveis b
e c
. Mas você pode escrever seu código da seguinte maneira - esse código será compilado e executado perfeitamente.
Código | Observação |
---|---|
|
0 0+2 0+2+3 |
Mas lembre-se que seu código deve ser transparente para outros desenvolvedores. É melhor não usar técnicas como essa, pois prejudica a legibilidade do código.
Aqui devemos lembrar que, antes que as variáveis recebam um valor, elas têm um valor padrão . Para o int
tipo, isso é zero.
Quando a JVM inicializar a a
variável, ela simplesmente atribuirá o valor padrão para o tipo int: 0.
Quando atingir b
, a variável a já será conhecida e terá um valor, então a JVM atribuirá a ela o valor 2.
E quando atingir a c
variável, as variáveis a
e b
já estarão inicializadas, então a JVM calculará facilmente o valor inicial para c
: 0+2+3.
Se você criar uma variável dentro de um método, não poderá usá-la, a menos que tenha atribuído um valor a ela anteriormente. Mas isso não é verdade para as variáveis de uma classe! Se um valor inicial não for atribuído a uma variável de uma classe, será atribuído um valor padrão a ela.
3. Constantes
Enquanto estamos analisando como os objetos são criados, vale a pena tocar na inicialização de constantes, ou seja, variáveis com o final
modificador.
Se uma variável tiver o final
modificador, deve ser atribuído um valor inicial. Você já sabe disso e não há nada de surpreendente nisso.
Mas o que você não sabe é que não precisa atribuir o valor inicial imediatamente se atribuí-lo no construtor. Isso funcionará muito bem para uma variável final. O único requisito é que, se você tiver vários construtores, uma variável final deve receber um valor em cada construtor.
Exemplo:
public class Cat
{
public final int maxAge = 25;
public final int maxWeight;
public Cat (int weight)
{
this.maxWeight = weight; // Assign an initial value to the constant
}
}
4. Código em um construtor
E mais algumas notas importantes sobre construtores. Mais tarde, ao continuar a aprender Java, você encontrará coisas como herança, serialização, exceções, etc. Todos eles influenciam o trabalho dos construtores em graus variados. Não faz sentido nos aprofundarmos nesses temas agora, mas somos obrigados a pelo menos abordá-los.
Por exemplo, aqui está uma observação importante sobre construtores. Em teoria, você pode escrever código de qualquer complexidade em um construtor. Mas não faça isso. Exemplo:
|
Abra um fluxo de leitura de arquivo Lê o arquivo em uma matriz de bytes Salve a matriz de bytes como uma string Exiba o conteúdo do arquivo na tela |
No construtor da classe FilePrinter, abrimos imediatamente um fluxo de bytes em um arquivo e lemos seu conteúdo. Este é um comportamento complexo e pode resultar em erros.
E se não houvesse tal arquivo? E se houver problemas com a leitura do arquivo? E se fosse muito grande?
Lógica complexa implica em alta probabilidade de erros e isso significa que o código deve tratar as exceções corretamente.
Exemplo 1 — Serialização
Em um programa Java padrão, há muitas situações em que não é você quem cria os objetos de sua classe. Por exemplo, suponha que você decida enviar um objeto pela rede: nesse caso, a própria máquina Java irá converter seu objeto em um conjunto de bytes, enviá-lo e recriar o objeto a partir do conjunto de bytes.
Mas suponha que seu arquivo não exista no outro computador. Haverá um erro no construtor e ninguém o tratará. E isso é capaz de fazer com que o programa seja encerrado.
Exemplo 2 — Inicializando campos de uma classe
Se o construtor de sua classe pode lançar exceções verificadas, ou seja, está marcado com a palavra-chave throws, então você deve capturar as exceções indicadas no método que cria seu objeto.
Mas e se não houver tal método? Exemplo:
Código | Observação |
---|---|
|
Este código não irá compilar. |
O FilePrinter
construtor de classe pode lançar uma exceção verificada , o que significa que você não pode criar um FilePrinter
objeto sem envolvê-lo em um bloco try-catch. E um bloco try-catch só pode ser escrito em um método
5. Construtor de classe base
Nas lições anteriores, discutimos um pouco sobre herança. Infelizmente, nossa discussão completa sobre herança e OOP está reservada para o nível dedicado a OOP, e a herança de construtores já é relevante para nós.
Se sua classe herdar outra classe, um objeto da classe pai será inserido dentro de um objeto de sua classe. Além do mais, a classe pai tem suas próprias variáveis e seus próprios construtores.
Isso significa que é muito importante para você saber e entender como as variáveis são inicializadas e os construtores são chamados quando sua classe tem uma classe pai e você herda suas variáveis e métodos.
Aulas
Como sabemos a ordem em que as variáveis são inicializadas e os construtores são chamados? Vamos começar escrevendo o código para duas classes. Um herdará o outro:
Código | Observação |
---|---|
|
A ChildClass classe herda a ParentClass classe. |
Precisamos determinar a ordem na qual as variáveis são inicializadas e os construtores são chamados. O registro nos ajudará a fazer isso.
Exploração madeireira
Logging é o processo de gravação de ações executadas por um programa enquanto ele é executado, gravando-as no console ou em um arquivo.
É bastante simples determinar que o construtor foi chamado: no corpo do construtor, escreva uma mensagem para o console. Mas como você pode saber se uma variável foi inicializada?
Na verdade, isso também não é muito difícil: escreva um método especial que retorne o valor usado para inicializar a variável e registre a inicialização. Isto é o que o código pode parecer:
código final
|
Criar um ChildClass objeto Este método grava o texto passado no console e também o retorna. Declare a ParentClass classe Display text e também inicialize as variáveis com ela. Escreva uma mensagem informando que o construtor foi chamado. Ignore o valor de retorno. Declare a ChildClass classe Display text e também inicialize as variáveis com ela. Escreva uma mensagem informando que o construtor foi chamado. Ignore o valor de retorno. |
Se você executar este código, o texto será exibido na tela da seguinte forma:
Saída do console do métodoMain.print() |
---|
|
Portanto, você sempre pode garantir pessoalmente que as variáveis de uma classe sejam inicializadas antes que o construtor seja chamado. Uma classe base é totalmente inicializada antes da inicialização da classe herdada.
GO TO FULL VERSION