Animal
(animal):
public class Animal {
String name;
int age;
}
Podemos crear para ello, por ejemplo, 2 clases descendientes: Cat
y Dog
. Esto se hace usando la palabra clave extends
.
public class Cat extends Animal {
}
public class Dog extends Animal {
}
Esto puede resultarnos útil en el futuro. Por ejemplo, si la tarea es cazar ratones, crearemos un objeto en el programa Cat
. Si la tarea es correr detrás de un palo, entonces usamos el objeto Dog
. Y si creamos un programa que simule una clínica veterinaria, funcionará con la clase Animal
(para poder tratar tanto a perros como a gatos). Es muy importante recordar para el futuro que al crear un objeto, primero se llama al constructor de su clase base , y solo entonces se llama al constructor de la clase en sí, cuyo objeto estamos creando. Es decir, al crear un objeto, Cat
primero funcionará el constructor de la clase Animal
, y solo después el constructor Cat
. Para asegurarnos de esto, agreguemos a los constructores Cat
y Animal
enviemos a la consola.
public class Animal {
public Animal() {
System.out.println("Constructor de animales completado");
}
}
public class Cat extends Animal {
public Cat() {
System.out.println("¡El constructor Cat funcionó!");
}
public static void main(String[] args) {
Cat cat = new Cat();
}
}
Salida de consola:
Отработал конструктор Animal
Отработал конструктор Cat!
De hecho, ¡así es como funciona todo! ¿Para qué sirve? Por ejemplo, para evitar duplicar campos comunes de dos clases. Por ejemplo, todo animal tiene corazón y cerebro, pero no todos los animales tienen cola. Podemos declarar campos comunes a todos los animales brain
en heart
la clase principal Animal
y un campo tail
en la subclase Cat
. Ahora crearemos un constructor para la clase Cat
, donde pasaremos los 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("Cerebro", "Corazón", "Cola");
}
}
Prestar atención:El constructor funciona correctamente, aunque la clase Cat
no tiene campos brain
y heart
. Estos campos fueron "extraídos" de la clase base Animal
. La clase descendiente tiene acceso a los campos de la clase base, por lo que Cat
son visibles en nuestra clase. Por lo tanto, no necesitamos Cat
duplicar estos campos en la clase; podemos tomarlos de la clase Animal
. Además, podemos llamar explícitamente al constructor de la clase base en el constructor de la clase descendiente. La clase base también se llama " superclase ", razón por la cual Java usa la palabra clave super
. En el ejemplo anterior
public Cat(String brain, String heart, String tail) {
this.brain = brain;
this.heart = heart;
this.tail = tail;
}
Asignamos por separado cada campo que está en nuestra clase principal. De hecho, no es necesario que haga esto. Basta con llamar al constructor de la clase padre y pasarle los parámetros necesarios:
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("Cerebro", "Corazón", "Cola");
}
}
En el constructor, Cat
llamamos al constructor Animal
y le pasamos dos campos. Sólo tenemos que inicializar explícitamente un campo, tail
que Animal
no está presente. ¿Recuerda que dijimos que cuando se crea un objeto, primero se llama al constructor de la clase principal? ¡Es por eso que la palabra super()
siempre debe aparecer primero en el constructor! De lo contrario, la lógica de los constructores se romperá y el programa generará un error.
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("Cerebro", "Corazón", "Cola");
}
}
El compilador sabe que al crear un objeto de una clase descendiente, se llama primero al constructor de la clase base. Y si intenta cambiar manualmente este comportamiento, no lo permitirá.
El proceso de creación de un objeto.
Arriba vimos un ejemplo con una clase base y principal:Animal
y Cat
. Ahora, usando estas dos clases como ejemplo, veremos el proceso de creación de un objeto e inicialización de variables. Sabemos que las variables son estáticas y variables de instancia (no estáticas). También sabemos que la clase base Animal
tiene sus propias variables y la clase descendiente Cat
tiene las suyas propias. Agreguemos una variable estática a la clase para Animal
mayor claridad . Cat
La variable animalCount
de clase Animal
sería el número total de especies animales en la Tierra y la variable catsCount
sería el número de especies de gatos. Además, asignaremos valores iniciales a todas las variables no estáticas de ambas clases (que luego cambiarán en el constructor).
public class Animal {
String brain = "El valor inicial del cerebro en la clase Animal";
String heart = "El valor inicial de corazón en la clase Animal";
public static int animalCount = 7700000;
public Animal(String brain, String heart) {
System.out.println("Se está ejecutando el constructor de la clase base Animal");
System.out.println("¿Ya se inicializaron las variables de la clase Animal?");
System.out.println("El valor actual de la variable estática animalCount = " + animalCount);
System.out.println("Valor actual del cerebro en la clase Animal =" + this.brain);
System.out.println("Valor actual del corazón en la clase Animal =" + this.heart);
System.out.println("¿Ya se inicializaron las variables de la clase Cat?");
System.out.println("El valor actual de la variable estática catsCount = " + Cat.catsCount);
this.brain = brain;
this.heart = heart;
System.out.println("¡Constructor de clase base animal completado!");
System.out.println("Valor actual del cerebro =" + this.brain);
System.out.println("Valor actual del corazón = " + this.heart);
}
}
public class Cat extends Animal {
String tail = "El valor inicial de la cola en la clase Cat";
static int catsCount = 37;
public Cat(String brain, String heart, String tail) {
super(brain, heart);
System.out.println("Se ha iniciado el constructor de la clase Gato (ya se ha ejecutado el constructor Animal)");
System.out.println("El valor actual de la variable estática catsCount = " + catsCount);
System.out.println("Valor actual cola =" + this.tail);
this.tail = tail;
System.out.println("Valor actual cola =" + this.tail);
}
public static void main(String[] args) {
Cat cat = new Cat("Cerebro", "Corazón", "Cola");
}
}
Entonces, creamos un nuevo objeto de la clase Cat
heredada de Animal
. Agreguemos una salida de consola detallada a nuestro programa para ver qué sucederá y en qué orden. Esto es lo que se mostrará en la consola como resultado de la creación de un objeto Cat
:
Выполняется конструктор базового класса Animal
Были ли уже проинициализированы переменные класса Animal?
Текущее significado статической переменной animalCount = 7700000
Текущее significado brain в классе Animal = Изначальное significado brain в классе Animal
Текущее significado heart в классе Animal = Изначальное significado heart в классе Animal
Были ли уже проинициализированы переменные класса Cat?
Текущее significado статической переменной catsCount = 37
Конструктор базового класса Animal завершил работу!
Текущее significado brain = Мозг
Текущее significado heart = Сердце
Конструктор класса Cat начал работу (конструктор Animal уже был выполнен)
Текущее significado статической переменной catsCount = 37
Текущее significado tail = Изначальное significado tail в классе Cat
Текущее significado tail = Хвост
Entonces, ahora podemos ver claramente en qué orden se inicializan las variables y se llaman los constructores al crear un nuevo objeto:
-
Se inicializan las variables estáticas de la clase base ( )
Animal
. En nuestro caso, a la variableanimalCount
de claseAnimal
se le asigna el valor 7700000. -
Se inicializan las variables estáticas de la clase descendiente ( )
Cat
. Preste atención: todavía estamos dentro del constructorAnimal
y la consola ya dice:Выполняется конструктор базового класса Animal Были ли уже проинициализированы переменные класса Animal? Текущее significado статической переменной animalCount = 7700000 Текущее significado brain в классе Animal = Изначальное significado brain в классе Animal Текущее significado heart в классе Animal = Изначальное significado heart в классе Animal Были ли уже проинициализированы переменные класса Cat? Текущее significado статической переменной catsCount = 37
-
A continuación, se inicializan las variables no estáticas de la clase base . Les asignamos específicamente valores iniciales, que luego se cambian por otros nuevos en el constructor. El constructor
Animal
aún no ha completado su trabajo, pero ya sebrain
hanheart
asignado los valores iniciales:Выполняется конструктор базового класса Animal Были ли уже проинициализированы переменные класса Animal? Текущее significado статической переменной animalCount = 7700000 Текущее significado brain в классе Animal = Изначальное significado brain в классе Animal Текущее significado heart в классе Animal = Изначальное significado heart в классе Animal
-
El constructor de la clase base comienza a funcionar .
Ya hemos visto que esta etapa es solo la cuarta consecutiva: en los primeros tres puntos, en el momento en que el constructor comienza a trabajar,
Animal
ya se han asignado valores a muchas variables. -
Inicializando campos no estáticos de una clase secundaria (
Cat
).Sucede antes de que el diseñador
Cat
comience a trabajar.En el momento en que empezó a trabajar, la variable
tail
ya tenía un valor:Конструктор класса Cat начал работу (конструктор Animal уже был выполнен) Текущее significado статической переменной catsCount = 37 Текущее significado tail = Изначальное significado tail в классе Cat
-
El constructor de la clase descendiente se llama
Cat
GO TO FULL VERSION