Animal
(animal):
public class Animal {
String name;
int age;
}
Podemos criar para ele, por exemplo, 2 classes descendentes - Cat
e Dog
. Isso é feito usando a palavra-chave extends
.
public class Cat extends Animal {
}
public class Dog extends Animal {
}
Isso pode ser útil para nós no futuro. Por exemplo, se a tarefa é capturar ratos, criaremos um objeto no programa Cat
. Se a tarefa for correr atrás de um stick, então usamos o object Dog
. E se criarmos um programa que simule uma clínica veterinária, ele vai funcionar com a turma Animal
(para poder tratar tanto gatos quanto cachorros). É muito importante lembrar para o futuro que ao criar um objeto, primeiro é chamado o construtor de sua classe base , e só então é chamado o construtor da própria classe, cujo objeto estamos criando. Ou seja, ao criar um objeto, Cat
o construtor da classe funcionará primeiro Animal
e só depois o construtor Cat
. Para ter certeza disso, vamos adicionar os construtores Cat
e Animal
enviar para o console.
public class Animal {
public Animal() {
System.out.println("Animal constructor completed");
}
}
public class Cat extends Animal {
public Cat() {
System.out.println("The constructor Cat worked!");
}
public static void main(String[] args) {
Cat cat = new Cat();
}
}
Saída do console:
Отработал конструктор Animal
Отработал конструктор Cat!
Na verdade, é assim que tudo funciona! Para que serve? Por exemplo, para evitar a duplicação de campos comuns de duas classes. Por exemplo, todo animal tem coração e cérebro, mas nem todo animal tem cauda. Podemos declarar campos comuns a todos os animais brain
na heart
classe pai Animal
e um campo tail
na subclasse Cat
. Agora criaremos um construtor para a classe Cat
, onde passaremos 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");
}
}
Prestar atenção:o construtor funciona com sucesso, embora a classe Cat
não possua campos brain
e heart
. Esses campos foram “puxados” da classe base Animal
. A classe descendente tem acesso aos campos da classe base, portanto Cat
eles ficam visíveis em nossa classe. Portanto, não precisamos Cat
duplicar esses campos na classe - podemos retirá-los da classe Animal
. Além disso, podemos chamar explicitamente o construtor da classe base no construtor da classe descendente. A classe base também é chamada de “ superclasse ”, e é por isso que Java usa a palavra-chave super
. 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 que está em nossa classe pai. Na verdade, você não precisa fazer isso. Basta chamar o construtor da classe pai e passar os parâmetros 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 construtor, Cat
chamamos o construtor Animal
e passamos dois campos para ele. Só precisamos inicializar explicitamente um campo - tail
que Animal
não está presente. Lembra como dissemos que quando um objeto é criado, o construtor da classe pai é chamado primeiro? Então é por isso que a palavra super()
deve sempre vir primeiro no construtor! Caso contrário, a lógica dos construtores será quebrada 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 ao criar um objeto de uma classe descendente, o construtor da classe base é chamado primeiro. E se você tentar alterar manualmente esse comportamento, isso não será permitido.
O processo de criação de um objeto.
Acima, vimos um exemplo com uma classe base e pai -Animal
e Cat
. Agora, usando essas duas classes como exemplo, veremos o processo de criação de um objeto e inicialização de variáveis. Sabemos que variáveis são variáveis estáticas e de instância (não estáticas). Também sabemos que a classe base possui suas próprias variáveis e a classe descendente possui as suas próprias. Vamos adicionar uma variável estática à classe para maior clareza . A variável de classe seria o número total de espécies animais na Terra, e a variável seria o número de espécies de gatos. Além disso, atribuiremos valores iniciais a todas as variáveis não estáticas de ambas as classes (que serão então alteradas no construtor). Animal
Cat
Animal
Cat
animalCount
Animal
catsCount
public class Animal {
String brain = "The initial value of brain in the Animal class";
String heart = "The initial value of heart in the Animal class";
public static int animalCount = 7700000;
public Animal(String brain, String heart) {
System.out.println("The constructor of the Animal base class is being executed");
System.out.println("Have the variables of the Animal class already been initialized?");
System.out.println("The current value of the static variable animalCount = " + animalCount);
System.out.println("Current value of brain in class Animal = " + this.brain);
System.out.println("Current value of heart in class Animal = " + this.heart);
System.out.println("Have the variables of the Cat class already been initialized?");
System.out.println("The current value of the static variable catsCount = " + Cat.catsCount);
this.brain = brain;
this.heart = heart;
System.out.println("Animal base class constructor completed!");
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 = "The initial value of tail in the Cat class";
static int catsCount = 37;
public Cat(String brain, String heart, String tail) {
super(brain, heart);
System.out.println("The constructor of the Cat class has started (the Animal constructor has already been executed)");
System.out.println("The current value of the static variable catsCount = " + catsCount);
System.out.println("Current value tail = " + this.tail);
this.tail = tail;
System.out.println("Current value tail = " + this.tail);
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
Então, criamos um novo objeto da classe Cat
herdada de Animal
. Vamos adicionar uma saída detalhada do console ao nosso programa para ver o que acontecerá e em que ordem. Isto é o que será enviado ao console como resultado da criação de um objeto Cat
:
Выполняется конструктор базового класса Animal
Были ли уже проинициализированы переменные класса Animal?
Текущее meaning статической переменной animalCount = 7700000
Текущее meaning brain в классе Animal = Изначальное meaning brain в классе Animal
Текущее meaning heart в классе Animal = Изначальное meaning heart в классе Animal
Были ли уже проинициализированы переменные класса Cat?
Текущее meaning статической переменной catsCount = 37
Конструктор базового класса Animal завершил работу!
Текущее meaning brain = Мозг
Текущее meaning heart = Сердце
Конструктор класса Cat начал работу (конструктор Animal уже был выполнен)
Текущее meaning статической переменной catsCount = 37
Текущее meaning tail = Изначальное meaning tail в классе Cat
Текущее meaning tail = Хвост
Então, agora podemos ver claramente em que ordem as variáveis são inicializadas e os construtores são chamados ao criar um novo objeto:
-
Variáveis estáticas da classe base ( ) são inicializadas
Animal
. No nosso caso, a variávelanimalCount
de classeAnimal
recebe o valor 7700000. -
As variáveis estáticas da classe descendente ( ) são inicializadas
Cat
. Preste atenção - ainda estamos dentro do construtorAnimal
e o console já diz:Выполняется конструктор базового класса Animal Были ли уже проинициализированы переменные класса Animal? Текущее meaning статической переменной animalCount = 7700000 Текущее meaning brain в классе Animal = Изначальное meaning brain в классе Animal Текущее meaning heart в классе Animal = Изначальное meaning heart в классе Animal Были ли уже проинициализированы переменные класса Cat? Текущее meaning статической переменной catsCount = 37
-
Em seguida, as variáveis não estáticas da classe base são inicializadas . Atribuímos especificamente a eles valores iniciais, que são então alterados para novos no construtor. O construtor
Animal
ainda não concluiu seu trabalho, mas os valores iniciais jábrain
foramheart
atribuídos:Выполняется конструктор базового класса Animal Были ли уже проинициализированы переменные класса Animal? Текущее meaning статической переменной animalCount = 7700000 Текущее meaning brain в классе Animal = Изначальное meaning brain в классе Animal Текущее meaning heart в классе Animal = Изначальное meaning heart в классе Animal
-
O construtor da classe base começa a funcionar .
Já vimos que esta etapa é apenas a quarta: nos três primeiros pontos, no momento em que o construtor começa a funcionar,
Animal
muitas variáveis já possuem valores atribuídos. -
Inicializando campos não estáticos de uma classe filha (
Cat
).Isso acontece antes do designer
Cat
começar a trabalhar.Na época em que ele começou a trabalhar, a variável
tail
já tinha um valor:Конструктор класса Cat начал работу (конструктор Animal уже был выполнен) Текущее meaning статической переменной catsCount = 37 Текущее meaning tail = Изначальное meaning tail в классе Cat
-
O construtor da classe descendente é chamado
Cat
GO TO FULL VERSION