Animal
(тварина):
public class Animal {
String name;
int age;
}
Ми можемо створити для нього, наприклад, 2 класи-нащадки - Cat
і Dog
. Це робиться з використанням ключового слова extends
.
public class Cat extends Animal {
}
public class Dog extends Animal {
}
Це може стати нам у нагоді в майбутньому. Наприклад, якщо буде завдання ловити мишей, створимо в програмі об'єкт Cat
. Якщо завдання бігати за паличкою - тут ми використовуємо об'єкт Dog
. А якщо створюватимемо програму, яка симулює ветеринарну клініку — вона працюватиме з класом Animal
(щоб вміти лікувати і кішок, і собак). Дуже важливо запам'ятати на майбутнє, що при створенні об'єкта насамперед викликається конструктор його базового класу , а потім — конструктор самого класу, об'єкт якого ми створюємо. Тобто при створенні об'єкта Cat
спочатку відпрацює конструктор класу Animal
, а потім конструктор Cat
. Щоб переконатися в цьому, додамо до конструкторів Cat
і Animal
виведення в консоль.
public class Animal {
public Animal() {
System.out.println("Відпрацював конструктор Animal");
}
}
public class Cat extends Animal {
public Cat() {
System.out.println("Відпрацював конструктор Cat!");
}
public static void main(String[] args) {
Cat cat = new Cat();
}
}
Виведення в консоль:
Отработал конструктор Animal
Отработал конструктор Cat!
Справді все так і працює! Для чого це потрібно? Наприклад, щоб не дублювати спільні поля двох класів. Наприклад, у кожної тварини є серце та мозок, але не у кожної є хвіст. Ми можемо оголосити загальні для всіх тварин поля brain
і heart
в батьківському класі Animal
, а поле tail
— у підкласі Cat
. Тепер ми створимо конструктор для класу Cat
, куди передамо всі 3 поля.
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("Мозок", "Серце", "Хвіст");
}
}
Зверни увагу:конструктор успішно працює, хоча в класі Cat
немає полів brain
та heart
. Ці поля "підтягнулися" з базового класу Animal
. Клас-спадкоємець має доступ до полів базового класу, тому в нашому класі Cat
вони видно. Тому нам не потрібно в класі Cat
дублювати ці поля – ми можемо взяти їх із класу Animal
. Більш того, ми можемо явно викликати конструктор базового класу в конструкторі класу-нащадка. Базовий клас ще називають " суперкласом ", тому в Java для його позначення використовується ключове слово super
. У попередньому прикладі
public Cat(String brain, String heart, String tail) {
this.brain = brain;
this.heart = heart;
this.tail = tail;
}
Ми окремо привласнювали кожне поле, яке є у нашому батьківському класі. Насправді, цього можна не робити. Достатньо викликати конструктор батьківського класу та передати йому потрібні параметри:
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("Мозок", "Серце", "Хвіст");
}
}
У конструкторі Cat
ми викликали конструктор Animal
і передали до нього два поля. Нам залишилося явно проініціалізувати лише одне поле — tail
якого Animal
немає. Пам'ятаєш, ми говорабо про те, що при створенні об'єкта насамперед викликається конструктор класу-батька? Так ось, саме тому слово super()
завжди має стояти у конструкторі першим! Інакше логіка роботи конструкторів буде порушена та програма видасть помилку.
public class Cat extends Animal {
String tail;
public Cat(String brain, String heart, String tail) {
this.tail = tail;
super(brain, heart);//помилка!
}
public static void main(String[] args) {
Cat cat = new Cat("Мозок", "Серце", "Хвіст");
}
}
Компілятор знає, що при створенні об'єкта класу-нащадка спочатку викликається конструктор базового класу. І якщо ти спробуєш вручну змінити цю поведінку – він не дозволить цього зробити.
Процес створення об'єкта.
Вище ми з тобою розглянули приклад із базовим та батьківським класом –Animal
і Cat
. Тепер на прикладі цих двох класів ми розглянемо процес створення об'єкта та ініціалізації змінних. Ми знаємо, що змінні бувають статичними та змінними екземпляра (нестатичними). Також ми знаємо, що у базовому класі Animal
є свої змінні, а у класі-нащадці Cat
— свої. Додамо до класу Animal
і Cat
по одній статичній змінній для наочності. Змінна animalCount
в класі Animal
означатиме загальну кількість видів тварин на землі, а зміннаcatsCount
- Число видів сімейства котячих. Крім того, надамо всім нестатичним змінним в обох класів стартові значення (яке потім зміниться в конструкторі).
public class Animal {
String brain = "Спочаткове значення brain у класі Animal";
String heart = "Споконвічне значення heart у класі Animal";
public static int animalCount = 7700000;
public Animal(String brain, String heart) {
System.out.println("Виконується конструктор базового класу Animal");
System.out.println("Чи були вже проініціалізовані змінні класу Animal?");
System.out.println("Поточне значення статичної змінної animalCount = " + animalCount);
System.out.println("Поточне значення brain у класі Animal = " + this.brain);
System.out.println("Поточне значення heart у класі Animal = " + this.heart);
System.out.println("Чи були вже проініціалізовані змінні класу Cat?");
System.out.println("Поточне значення статичної змінної catsCount = " + Cat.catsCount);
this.brain = brain;
this.heart = heart;
System.out.println("Конструктор базового класу Animal завершив роботу!");
System.out.println("Поточне значення brain = " + this.brain);
System.out.println("Поточне значення heart = " + this.heart);
}
}
public class Cat extends Animal {
String tail = "Споконвічне значення tail у класі Cat";
static int catsCount = 37;
public Cat(String brain, String heart, String tail) {
super(brain, heart);
System.out.println("Конструктор класу Cat розпочав роботу (конструктор Animal вже був виконаний)");
System.out.println("Поточне значення статичної змінної catsCount = " + catsCount);
System.out.println("Поточне значення tail = " + this.tail);
this.tail = tail;
System.out.println("Поточне значення tail = " + this.tail);
}
public static void main(String[] args) {
Cat cat = new Cat("Мозок", "Серце", "Хвіст");
}
}
Отже, ми створюємо новий об'єкт класу Cat
, успадкованого від Animal
. Додамо до нашої програми докладний консольний висновок, щоб подивитися що і в якому порядку відбуватиметься. Ось що буде виведено в консоль в результаті створення об'єкта Cat
:
Выполняется конструктор базового класса Animal
Были ли уже проинициализированы переменные класса Animal?
Текущее значення статической переменной animalCount = 7700000
Текущее значення brain в классе Animal = Изначальное значення brain в классе Animal
Текущее значення heart в классе Animal = Изначальное значення heart в классе Animal
Были ли уже проинициализированы переменные класса Cat?
Текущее значення статической переменной catsCount = 37
Конструктор базового класса Animal завершил работу!
Текущее значення brain = Мозг
Текущее значення heart = Сердце
Конструктор класса Cat начал работу (конструктор Animal уже был выполнен)
Текущее значення статической переменной catsCount = 37
Текущее значення tail = Изначальное значення tail в классе Cat
Текущее значення tail = Хвост
Отже, тепер ми наочно бачимо у порядку відбувається ініціалізація змінних і виклик конструкторів під час створення нового об'єкта:
-
Ініціалізуються статичні змінні базового класу (
Animal
). У нашому випадку - змінноїanimalCount
класуAnimal
надається значення 7700000. -
Ініціалізуються статичні змінні класу-нащадка (
Cat
). Зверни увагу - ми все ще всередині конструктораAnimal
, а в консолі вже написано:Выполняется конструктор базового класса Animal Были ли уже проинициализированы переменные класса Animal? Текущее значення статической переменной animalCount = 7700000 Текущее значення brain в классе Animal = Изначальное значення brain в классе Animal Текущее значення heart в классе Animal = Изначальное значення heart в классе Animal Были ли уже проинициализированы переменные класса Cat? Текущее значення статической переменной catsCount = 37
-
Далі ініціалізуються нестатичні змінні базового класу . Ми спеціально надали їм початкові значення, які потім у конструкторі змінюються на нові. Конструктор
Animal
ще не відпрацював до кінця, але початкові значенняbrain
вжеheart
присвоєно:Выполняется конструктор базового класса Animal Были ли уже проинициализированы переменные класса Animal? Текущее значення статической переменной animalCount = 7700000 Текущее значення brain в классе Animal = Изначальное значення brain в классе Animal Текущее значення heart в классе Animal = Изначальное значення heart в классе Animal
-
Починає роботу конструктор базового класу .
У тому, що цей етап іде лише четвертим за рахунком, ми вже переконалися: у перших трьох пунктах на момент початку роботи конструктора
Animal
багатьом змінним вже надано значення. -
Ініціалізація нестатичних полів дочірнього класу (
Cat
).Вона відбувається раніше, ніж конструктор
Cat
розпочинає роботу.На момент, коли він почав роботу, змінна
tail
вже мала значення:Конструктор класса Cat начал работу (конструктор Animal уже был выполнен) Текущее значення статической переменной catsCount = 37 Текущее значення tail = Изначальное значення tail в классе Cat
-
Викликається конструктор класу нащадка
Cat
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ