

Animal
classe:
public class Animal {
String name;
int age;
}
Podemos declarar 2 classes filhas: Cat
e Dog
. Isso é feito usando a palavra-chave extends .
public class Cat extends Animal {
}
public class Dog extends Animal {
}
Podemos achar isso útil no futuro. Por exemplo, se houver uma tarefa para pegar ratos, criaremos um Cat
objeto em nosso programa. Se a tarefa for correr atrás de um pedaço de pau, usaremos um Dog
objeto. E se criarmos um programa que simule uma clínica veterinária, ele funcionará com a Animal
turma (e assim poderá atender tanto cães quanto gatos). É muito importante lembrar que quando um objeto é criado, primeiro é chamado o construtor de sua classe base . Somente após o término desse construtor é que o programa executa o construtor da classe correspondente ao objeto que estamos criando. Em outras palavras, ao criar um Cat
objeto, o Animal
construtor é executado primeiro e só depois é executado oCat
construtor executado . Para ver isso, adicione alguma saída do console aos construtores Cat
e Animal
.
public class Animal {
public Animal() {
System.out.println("Animal constructor executed");
}
}
public class Cat extends Animal {
public Cat() {
System.out.println("Cat constructor executed!");
}
public static void main(String[] args) {
Cat cat = new Cat();
}
}
Saída do console: Construtor animal executado Construtor gato executado! De fato, funciona assim! Por que? Um motivo é evitar a duplicação de campos compartilhados entre as duas classes. Por exemplo, todo animal tem coração e cérebro, mas nem todo animal tem rabo. Poderíamos declarar os campos do cérebro e do coração , que são comuns a todos os animais, na Animal
classe pai, e um campo de cauda na Cat
subclasse. . Agora vamos declarar um Cat
construtor de classe que recebe argumentos para todos os 3 campos.
public class Cat extends Animal {
String tail;
public Cat(String brain, String heart, String tail) {
this.brain = brain;
this.heart = heart;
this.tail = tail;
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
Observação: o construtor funciona corretamente mesmo que a Cat
classe não tenha campos cerebrais e cardíacos . Esses campos são "herdados" da Animal
classe base. A classe herdeira tem acesso aos campos da classe base , portanto, eles ficam visíveis em nossa Cat
classe. Como resultado, não precisamos duplicar esses campos na Cat
classe. Podemos retirá-los da Animal
classe. Além disso, podemos chamar explicitamente o construtor da classe base no construtor da classe filha. Uma classe base também é chamada de " superclasse ". É por isso que Java usa a palavra-chave super para indicar a classe base. No exemplo anterior
public Cat(String brain, String heart, String tail) {
this.brain = brain;
this.heart = heart;
this.tail = tail;
}
Atribuímos separadamente cada campo em nossa classe pai. Na verdade, não precisamos fazer isso. Basta chamar o construtor da classe pai e passar os argumentos necessários:
public class Animal {
String brain;
String heart;
public Animal(String brain, String heart) {
this.brain = brain;
this.heart = heart;
}
public class Cat extends Animal {
String tail;
public Cat(String brain, String heart, String tail) {
super(brain, heart);
this.tail = tail;
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
No Cat
construtor, chamamos o Animal
construtor e passamos dois campos. Tínhamos apenas um campo para inicializar explicitamente: tail , que não está em Animal
. Lembra que mencionamos que o construtor da classe pai é chamado primeiro quando um objeto é criado? É por isso que super() deve sempre ser o primeiro em um construtor! Caso contrário, a lógica do construtor será violada e o programa gerará um erro.
public class Cat extends Animal {
String tail;
public Cat(String brain, String heart, String tail) {
this.tail = tail;
super(brain, heart);// Error!
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
O compilador sabe que quando um objeto de uma classe filha é criado, o construtor da classe base é chamado primeiro. E se você tentar alterar manualmente esse comportamento, o compilador não permitirá.
Como um objeto é criado
Anteriormente, vimos um exemplo com uma classe base e pai:Animal
e Cat
. Usando essas duas classes como exemplos, veremos agora o processo de criação de um objeto e inicialização de variáveis. Sabemos que existem variáveis estáticas e de instância (não estáticas) . Também sabemos que a Animal
classe base possui variáveis e a Cat
classe filha possui suas próprias. Para maior clareza, adicionaremos uma variável estática a cada uma das classes Animal
e Cat
. A variável animalCount na Animal
classe representará o número total de espécies animais na Terra, e a variável catCountvariável significará o número de espécies de gatos. Além disso, atribuiremos valores iniciais a todas as variáveis não estáticas em ambas as classes (que serão alteradas no construtor).
public class Animal {
String brain = "Initial value of brain in the Animal class";
String heart = "Initial value of heart in the Animal class";
public static int animalCount = 7700000;
public Animal(String brain, String heart) {
System.out.println("Animal base class constructor is running");
System.out.println("Have the variables of the Animal class already been initialized?");
System.out.println("Current value of static variable animalCount = " + animalCount);
System.out.println("Current value of brain in the Animal class = " + this.brain);
System.out.println("Current value of heart in the Animal class = " + this.heart);
System.out.println("Have the variables of the Cat class already been initialized?");
System.out.println("Current value of static variable catCount = " + Cat.catCount);
this.brain = brain;
this.heart = heart;
System.out.println("Animal base class constructor is done!");
System.out.println("Current value of brain = " + this.brain);
System.out.println("Current value of heart = " + this.heart);
}
}
public class Cat extends Animal {
String tail = "Initial value of tail in the Cat class";
static int catCount = 37;
public Cat(String brain, String heart, String tail) {
super(brain, heart);
System.out.println("The cat class constructor has started (The Animal constructor already finished)");
System.out.println("Current value of static variable catCount = " + catCount);
System.out.println("Current value of tail = " + this.tail);
this.tail = tail;
System.out.println("Current value of tail = " + this.tail);
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
Portanto, estamos criando uma nova instância da Cat
classe, que herda Animal
. Adicionamos algumas saídas de console detalhadas para ver o que está acontecendo e em que ordem. Isso é o que será exibido quando um Cat
objeto for criado: O construtor da classe base Animal está em execução As variáveis da classe Animal já foram inicializadas? Valor atual da variável estática animalCount = 7700000 Valor atual do cérebro na classe Animal = Valor inicial do cérebro na classe Animal Valor atual do coração na classe Animal = Valor inicial do coração na classe Animal Já tem as variáveis da classe Gato foi inicializado? Valor atual da variável estática catCount = 37 O construtor da classe base Animal está pronto! Valor atual de brain = Brain Valor atual heart = Heart O construtor da classe cat foi iniciado (o construtor Animal já terminou) Valor atual da variável estática catCount = 37 Valor atual de tail = Valor inicial de tail na classe Cat Valor atual de tail = Cauda Então, agora podemos ver claramente a ordem de inicialização de variáveis e chamadas de construtor quando um novo objeto é criado:
- As variáveis estáticas da classe base (
Animal
) são inicializadas. Em nosso caso, a variável animalCountAnimal
da classe é definida como 7700000. -
As variáveis estáticas da classe filha (
Cat
) são inicializadas.Obs: ainda estamos dentro do
Animal
construtor e já exibimos:O construtor da classe base Animal está em execução
As variáveis da classe Animal já foram inicializadas?
Valor atual da variável estática animalCount = 7700000
Valor atual do cérebro na classe Animal = Valor inicial do cérebro na classe Animal
Valor atual do coração na classe Animal = Valor inicial do coração na classe Animal
Já tem as variáveis da classe Gato foi inicializado?
Valor atual da variável estática catCount = 37 -
Em seguida, as variáveis não estáticas da classe base são inicializadas. Atribuímos especificamente valores iniciais a eles, que são então substituídos no construtor. O construtor Animal ainda não terminou, mas os valores iniciais de brain e heart já foram atribuídos:
O construtor da classe base Animal está em execução
As variáveis da classe Animal já foram inicializadas?
Valor atual da variável estática animalCount = 7700000
Valor atual do cérebro na classe Animal = Valor inicial do cérebro na classe Animal
Valor atual do coração na classe Animal = Valor inicial do coração na classe Animal -
O construtor da classe base é iniciado.
Já nos convencemos de que esta etapa é a quarta: nas três primeiras etapas do início doAnimal
construtor, muitas variáveis já receberam valores. -
Os campos não estáticos da classe filha (
Cat
) são inicializados.
Isso acontece antes que oCat
construtor comece a executar.
Quando começa a rodar, a variável tail já tem um valor:O construtor da classe cat foi iniciado (o construtor Animal já terminou) Valor atual da variável estática catCount = 37 Valor atual de tail = Valor inicial de tail na classe Cat
-
O construtor da
Cat
classe filha é chamadoE é assim que a criação de um objeto se parece em Java!
Devo dizer que não somos grandes fãs de aprendizado mecânico, mas é melhor memorizar a ordem de inicialização de variáveis e chamadas de construtor .
Isso aumentará muito sua compreensão do fluxo do programa e do estado de seus objetos em qualquer momento específico.
Além disso, muitas classes não usam herança. Nesse caso, as etapas relacionadas à classe base não se aplicam.
Mais leitura: |
---|
GO TO FULL VERSION