JavaRush /Java блог /Random UA /Конструктори базових класів

Конструктори базових класів

Стаття з групи Random UA
Вітання! Минулого разу ми говорабо про конструкторів і дізналися про них досить багато. Зараз ми поговоримо про таку річ, як конструктори базових класів. Що таке базовий клас ? Справа в тому, що Java кілька різних класів можуть мати загальне походження. Конструктори базових класів - 2Це називається успадкуванням . У кількох класів-нащадків може бути один загальний клас-предок. Наприклад, уявимо, що у нас є клас 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 = Хвост
Отже, тепер ми наочно бачимо у порядку відбувається ініціалізація змінних і виклик конструкторів під час створення нового об'єкта:
  1. Ініціалізуються статичні змінні базового класу ( Animal). У нашому випадку - змінної animalCountкласу Animalнадається значення 7700000.

  2. Ініціалізуються статичні змінні класу-нащадка ( Cat). Зверни увагу - ми все ще всередині конструктора Animal, а в консолі вже написано:

    
        Выполняется конструктор базового класса Animal
        Были ли уже проинициализированы переменные класса Animal?
        Текущее значення статической переменной animalCount = 7700000
        Текущее значення brain в классе Animal = Изначальное значення brain в классе Animal
        Текущее значення heart в классе Animal = Изначальное значення heart в классе Animal
        Были ли уже проинициализированы переменные класса Cat?
        Текущее значення статической переменной catsCount = 37
  3. Далі ініціалізуються нестатичні змінні базового класу . Ми спеціально надали їм початкові значення, які потім у конструкторі змінюються на нові. Конструктор Animalще не відпрацював до кінця, але початкові значення brainвже heartприсвоєно:

    
        Выполняется конструктор базового класса Animal
        Были ли уже проинициализированы переменные класса Animal?
        Текущее значення статической переменной animalCount = 7700000
        Текущее значення brain в классе Animal = Изначальное значення brain в классе Animal
        Текущее значення heart в классе Animal = Изначальное значення heart в классе Animal
  4. Починає роботу конструктор базового класу .

    У тому, що цей етап іде лише четвертим за рахунком, ми вже переконалися: у перших трьох пунктах на момент початку роботи конструктора Animalбагатьом змінним вже надано значення.

  5. Ініціалізація нестатичних полів дочірнього класу ( Cat).

    Вона відбувається раніше, ніж конструктор Catрозпочинає роботу.

    На момент, коли він почав роботу, змінна tailвже мала значення:

    
    Конструктор класса Cat начал работу (конструктор Animal уже был выполнен)
    Текущее значення статической переменной catsCount = 37
    Текущее значення tail = Изначальное значення tail в классе Cat
  6. Викликається конструктор класу нащадка Cat

Ось так виглядає процес створення об'єкта Java! Треба сказати, що ми не великі шанувальники зубріння, але порядок ініціалізації змінних та виклику конструкторів краще вивчити напам'ять та запам'ятати на майбутнє . Це сильно збільшить твоє розуміння ходу роботи програми та стану твоїх об'єктів у кожний конкретний момент. Тим паче, що часто класи не мають прямих батьківських класів (суперкласів/ базових класів). У такому разі пункти, пов'язані з базовим класом, не виконуватимуться.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ