Animal
(animal):
public class Animal {
String name;
int age;
}
We can create for it, for example, 2 descendant classes - Cat
and Dog
. This is done using the keyword extends
.
public class Cat extends Animal {
}
public class Dog extends Animal {
}
This may be useful to us in the future. For example, if the task is to catch mice, we will create an object in the program Cat
. If the task is to run after a stick, then we use the object Dog
. And if we create a program that simulates a veterinary clinic, it will work with the class Animal
(to be able to treat both cats and dogs). It is very important to remember for the future that when creating an object, the constructor of its base class is called first , and only then the constructor of the class itself, the object of which we are creating, is called. That is, when creating an object, Cat
the class constructor will work first Animal
, and only then the constructor Cat
. To make sure of this, let’s add to the constructors Cat
and Animal
output to the 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();
}
}
Console output:
Отработал конструктор Animal
Отработал конструктор Cat!
Indeed, this is how it all works! What is it for? For example, to avoid duplicating common fields of two classes. For example, every animal has a heart and a brain, but not every animal has a tail. We can declare fields common to all animals brain
in heart
the parent class Animal
, and a field tail
in the subclass Cat
. Now we will create a constructor for the class Cat
, where we will pass all 3 fields.
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");
}
}
Pay attention:the constructor works successfully, although the class Cat
does not have fields brain
and heart
. These fields were “pulled up” from the base class Animal
. The descendant class has access to the fields of the base class, so Cat
they are visible in our class. Therefore, we do not need Cat
to duplicate these fields in the class - we can take them from the class Animal
. Moreover, we can explicitly call the constructor of the base class in the constructor of the descendant class. The base class is also called the “ superclass ,” which is why Java uses the keyword super
. In the previous example
public Cat(String brain, String heart, String tail) {
this.brain = brain;
this.heart = heart;
this.tail = tail;
}
We separately assigned each field that is in our parent class. In fact, you don't have to do this. It is enough to call the constructor of the parent class and pass it the necessary parameters:
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");
}
}
In the constructor, Cat
we called the constructor Animal
and passed two fields to it. We only have to explicitly initialize one field - tail
which is Animal
not present. Remember how we said that when an object is created, the constructor of the parent class is called first? So, this is why the word super()
should always come first in the constructor! Otherwise, the logic of the constructors will be broken and the program will generate an 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("Brain", "Heart", "Tail");
}
}
The compiler knows that when creating an object of a descendant class, the constructor of the base class is called first. And if you try to manually change this behavior, it will not allow it.
The process of creating an object.
Above we looked at an example with a base and parent class -Animal
and Cat
. Now, using these two classes as an example, we will look at the process of creating an object and initializing variables. We know that variables are static and instance variables (non-static). We also know that the base class Animal
has its own variables, and the descendant class Cat
has its own. Let's add one static variable to the class for Animal
clarity . The class Cat
variable would be the total number of animal species on earth, and the variable would be the number of cat species. In addition, we will assign starting values to all non-static variables of both classes (which will then change in the constructor). 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");
}
}
So, we create a new object of the class Cat
inherited from Animal
. Let's add detailed console output to our program to see what will happen and in what order. This is what will be output to the console as a result of creating an object 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 = Хвост
So, now we can clearly see in what order variables are initialized and constructors are called when creating a new object:
-
Static variables of the base class ( ) are initialized
Animal
. In our case, theanimalCount
class variableAnimal
is assigned the value 7700000. -
The static variables of the descendant class ( ) are initialized
Cat
. Pay attention - we are still inside the constructorAnimal
, and the console already says:Выполняется конструктор базового класса Animal Были ли уже проинициализированы переменные класса Animal? Текущее meaning статической переменной animalCount = 7700000 Текущее meaning brain в классе Animal = Изначальное meaning brain в классе Animal Текущее meaning heart в классе Animal = Изначальное meaning heart в классе Animal Были ли уже проинициализированы переменные класса Cat? Текущее meaning статической переменной catsCount = 37
-
Next, non-static variables of the base class are initialized . We specifically assigned them initial values, which are then changed to new ones in the constructor. The constructor
Animal
has not yet completed its work, but the initial valuesbrain
haveheart
already been assigned:Выполняется конструктор базового класса Animal Были ли уже проинициализированы переменные класса Animal? Текущее meaning статической переменной animalCount = 7700000 Текущее meaning brain в классе Animal = Изначальное meaning brain в классе Animal Текущее meaning heart в классе Animal = Изначальное meaning heart в классе Animal
-
The base class constructor starts working .
We have already seen that this stage is only the fourth: in the first three points, at the time the constructor starts working,
Animal
many variables have already been assigned values. -
Initializing non-static fields of a child class (
Cat
).It happens before the designer
Cat
starts working.At the time he started working, the variable
tail
already had a value:Конструктор класса Cat начал работу (конструктор Animal уже был выполнен) Текущее meaning статической переменной catsCount = 37 Текущее meaning tail = Изначальное meaning tail в классе Cat
-
The constructor of the descendant class is called
Cat
GO TO FULL VERSION