CodeGym /Blog Java /Random-PL /Konstruktory klasy bazowej
Autor
Volodymyr Portianko
Java Engineer at Playtika

Konstruktory klasy bazowej

Opublikowano w grupie Random-PL
Cześć! Ostatnim razem rozmawialiśmy o konstruktorach i dużo się o nich dowiedzieliśmy. Teraz porozmawiamy o konstruktorach klas bazowych .
Konstruktory klasy bazowej - 1
Co to jest klasa podstawowa ? Ma to związek z faktem, że w Javie kilka różnych klas może mieć wspólne pochodzenie.
Konstruktory klasy bazowej - 2
Nazywa się to dziedziczeniem . Kilka klas podrzędnych może mieć jednego wspólnego przodka. Na przykład wyobraź sobie, że mamy Animalklasę:

public class Animal {
  
   String name;
   int age;
}
Możemy zadeklarować 2 klasy potomne: Cati 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. Na przykład, jeśli jest zadanie łapania myszy, utworzymy Cat obiekt w naszym programie. Jeśli zadaniem jest pogoń za kijem, użyjemy przedmiotu Dog . A jeśli stworzymy program, który symuluje klinikę weterynaryjną, będzie działał z Animal klasą (a więc będzie mógł leczyć zarówno koty, jak i psy). Bardzo ważne jest, aby pamiętać, że podczas tworzenia obiektu najpierw wywoływany jest konstruktor jego klasy bazowej . Dopiero po zakończeniu pracy tego konstruktora program wykonuje konstruktor klasy odpowiadającej tworzonemu przez nas obiektowi. Innymi słowy, podczas tworzenia Catobiektu najpierw uruchamiany jest konstruktor ,Animal a dopiero potem jest uruchamiany konstruktorCatwykonany konstruktor . Aby to zobaczyć, dodaj dane wyjściowe konsoli do konstruktorów Cati 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();
   }
}
Wyjście konsoli: Konstruktor Animal wykonany Konstruktor Cat wykonany! Rzeczywiście, to działa w ten sposób! Dlaczego? Jednym z powodów jest uniknięcie powielania pól współużytkowanych przez dwie klasy. Na przykład każde zwierzę ma serce i mózg, ale nie każde zwierzę ma ogon. Moglibyśmy zadeklarować pola mózgowe i sercowe , które są wspólne dla wszystkich zwierząt, w Animalklasie rodzicielskiej i pole ogonowe w Catpodklasie. . Teraz zadeklarujemy Catkonstruktor klasy, który pobiera argumenty dla wszystkich 3 pól.

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");
   }
}
Uwaga: Konstruktor działa poprawnie, mimo że klasa Catnie ma pól mózgowych i sercowych . Te pola są „dziedziczone” z Animalklasy bazowej. Klasa dziedzicząca ma dostęp do pól klasy bazowej , więc są one widoczne w naszej Catklasie. W rezultacie nie musimy duplikować tych pól w Catklasie. Możemy je zabrać z Animalklasy. Co więcej, możemy jawnie wywołać konstruktor klasy bazowej w konstruktorze klasy potomnej. Klasa podstawowa jest również nazywana „ nadklasą ”. Dlatego Java używa słowa kluczowego super , aby wskazać klasę bazową. W poprzednim przykładzie

public Cat(String brain, String heart, String tail) {
       this.brain = brain;
       this.heart = heart;
       this.tail = tail;
   }
Oddzielnie przypisaliśmy każde pole w naszej klasie nadrzędnej. Właściwie nie musimy tego robić. Wystarczy wywołać konstruktor klasy nadrzędnej i przekazać niezbędne argumenty:

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");
   }
}
W Catkonstruktorze wywołaliśmy Animalkonstruktora i przekazaliśmy dwa pola. Mieliśmy tylko jedno pole do jawnej inicjalizacji: tail , którego nie ma w Animal. Pamiętasz, jak wspomnieliśmy, że konstruktor klasy nadrzędnej jest wywoływany jako pierwszy podczas tworzenia obiektu? Dlatego funkcja super() powinna zawsze znajdować się na pierwszym miejscu w konstruktorze! W przeciwnym razie zostanie naruszona logika konstruktora i 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);// Error!
   }

   public static void main(String[] args) {
       Cat cat = new Cat("Brain", "Heart", "Tail");
   }
}
Kompilator wie, że kiedy tworzony jest obiekt klasy potomnej, najpierw wywoływany jest konstruktor klasy bazowej. A jeśli spróbujesz ręcznie zmienić to zachowanie, kompilator na to nie pozwoli.

Jak powstaje obiekt

Wcześniej przyjrzeliśmy się przykładowi z klasą podstawową i nadrzędną: Animali Cat. Używając tych dwóch klas jako przykładów, przyjrzymy się teraz procesowi tworzenia obiektu i inicjalizacji zmiennych. Wiemy, że istnieją zmienne statyczne i instancyjne (niestatyczne) . Wiemy również, że Animalklasa bazowa ma zmienne, a Catklasa potomna ma swoje własne. Dla jasności dodamy po jednej zmiennej statycznej do klas Animali Cat. Zmienna animalCount w klasie będzie reprezentować całkowitą liczbę gatunków zwierząt na Ziemi oraz catCountAnimalzmienna będzie oznaczać liczbę gatunków kotów. Dodatkowo przypiszemy wartości początkowe wszystkim zmiennym niestatycznym w obu klasach (które następnie zostaną zmienione w konstruktorze).

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");
   }
}
Tworzymy więc nową instancję klasy Cat, która dziedziczy Animal. Dodaliśmy szczegółowe dane wyjściowe konsoli, aby zobaczyć, co się dzieje iw jakiej kolejności. Oto, co zostanie wyświetlone po Catutworzeniu obiektu: Konstruktor klasy podstawowej Animal jest uruchomiony Czy zmienne klasy Animal zostały już zainicjowane? Bieżąca wartość zmiennej statycznej animalCount = 7700000 Bieżąca wartość mózgu w klasie Animal = Początkowa wartość mózgu w klasie Animal Bieżąca wartość serca w klasie Animal = Początkowa wartość serca w klasie Animal Czy zmienne klasy Cat są już dostępne został zainicjowany? Bieżąca wartość zmiennej statycznej catCount = 37 Konstruktor klasy bazowej Animal jest gotowy! Aktualna wartość brain = Brain Aktualna wartość heart = Heart Konstruktor klasy cat został uruchomiony (Konstruktor Animal został już zakończony) Aktualna wartość zmiennej statycznej catCount = 37 Aktualna wartość tail = Początkowa wartość tail w klasie Cat Aktualna wartość tail = Ogon Teraz możemy wyraźnie zobaczyć kolejność inicjalizacji zmiennych i wywołań konstruktorów podczas tworzenia nowego obiektu:
  1. Inicjowane są zmienne statyczne klasy bazowej ( Animal). W naszym przypadku zmienna animalCountAnimal klasy jest ustawiona na 7700000.

  2. Inicjowane są zmienne statyczne klasy potomnej ( Cat).

    Uwaga: wciąż jesteśmy wewnątrz Animalkonstruktora i już wyświetliliśmy:

    Konstruktor klasy podstawowej Animal jest uruchomiony
    Czy zmienne klasy Animal zostały już zainicjowane?
    Bieżąca wartość zmiennej statycznej animalCount = 7700000
    Bieżąca wartość mózgu w klasie Animal = Początkowa wartość mózgu w klasie Animal
    Bieżąca wartość serca w klasie Animal = Początkowa wartość serca w klasie Animal
    Czy zmienne klasy Cat są już dostępne został zainicjowany?
    Bieżąca wartość zmiennej statycznej catCount = 37


  3. Następnie inicjowane są zmienne niestatyczne klasy bazowej . Specjalnie nadaliśmy im wartości początkowe, które następnie są zastępowane w konstruktorze. Konstruktor Animal jeszcze się nie skończył, ale początkowe wartości dla mózgu i serca zostały już przypisane:

    Konstruktor klasy podstawowej Animal jest uruchomiony
    Czy zmienne klasy Animal zostały już zainicjowane?
    Aktualna wartość zmiennej statycznej animalCount = 7700000
    Aktualna wartość mózgu w klasie Animal = Początkowa wartość mózgu w klasie Animal
    Aktualna wartość serca w klasie Animal = Początkowa wartość serca w klasie Animal


  4. Uruchamia się konstruktor klasy bazowej .
    Przekonaliśmy się już, że ten krok jest czwarty: w pierwszych trzech krokach na początku konstruktora Animalwielu zmiennym zostały już przypisane wartości.


  5. Inicjowane są pola niestatyczne klasy potomnej ( ). Dzieje się to przed uruchomieniem konstruktora. Kiedy zaczyna działać, zmienna ogon ma już wartość:Cat
    Cat

    Konstruktor klasy cat został uruchomiony (Konstruktor Animal już gotowy) Aktualna wartość zmiennej statycznej catCount = 37 Aktualna wartość tail = Początkowa wartość tail w klasie Cat


  6. Konstruktor Catklasy potomnej jest wywoływany

    A tak wygląda tworzenie obiektu w Javie!

    Muszę powiedzieć, że nie jesteśmy wielkimi fanami uczenia się na pamięć, ale najlepiej zapamiętać kolejność inicjalizacji zmiennych i wywołań konstruktorów .

    To znacznie poprawi twoje zrozumienie przebiegu programu i stanu twoich obiektów w danym momencie.

    Ponadto wiele klas nie korzysta z dziedziczenia. W takim przypadku kroki związane z klasą podstawową nie mają zastosowania.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION