Przeanalizujemy to od początku do końca: jak wywoływane są konstruktory, jak iw jakiej kolejności inicjowane są pola (w tym pola statyczne) itp. Wcześniej poruszyliśmy niektóre kwestie omówione w artykule, więc możesz rzucić okiem materiał na temat konstruktorów klas bazowych . Najpierw przypomnijmy sobie, jak powstaje obiekt. Dobrze pamiętasz, jak ten proces wygląda z punktu widzenia programisty: tworzy klasę, pisze newi wszystko gotowe :) Tutaj porozmawiamy o tym, co dzieje się wewnątrz komputera i maszyny Java, kiedy piszemy, na przykład:
Cat cat = new Cat();
Już o tym rozmawialiśmy, ale na wszelki wypadek przypomnimy:
- Najpierw przydzielana jest pamięć do przechowywania obiektu.
- Następnie maszyna Java tworzy referencję do obiektu (w naszym przypadku referencją jest Cat cat).
- Na koniec inicjowane są zmienne i wywoływany jest konstruktor (przyjrzymy się temu procesowi bardziej szczegółowo).
Te dwa pierwsze punkty nie powinny rodzić żadnych specjalnych pytań. Alokacja pamięci to prosty proces, a możliwe są tylko dwa wyniki: albo jest pamięć, albo jej nie ma :) A tworzenie linku nie jest niczym niezwykłym. Ale trzeci punkt reprezentuje cały zestaw operacji wykonywanych w ściśle określonej kolejności. Nie jestem fanem wkuwania do testów, ale trzeba dobrze zrozumieć ten proces i zapamiętać tę sekwencję działań. Kiedy rozmawialiśmy o procesie tworzenia obiektów na poprzednich lekcjach, tak naprawdę nie wiedziałeś jeszcze nic o dziedziczeniu, więc wyjaśnienie niektórych rzeczy było problematyczne. Teraz już wiesz całkiem sporo i możemy w końcu zająć się tym pytaniem w całości :) A więc trzeci punkt mówi " Na koniec inicjowane są zmienne i wywoływany jest konstruktor ". Ale w jakiej kolejności to wszystko się dzieje? Dla lepszego zrozumienia utwórzmy dwie bardzo proste klasy — rodzica i dziecko:
public class Vehicle {
public static int vehicleCounter = 0;
private String description = "Vehicle";
public Vehicle() {
}
public String getDescription() {
return description;
}
}
public class Truck extends Vehicle {
private static int truckCounter = 0;
private int yearOfManufacture;
private String model;
private int maxSpeed;
public Truck(int yearOfManufacture, String model, int maxSpeed) {
this.yearOfManufacture = yearOfManufacture;
this.model = model;
this.maxSpeed = maxSpeed;
Vehicle.vehicleCounter++;
truckCounter++;
}
}
Klasa Truckjest implementacją ciężarówki z polami reprezentującymi jej rok, model i maksymalną prędkość. Teraz chcemy stworzyć jeden taki obiekt:
public class Main {
public static void main(String[] args) throws IOException {
Truck truck = new Truck(2017, "Scania S 500 4x2", 220);
}
}
Dla maszyny Java proces będzie wyglądał następująco:
-
Pierwszą rzeczą, która się dzieje, jest zainicjowanie zmiennych statycznych klasy
Vehicle. Tak, powiedziałem, żeVehicleklasa, a nieTruck. Zmienne statyczne są inicjowane przed wywołaniem konstruktorów i zaczyna się to w klasie nadrzędnej. Spróbujmy to zweryfikować. UstawiamyvehicleCounterpole wVehicleklasie na 10 i staramy się je wyświetlić zarówno w konstruktorzeVehiclejak iwTruckkonstruktorze.public class Vehicle { public static int vehicleCounter = 10; private String description = "Vehicle"; public Vehicle() { System.out.println(vehicleCounter); } public String getDescription() { return description; } } public class Truck extends Vehicle { private static int truckCount = 0; private int yearOfManufacture; private String model; private int maxSpeed; public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println(vehicleCounter); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCount++; } }Celowo umieściliśmy instrukcję println na samym początku konstruktora,
Truckaby upewnić się, że pola ciężarówki nie zostały jeszcze zainicjowane, gdyvehicleCounterwyświetla się .A oto wynik:
10 10 -
Po zainicjowaniu zmiennych statycznych klasy nadrzędnej, inicjowane są zmienne statyczne klasy potomnej. W naszym przypadku jest to
truckCounterpole klasyTruck.Zróbmy kolejny eksperyment, w którym spróbujemy wyświetlić wartość
truckCounterwewnątrzTruckkonstruktora przed zainicjowaniem innych pól:public class Truck extends Vehicle { private static int truckCounter = 10; private int yearOfManufacture; private String model; private int maxSpeed; public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println(truckCounter); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCounter++; } }Jak widać, wartość 10 została już przypisana do naszej zmiennej statycznej w momencie
Truckstartu konstruktora. -
To jeszcze nie czas na konstruktorów! Inicjalizacja zmiennej jest kontynuowana. Zmienne niestatyczne klasy nadrzędnej są inicjowane jako trzecie. Jak widać, dziedziczenie znacznie komplikuje proces tworzenia obiektu, ale nic na to nie poradzisz: Trzeba tylko zapamiętać pewne rzeczy w programowaniu :)
W ramach eksperymentu możemy przypisać zmiennej
descriptionwVehicleklasie pewną wartość początkową, a następnie zmienić ją w konstruktorze.public class Vehicle { public static int vehicleCounter = 10; private String description = "Initial value of the description field"; public Vehicle() { System.out.println(description); description = "Vehicle"; System.out.println(description); } public String getDescription() { return description; } }Uruchommy naszą
main()metodę, która tworzy ciężarówkę:public class Main { public static void main(String[] args) throws IOException { Truck truck = new Truck(2017, "Scania S 500 4x2", 220); } }Otrzymujemy następujący wynik:
Initial value of the description field VehicleDowodzi to, że gdy
Vehiclekonstruktor zaczyna,descriptionpole ma już przypisaną wartość. -
W końcu przyszedł czas na konstruktorów! Mówiąc dokładniej, nadszedł czas na konstruktor klasy bazowej. Jest ona wywoływana w czwartym kroku procesu tworzenia obiektu.
To też jest dość łatwe do zweryfikowania. Spróbujmy wypisać dwie linie do konsoli: jedną wewnątrz
Vehiclekonstruktora klasy bazowej, drugą wewnątrzTruckkonstruktora. Musimy być przekonani, że linia wewnątrzVehiclejest wyświetlana jako pierwsza:public Vehicle() { System.out.println("Hello from the Vehicle constructor!"); } public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println("Hello from the Truck constructor!"); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCounter++; }Uruchomimy naszą
main()metodę i spojrzymy na wynik:Hello from the Vehicle constructor! Hello from the Truck constructor!Doskonały. To znaczy, że się nie mylimy :) Przejdźmy dalej.
-
Teraz czas na inicjalizację pól niestatycznych klasy potomnej, czyli naszej
Truck. Pola bezpośrednio w tworzonej klasie nie są inicjowane aż do piątego kroku! Zaskakujące, ale prawdziwe :) Ponownie zrobimy proste sprawdzenie — podobnie jak w przypadku klasy nadrzędnej: podamy wartość początkową zmiennej,maxSpeedawTruckkonstruktorze sprawdzimy, czy wartość została przypisana przed uruchomieniem konstruktora:public class Truck extends Vehicle { private static int truckCounter = 10; private int yearOfManufacture; private String model; private int maxSpeed = 150; public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println("Initial value of maxSpeed = " + this.maxSpeed); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCounter++; } }Wyjście konsoli:
Initial value of maxSpeed = 150Jak widać, gdy
Truckkonstruktor startuje,maxSpeedjest już równe 150! -
Konstruktor
Truckklasy potomnej jest wywoływany.I dopiero w tym momencie zostanie wywołany konstruktor klasy, którą tworzymy!
Dopiero w szóstym kroku do pól zostaną przypisane wartości, które przekazujemy jako argumenty do naszej ciężarówki.
Jak widać „konstruowanie” ciężarówki, czyli proces tworzenia obiektu, nie jest proste. Ale wygląda na to, że podzieliliśmy to na najmniejsze części :)
Dlaczego tak ważne jest dobre zrozumienie tego procesu? Wyobraź sobie, jak nieoczekiwane mogłyby być efekty tworzenia zwykłego przedmiotu, gdybyś nie wiedział dokładnie, co dzieje się „pod maską” :) Teraz czas wrócić do kursu i wykonać kilka zadań! Powodzenia i do zobaczenia wkrótce! :)
GO TO FULL VERSION