Animal
(zwierzę):
public class Animal {
String name;
int age;
}
Możemy dla niego stworzyć np. 2 klasy potomne - Cat
i Dog
. Odbywa się to za pomocą słowa kluczowego extends
.
public class Cat extends Animal {
}
public class Dog extends Animal {
}
Może nam się to przydać w przyszłości. Przykładowo, jeśli zadaniem będzie łapanie myszy, utworzymy w programie obiekt Cat
. Jeśli zadaniem jest bieganie za kijem, wówczas korzystamy z obiektu Dog
. A jeśli stworzymy program symulujący klinikę weterynaryjną, to będzie on współpracował z klasą Animal
(aby móc leczyć zarówno koty, jak i psy). Bardzo ważne jest, aby pamiętać na przyszłość, że podczas tworzenia obiektu najpierw wywoływany jest konstruktor jego klasy bazowej , a dopiero potem konstruktor samej klasy, której obiekt tworzymy. Oznacza to, że podczas tworzenia obiektu Cat
najpierw będzie działał konstruktor klasy Animal
, a dopiero potem konstruktor Cat
. Aby się o tym przekonać, dodajmy do konstruktorów Cat
i Animal
wyprowadźmy na konsolę.
public class Animal {
public Animal() {
System.out.println(„Konstruktor zwierząt ukończony”);
}
}
public class Cat extends Animal {
public Cat() {
System.out.println(„Konstruktor Cat zadziałał!”);
}
public static void main(String[] args) {
Cat cat = new Cat();
}
}
Wyjście konsoli:
Отработал конструктор Animal
Отработал конструктор Cat!
Rzeczywiście, tak to wszystko działa! Po co to jest? Na przykład, aby uniknąć duplikowania wspólnych pól dwóch klas. Na przykład każde zwierzę ma serce i mózg, ale nie każde zwierzę ma ogon. Możemy zadeklarować pola wspólne dla wszystkich zwierząt brain
w heart
klasie nadrzędnej Animal
i pole tail
w podklasie Cat
. Teraz stworzymy konstruktor dla klasy Cat
, w którym przekażemy wszystkie 3 pola.
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("Mózg", "Serce", "Ogon");
}
}
Zwróć uwagę:konstruktor działa pomyślnie, mimo że klasa Cat
nie posiada pól brain
i heart
. Pola te zostały „pobrane” z klasy bazowej Animal
. Klasa potomna ma dostęp do pól klasy bazowej, dzięki czemu Cat
są one widoczne w naszej klasie. Nie musimy zatem Cat
powielać tych pól w klasie – możemy je pobrać z klasy Animal
. Co więcej, możemy jawnie wywołać konstruktor klasy bazowej w konstruktorze klasy potomnej. Klasa bazowa nazywana jest także „nadklasą ” i dlatego w Javie używane jest słowo kluczowe super
. W poprzednim przykładzie
public Cat(String brain, String heart, String tail) {
this.brain = brain;
this.heart = heart;
this.tail = tail;
}
Każde pole znajdujące się w naszej klasie nadrzędnej przypisaliśmy osobno. W rzeczywistości nie musisz tego robić. Wystarczy wywołać konstruktor klasy nadrzędnej i przekazać mu niezbędne parametry:
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("Mózg", "Serce", "Ogon");
}
}
W konstruktorze Cat
wywołaliśmy konstruktora Animal
i przekazaliśmy mu dwa pola. Musimy tylko jawnie zainicjować jedno pole, tail
które Animal
nie jest obecne. Pamiętasz, jak powiedzieliśmy, że podczas tworzenia obiektu najpierw wywoływany jest konstruktor klasy nadrzędnej? Dlatego właśnie słowo super()
powinno zawsze znajdować się na pierwszym miejscu w konstruktorze! W przeciwnym razie logika konstruktorów zostanie złamana, a program wygeneruje błąd.
public class Cat extends Animal {
String tail;
public Cat(String brain, String heart, String tail) {
this.tail = tail;
super(brain, heart);//błąd!
}
public static void main(String[] args) {
Cat cat = new Cat("Mózg", "Serce", "Ogon");
}
}
Kompilator wie, że podczas tworzenia obiektu klasy potomnej najpierw wywoływany jest konstruktor klasy bazowej. A jeśli spróbujesz ręcznie zmienić to zachowanie, nie pozwoli na to.
Proces tworzenia obiektu.
Powyżej przyjrzeliśmy się przykładowi z klasą bazową i klasą nadrzędną -Animal
i Cat
. Teraz, używając tych dwóch klas jako przykładu, przyjrzymy się procesowi tworzenia obiektu i inicjalizacji zmiennych. Wiemy, że zmienne są statyczne , a zmienne instancji (niestatyczne). Wiemy również, że klasa bazowa Animal
ma swoje własne zmienne, a klasa potomna Cat
ma swoje własne. Animal
Dla przejrzystości dodajmy Cat
do klasy jedną zmienną statyczną . Zmienną animalCount
klasową Animal
byłaby całkowita liczba gatunków zwierząt na Ziemi, a zmienną catsCount
byłaby liczba gatunków kotów. Dodatkowo przypiszemy wartości początkowe wszystkim zmiennym niestatycznym obu klas (co następnie ulegnie zmianie w konstruktorze).
public class Animal {
String brain = „Początkowa wartość mózgu w klasie zwierząt”;
String heart = „Początkowa wartość serca w klasie zwierząt”;
public static int animalCount = 7700000;
public Animal(String brain, String heart) {
System.out.println(„Trwa wykonywanie konstruktora klasy bazowej Animal”);
System.out.println(„Czy zmienne klasy Animal zostały już zainicjowane?”);
System.out.println(„Bieżąca wartość zmiennej statycznej animalCount =” + animalCount);
System.out.println(„Aktualna wartość mózgu w klasie Animal =” + this.brain);
System.out.println(„Aktualna wartość serca w klasie Animal =” + this.heart);
System.out.println(„Czy zmienne klasy Cat zostały już zainicjowane?”);
System.out.println(„Bieżąca wartość zmiennej statycznej kotyCount =” + Cat.catsCount);
this.brain = brain;
this.heart = heart;
System.out.println(„Konstruktor podstawowej klasy zwierząt został ukończony!”);
System.out.println(„Aktualna wartość mózgu =” + this.brain);
System.out.println(„Aktualna wartość serca =” + this.heart);
}
}
public class Cat extends Animal {
String tail = „Początkowa wartość ogona w klasie Cat”;
static int catsCount = 37;
public Cat(String brain, String heart, String tail) {
super(brain, heart);
System.out.println(„Konstruktor klasy Cat został uruchomiony (konstruktor Animal został już wykonany)”);
System.out.println(„Bieżąca wartość zmiennej statycznej kotyCount =” + catsCount);
System.out.println(„Ogon wartości bieżącej =” + this.tail);
this.tail = tail;
System.out.println(„Ogon wartości bieżącej =” + this.tail);
}
public static void main(String[] args) {
Cat cat = new Cat("Mózg", "Serce", "Ogon");
}
}
Tworzymy więc nowy obiekt klasy Cat
odziedziczonej z Animal
. Dodajmy do naszego programu szczegółowe dane wyjściowe konsoli, aby zobaczyć, co się stanie i w jakiej kolejności. Oto, co zostanie wypisane na konsolę w wyniku utworzenia obiektu Cat
:
Выполняется конструктор базового класса Animal
Были ли уже проинициализированы переменные класса Animal?
Текущее oznaczający статической переменной animalCount = 7700000
Текущее oznaczający brain в классе Animal = Изначальное oznaczający brain в классе Animal
Текущее oznaczający heart в классе Animal = Изначальное oznaczający heart в классе Animal
Были ли уже проинициализированы переменные класса Cat?
Текущее oznaczający статической переменной catsCount = 37
Конструктор базового класса Animal завершил работу!
Текущее oznaczający brain = Мозг
Текущее oznaczający heart = Сердце
Конструктор класса Cat начал работу (конструктор Animal уже был выполнен)
Текущее oznaczający статической переменной catsCount = 37
Текущее oznaczający tail = Изначальное oznaczający tail в классе Cat
Текущее oznaczający tail = Хвост
Zatem teraz możemy wyraźnie zobaczyć, w jakiej kolejności inicjowane są zmienne i wywoływane konstruktory podczas tworzenia nowego obiektu:
-
Inicjowane są zmienne statyczne klasy bazowej ( )
Animal
. W naszym przypadku zmiennejanimalCount
klasyAnimal
przypisano wartość 7700000. -
Inicjowane są zmienne statyczne klasy potomka ( )
Cat
. Uwaga - nadal jesteśmy w konstruktorzeAnimal
, a konsola już mówi:Выполняется конструктор базового класса Animal Были ли уже проинициализированы переменные класса Animal? Текущее oznaczający статической переменной animalCount = 7700000 Текущее oznaczający brain в классе Animal = Изначальное oznaczający brain в классе Animal Текущее oznaczający heart в классе Animal = Изначальное oznaczający heart в классе Animal Были ли уже проинициализированы переменные класса Cat? Текущее oznaczający статической переменной catsCount = 37
-
Następnie inicjalizowane są zmienne niestatyczne klasy bazowej . Specjalnie przypisaliśmy im wartości początkowe, które następnie są zmieniane w konstruktorze na nowe. Konstruktor
Animal
nie zakończył jeszcze swojej pracy, ale początkowe wartościbrain
zostałyheart
już przypisane:Выполняется конструктор базового класса Animal Были ли уже проинициализированы переменные класса Animal? Текущее oznaczający статической переменной animalCount = 7700000 Текущее oznaczający brain в классе Animal = Изначальное oznaczający brain в классе Animal Текущее oznaczający heart в классе Animal = Изначальное oznaczający heart в классе Animal
-
Konstruktor klasy bazowej zaczyna działać .
Widzieliśmy już, że jest to dopiero czwarty etap: w pierwszych trzech punktach, w momencie rozpoczęcia pracy konstruktora,
Animal
wielu zmiennym przypisano już wartości. -
Inicjowanie niestatycznych pól klasy podrzędnej (
Cat
).Dzieje się to zanim projektant
Cat
zacznie pracować.W momencie rozpoczęcia pracy zmienna
tail
miała już wartość:Конструктор класса Cat начал работу (конструктор Animal уже был выполнен) Текущее oznaczający статической переменной catsCount = 37 Текущее oznaczający tail = Изначальное oznaczający tail в классе Cat
-
Wywoływany jest konstruktor klasy potomnej
Cat
GO TO FULL VERSION