Wir werden es von Anfang bis Ende analysieren: wie Konstruktoren aufgerufen werden, wie und in welcher Reihenfolge Felder (einschließlich statischer Felder) initialisiert werden usw. Wir haben bereits einige der im Artikel besprochenen Punkte angesprochen, sodass Sie einen Blick darauf werfen können Das Material zu Basisklassenkonstruktoren . Erinnern wir uns zunächst daran, wie ein Objekt erstellt wird. Sie erinnern sich noch gut daran, wie dieser Prozess aus der Sicht eines Entwicklers aussieht: Er erstellt eine Klasse, schreibt newund alles ist fertig :) Hier sprechen wir darüber, was im Computer und in der Java-Maschine passiert, wenn wir schreiben, zum Beispiel:
Cat cat = new Cat();
Darüber haben wir bereits gesprochen, aber für alle Fälle möchten wir Sie daran erinnern:
- Zunächst wird Speicher zum Speichern des Objekts zugewiesen.
- Als nächstes erstellt die Java-Maschine eine Referenz auf das Objekt (in unserem Fall lautet die Referenz Cat cat).
- Abschließend werden Variablen initialisiert und der Konstruktor aufgerufen (wir werden uns diesen Vorgang genauer ansehen).
Diese ersten beiden Punkte sollten keine besonderen Fragen aufwerfen. Die Speicherzuweisung ist ein einfacher Vorgang und es gibt nur zwei mögliche Ergebnisse: Entweder ist Speicher vorhanden oder nicht :) Und das Erstellen eines Links ist nicht ungewöhnlich. Aber der dritte Punkt stellt eine ganze Reihe von Operationen dar, die in strenger Reihenfolge ausgeführt werden. Ich bin kein Fan von Pauken für Tests, aber Sie müssen diesen Prozess gut verstehen und sich diese Abfolge von Vorgängen merken. Als wir in den vorherigen Lektionen über den Objekterstellungsprozess gesprochen haben, wussten Sie noch nicht wirklich etwas über Vererbung, daher war es problematisch, einige Dinge zu erklären. Jetzt wissen Sie ziemlich viel und wir können uns dieser Frage endlich ausführlich widmen :) Der dritte Punkt lautet also: „ Zuletzt werden Variablen initialisiert und der Konstruktor aufgerufen. “ Aber in welcher Reihenfolge geschieht das alles? Zum besseren Verständnis erstellen wir zwei supereinfache Klassen – eine übergeordnete und eine untergeordnete Klasse:
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++;
}
}
Die TruckKlasse ist eine Implementierung eines Lkw mit Feldern, die sein Jahr, sein Modell und seine Höchstgeschwindigkeit darstellen. Nun wollen wir ein solches Objekt erstellen:
public class Main {
public static void main(String[] args) throws IOException {
Truck truck = new Truck(2017, "Scania S 500 4x2", 220);
}
}
Für die Java-Maschine sieht der Prozess folgendermaßen aus:
-
Als Erstes werden die statischen Variablen der
VehicleKlasse initialisiert . Ja, ich sagte dieVehicleKlasse, nichtTruck. Statische Variablen werden vor dem Aufruf von Konstruktoren initialisiert, und dies beginnt in der übergeordneten Klasse. Versuchen wir, dies zu überprüfen. Wir setzen dasvehicleCounterFeld in der Klasse auf 10 und versuchen, es sowohl im Konstruktor als auchVehicleanzuzeigen .VehicleTruckpublic 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++; } }Wir haben die println-Anweisung bewusst ganz am Anfang des
TruckKonstruktors platziert, um sicherzustellen, dass die Felder des LKWs noch nicht initialisiert wurden, wennvehicleCountersie angezeigt wird.Und hier ist das Ergebnis:
10 10 -
Nachdem die statischen Variablen der übergeordneten Klasse initialisiert wurden, werden die statischen Variablen der untergeordneten Klasse initialisiert. In unserem Fall ist dies das
truckCounterFeld derTruckKlasse.Führen wir ein weiteres Experiment durch, bei dem wir versuchen, den Wert von
truckCounterimTruckKonstruktor anzuzeigen, bevor die anderen Felder initialisiert werden: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++; } }Wie Sie sehen können, wurde unserer statischen Variablen zu Beginn des Konstruktors bereits der Wert 10 zugewiesen
Truck. -
Es ist noch nicht die Zeit für die Konstrukteure! Die Variableninitialisierung wird fortgesetzt. Die nicht statischen Variablen der übergeordneten Klasse werden als drittes initialisiert. Wie Sie sehen, verkompliziert die Vererbung den Prozess der Objekterstellung erheblich, aber Sie können nichts dagegen tun: Sie müssen sich nur einige Dinge beim Programmieren merken :)
descriptionAls Experiment können wir der Variablen in der Klasse einen Anfangswert zuweisenVehicleund ihn dann im Konstruktor ändern.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; } }Lassen Sie uns unsere
main()Methode ausführen, die einen LKW erstellt:public class Main { public static void main(String[] args) throws IOException { Truck truck = new Truck(2017, "Scania S 500 4x2", 220); } }Wir erhalten folgendes Ergebnis:
Initial value of the description field VehicleDies beweist, dass
Vehicledem Feld zu Beginn des Konstruktorsdescriptionbereits ein Wert zugewiesen wurde. -
Endlich ist es Zeit für die Konstrukteure! Genauer gesagt ist es Zeit für den Basisklassenkonstruktor. Es wird im vierten Schritt des Objekterstellungsprozesses aufgerufen.
Dies lässt sich auch relativ einfach überprüfen. Versuchen wir, zwei Zeilen an die Konsole auszugeben: eine im
VehicleBasisklassenkonstruktor und die zweite imTruckKonstruktor. Wir müssen davon überzeugt sein, dass die Zeile darinVehiclezuerst angezeigt wird: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++; }Wir führen unsere
main()Methode aus und schauen uns das Ergebnis an:Hello from the Vehicle constructor! Hello from the Truck constructor!Exzellent. Das heißt, wir irren uns nicht :) Machen wir weiter.
-
Jetzt ist es Zeit für die Initialisierung der nicht statischen Felder der untergeordneten Klasse, also unserer
TruckKlasse. Die Felder unmittelbar innerhalb der zu instanziierenden Klasse werden erst im fünften Schritt initialisiert! Überraschend, aber wahr :) Auch hier führen wir eine einfache Prüfung durch – genau wie bei der übergeordneten Klasse: Wir geben der Variablen einen Anfangswert und prüfenmaxSpeedim Konstruktor, ob der Wert zugewiesen wurde, bevor der Konstruktor gestartet wurde:Truckpublic 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++; } }Konsolenausgabe:
Initial value of maxSpeed = 150Wie Sie sehen können, beträgt der Wert beim Start des
TruckKonstruktors bereits 150!maxSpeed -
Der Konstruktor der
Truckuntergeordneten Klasse wird aufgerufen.Und erst an dieser Stelle wird als letztes der Konstruktor der Klasse aufgerufen, die wir instanziieren!
Erst im sechsten Schritt werden den Feldern die Werte zugewiesen, die wir als Argumente an unseren LKW übergeben.
Wie Sie sehen, ist das „Konstruieren“ eines Lkw, also der Objekterstellungsprozess, nicht einfach. Aber es scheint, dass wir es in die kleinsten Teile zerlegt haben :)
Warum ist es so wichtig, diesen Prozess gut zu verstehen? Stellen Sie sich vor, wie unerwartet die Ergebnisse beim Erstellen eines gewöhnlichen Objekts sein könnten, wenn Sie nicht genau wüssten, was „unter der Haube“ passiert :) Jetzt ist es an der Zeit, zum Kurs zurückzukehren und einige Aufgaben zu erledigen! Viel Glück und bis bald! :) :)
GO TO FULL VERSION