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
класа (и по този начин ще може да лекува Howто котки, така и кучета). Много е важно да запомните, че когато се създава обект, първо се извиква конструкторът на неговия базов клас . Едва след като този конструктор е завършен, програмата изпълнява конструктора на класа, съответстващ на обекта, който създаваме. С други думи, когато се създава Cat
обект, първо се изпълнява конструкторът иAnimal
едва след товаCat
изпълнен конструктор . За да видите това, добавете малко конзолен изход към Cat
и Animal
конструкторите.
public class Animal {
public Animal() {
System.out.println("Animal constructor executed");
}
}
public class Cat extends Animal {
public Cat() {
System.out.println("Cat constructor executed!");
}
public static void main(String[] args) {
Cat cat = new Cat();
}
}
Изход от конзолата: Конструкторът на животни е изпълнен Конструкторът на котка е изпълнен! Наистина, работи по този начин! Защо? Една от причините е да се избегне дублиране на полета, споделени между двата класа. Например всяко животно има сърце и мозък, но не всяко животно има опашка. Можем да декларираме мозъчни и сърдечни полета, които са общи за всички животни, в Animal
родителския клас и поле на опашката в 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("Brain", "Heart", "Tail");
}
}
Забележка: Конструкторът работи правилно, въпреки че класът Cat
няма мозъчни и сърдечни полета . Тези полета са "наследени" от 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("Brain", "Heart", "Tail");
}
}
В Cat
конструктора извикахме Animal
конструктора и предадохме две полета. Имахме само едно поле за изрично инициализиране: опашка , което не е в Animal
. Помните ли, че споменахме, че конструкторът на родителския клас се извиква първи, когато се създава обект? Ето защо super() винаги трябва да е първи в конструктор! В противен случай логиката на конструктора ще бъде нарушена и програмата ще генерира грешка.
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");
}
}
Компилаторът знае, че когато се създава обект от дъщерен клас, първо се извиква конструкторът на базовия клас. И ако се опитате ръчно да промените това поведение, компилаторът няма да го позволи.
Как се създава обект
По-рано разгледахме пример с базов и родителски клас:Animal
и Cat
. Използвайки тези два класа като примери, сега ще разгледаме процеса на създаване на обект и инициализиране на променливи. Знаем, че има статични и екземплярни (нестатични) променливи . Знаем също, че Animal
базовият клас има променливи, а Cat
дъщерният клас има свои собствени. За по-голяма яснота ще добавим по една статична променлива към класовете Animal
и Cat
. Променливата animalCount в класа ще представлява общия брой животински видове на Земята, а catCountAnimal
променлива ще означава броя на видовете котки. Освен това ще присвоим начални стойности на всички нестатични променливи в двата класа (които след това ще бъдат променени в конструктора).
public class Animal {
String brain = "Initial value of brain in the Animal class";
String heart = "Initial value of heart in the Animal class";
public static int animalCount = 7700000;
public Animal(String brain, String heart) {
System.out.println("Animal base class constructor is running");
System.out.println("Have the variables of the Animal class already been initialized?");
System.out.println("Current value of static variable animalCount = " + animalCount);
System.out.println("Current value of brain in the Animal class = " + this.brain);
System.out.println("Current value of heart in the Animal class = " + this.heart);
System.out.println("Have the variables of the Cat class already been initialized?");
System.out.println("Current value of static variable catCount = " + Cat.catCount);
this.brain = brain;
this.heart = heart;
System.out.println("Animal base class constructor is done!");
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 = "Initial value of tail in the Cat class";
static int catCount = 37;
public Cat(String brain, String heart, String tail) {
super(brain, heart);
System.out.println("The cat class constructor has started (The Animal constructor already finished)");
System.out.println("Current value of static variable catCount = " + catCount);
System.out.println("Current value of tail = " + this.tail);
this.tail = tail;
System.out.println("Current value of tail = " + this.tail);
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
Така че създаваме нов екземпляр на Cat
класа, който наследява Animal
. Добавихме подробен конзолен изход, за да видим Howво се случва и в Howъв ред. Ето Howво ще се покаже, когато Cat
бъде създаден обект: Конструкторът на базовия клас Animal работи. Инициализирани ли са вече променливите на класа Animal? Текуща стойност на статичната променлива animalCount = 7700000 Текуща стойност на мозъка в класа Animal = Първоначалната стойност на мозъка в класа Animal Текущата стойност на сърцето в класа Animal = Първоначалната стойност на сърцето в класа Animal Вече имате променливите на класа Cat инициализирано? Текуща стойност на статичната променлива catCount = 37 Конструкторът на базовия клас за животни е готов! Текуща стойност на мозъка = Мозък Текуща стойност heart = Heart Конструкторът на клас cat е стартиран (Конструкторът Animal вече е завършен) Текуща стойност на статичната променлива catCount = 37 Текуща стойност на опашката = Първоначална стойност на опашката в класа Cat Текуща стойност на опашката = Опашка И така, сега можем ясно да видим реда на инициализацията на променливите и извикванията на конструктора, когато се създава нов обект:
- Статичните променливи на базовия клас (
Animal
) се инициализират. В нашия случайAnimal
променливата animalCount на класа е зададена на 7700000. -
Статичните променливи на дъщерния клас (
Cat
) се инициализират.Забележка: все още сме вътре в
Animal
конструктора и вече сме показали:Конструкторът на базовия клас Animal работи.
Инициализирани ли са вече променливите на класа Animal?
Текуща стойност на статичната променлива animalCount = 7700000
Текуща стойност на мозъка в класа Animal = Първоначалната стойност на мозъка в класа Animal
Текущата стойност на сърцето в класа Animal = Първоначалната стойност на сърцето в класа Animal
Вече имате променливите на класа Cat инициализирано?
Текуща стойност на статичната променлива catCount = 37 -
След това се инициализират нестатичните променливи на базовия клас . Специално им присвоихме начални стойности, които след това се заменят в конструктора. Конструкторът Animal все още не е завършен, но първоначалните стойности на мозъка и сърцето вече са зададени:
Конструкторът на базовия клас Animal работи.
Инициализирани ли са вече променливите на класа Animal?
Текуща стойност на статичната променлива animalCount = 7700000
Текуща стойност на мозъка в класа Animal = Първоначална стойност на мозъка в класа Animal
Текуща стойност на сърцето в класа Animal = Първоначална стойност на сърцето в класа Animal -
Конструкторът на базов клас стартира.
Вече се убедихме, че тази стъпка е четвърта: в първите три стъпки в началото наAnimal
конструктора на много променливи вече са присвоени стойности. -
Нестатичните полета на дъщерния клас (
Cat
) се инициализират.
Това се случва предиCat
конструкторът да започне да работи.
Когато започне да работи, променливата на опашката вече има стойност:Конструкторът на клас cat е стартиран (Конструкторът Animal вече е завършен) Текуща стойност на статичната променлива catCount = 37 Текуща стойност на опашката = Първоначална стойност на опашката в класа Cat
-
Cat
Извиква се конструкторът на дъщерния класЕто How изглежда създаването на обект в Java!
Трябва да кажа, че не сме големи фенове на ученето наизуст, но е най-добре да запомните реда на инициализацията на променливите и извикванията на конструктора .
Това значително ще увеличи вашето разбиране за потока на програмата и състоянието на вашите обекти във всеки конкретен момент.
Освен това много класове не използват наследяване. В този случай стъпките, свързани с базовия клас, не се прилагат.
Още четене: |
---|
GO TO FULL VERSION