Grundlegende Konzepte der Java-Vererbung und -Synthese
Vererbung: Sie können eine neue Klasse basierend auf einer vorhandenen Klasse erstellen . Durch die Vererbung vorhandener Klassen können Sie die Methoden und Felder dieser Klassen wiederverwenden. Auf dieser Basis können neue Methoden und Felder hinzugefügt und so die Funktionalität der Klasse erweitert werden.
Synthese: Das Erstellen eines Originalobjekts in einer neuen Klasse wird als Komposition bezeichnet. Auf diese Weise können Sie vorhandenen Code wiederverwenden, ohne seine Form zu ändern.
Empfohlene verwandte Video-Tutorials: Java-Video-Tutorial
1. Geerbte Syntax
Das Schlüsselwort „extends“ gibt an, dass es sich um eine neue Klasse handelt wird von einer vorhandenen Klasse abgeleitet. Die vorhandene Klasse wird als übergeordnete Klasse oder Basisklasse bezeichnet, und die neue Klasse wird als Unterklasse oder abgeleitete Klasse bezeichnet. Beispiel:
class Student extends Person { }
Die Klasse Student erbt Person. Die Klasse Person wird als übergeordnete Klasse oder Basisklasse bezeichnet, und die Klasse Student wird als Unterklasse oder abgeleitete Klasse bezeichnet.
2. Synthesesyntax
Die Synthese ist relativ einfach, nämlich das Erstellen einer vorhandenen Klasse innerhalb einer Klasse.
class Student { Dog dog; }
Aktualisierte Modellierung
1. Grundkonzepte
Die Rolle der Vererbung liegt in der Duplizierung der Codenutzung. Da durch Vererbung alle Methoden der übergeordneten Klasse auch in der untergeordneten Klasse verwendet werden können, können an die übergeordnete Klasse gesendete Nachrichten auch an die abgeleitete Klasse gesendet werden. Wenn es in der Person-Klasse eine Eat-Methode gibt, gibt es diese Methode auch in der Student-Klasse, was bedeutet, dass das Student-Objekt ebenfalls ein Typ von Person ist.
class Person { public void eat() { System.out.println("eat"); } static void show(Person p) { p.eat(); } } public class Student extends Person{ public static void main(String[] args) { Student s = new Student(); Person.show(s); // ① } }
[Run result]:
eat
Die in Person definierte Show-Methode wird verwendet, um das Person-Handle zu empfangen, aber was bei ① empfangen wird, ist ein Verweis auf das Student-Objekt. Dies liegt daran, dass das Student-Objekt auch ein Person-Objekt ist. In der Show-Methode kann das eingehende Handle (Objektreferenz) ein Person-Objekt und ein von Person abgeleitetes Klassenobjekt sein. Dieses Verhalten beim Konvertieren eines Student-Handles in ein Person-Handle wird als Upcasting bezeichnet.
2. Warum müssen wir die Form zurückverfolgen?
Warum ignorieren wir absichtlich den Objekttyp, der sie aufruft, wenn wir eat aufrufen? Es erscheint intuitiver und leichter zu verstehen, wenn die Show-Methode einfach das Student-Handle erhält, aber das würde erfordern, dass jede neue von der Person-Klasse abgeleitete Klasse ihre eigene Show-Methode implementiert:
class Value { private int count = 1; private Value(int count) { this.count = count; } public static final Value v1 = new Value(1), v2 = new Value(2), v3 = new Value(3); } class Person { public void eat(Value v) { System.out.println("Person.eat()"); } } class Teacher extends Person { public void eat(Value v) { System.out.println("Teacher.eat()"); } } class Student extends Person { public void eat(Value v) { System.out.println("Student.eat()"); } } public class UpcastingDemo { public static void show(Student s) { s.eat(Value.v1); } public static void show(Teacher t) { t.eat(Value.v1); } public static void show(Person p) { p.eat(Value.v1); } public static void main(String[] args) { Student s = new Student(); Teacher t = new Teacher(); Person p = new Person(); show(s); show(t); show(p); } }
Dieser Ansatz hat offensichtlich einen Nachteil besteht darin, dass für jede abgeleitete Klasse der Person-Klasse eng verwandte Methoden definiert werden müssen, was zu einer Menge doppeltem Code führt. Wenn Sie hingegen vergessen, eine Methode zu überladen, wird kein Fehler gemeldet. Die drei Show-Methoden im obigen Beispiel können zu einer kombiniert werden:
public static void show(Person p) { p.eat(Value.v1); }
Dynamische Bindung
Beim Ausführen von Show(s) Die Ausgabe Das Ergebnis ist Student.eat(), was zwar das gewünschte Ergebnis ist, aber es scheint nicht in der von uns gewünschten Form ausgeführt zu werden. Schauen wir uns die Show-Methode an:
public static void show(Person p) { p.eat(Value.v1); }
Sie erhält das Person-Handle . Woher weiß es bei der Ausführung von When show(s), dass das Person-Handle auf ein Student-Objekt und nicht auf ein Teacher-Objekt verweist? Der Compiler hat keine Möglichkeit zu wissen, was das als nächstes erläuterte Bindungsproblem betrifft.
1. Bindung von Methodenaufrufen
Das Verbinden einer Methode mit einem Methodenkörper wird als Bindung bezeichnet. Wenn die Bindung vor der Ausführung erfolgt, spricht man von „früher Bindung“. Wenn im obigen Beispiel nur ein Personenhandle vorhanden ist, weiß der Compiler nicht, welche Methode er aufrufen soll. Java implementiert einen Methodenaufrufmechanismus, der den Typ eines Objekts während der Laufzeit ermitteln und dann die entsprechende Methode aufrufen kann. Diese Bindung, die während der Laufzeit durchgeführt wird und auf dem Typ des Objekts basiert, wird als dynamische Bindung bezeichnet. Sofern eine Methode nicht als endgültig deklariert wird, sind alle Methoden in Java dynamisch gebunden.
Verwenden Sie ein Bild, um die Vererbungsbeziehung der Upstream-Modellierung darzustellen:
Es kann im Code wie folgt zusammengefasst werden:
Shape s = new Shape();
Gemäß Erstellen Sie die Vererbungsbeziehung. Es ist zulässig, das Objekthandle „Kreis“ einer Form zuzuweisen, da „Kreis“ ein Formtyp ist.
Wenn eine der Basisklassenmethoden aufgerufen wird:
Shape s = new Shape();
Zu diesem Zeitpunkt wird Circle.draw() aufgerufen, was auf die dynamische Bindung zurückzuführen ist.
class Person { void eat() {} void speak() {} } class Boy extends Person { void eat() { System.out.println("Boy.eat()"); } void speak() { System.out.println("Boy.speak()"); } } class Girl extends Person { void eat() { System.out.println("Girl.eat()"); } void speak() { System.out.println("Girl.speak()"); } } public class Persons { public static Person randPerson() { switch ((int)(Math.random() * 2)) { default: case 0: return new Boy(); case 1: return new Girl(); } } public static void main(String[] args) { Person[] p = new Person[4]; for (int i = 0; i < p.length; i++) { p[i] = randPerson(); // 随机生成Boy或Girl } for (int i = 0; i < p.length; i++) { p[i].eat(); } } }
Person richtet eine gemeinsame Schnittstelle für alle von Person abgeleiteten Klassen ein und alle abgeleiteten Klassen haben zwei Verhaltensweisen: Essen und Sprechen. Abgeleitete Klassen überschreiben diese Definitionen und definieren beide Verhaltensweisen neu.
In der Hauptklasse wählt randPerson zufällig das Handle des Person-Objekts aus. **Die Einspruchsformung erfolgt in der Return-Anweisung. Die **return-Anweisung ruft ein Boy- oder Girl-Handle ab und gibt es als Person-Typ zurück. Derzeit kennen wir den spezifischen Typ nicht, wir wissen nur, dass es sich um ein Person-Objekt-Handle handelt.
Rufen Sie die randPerson-Methode in der Hauptmethode auf, um das Person-Objekt in das Array einzufügen, aber ich kenne die spezifische Situation nicht. Wenn die Eat-Methode jedes Elements des Arrays aufgerufen wird, besteht die Rolle der dynamischen Bindung darin, die neu definierte Methode des Objekts auszuführen.
Für die dynamische Bindung ist jedoch eine Voraussetzung erforderlich. Die Bindungsmethode muss in der Basisklasse vorhanden sein, andernfalls kann sie nicht kompiliert werden.
class Person { void eat() { System.out.println("Person.eat()"); } } class Boy extends Person { void eat() { System.out.println("Boy.eat()"); } void speak() { System.out.println("Boy.speak()"); } } public class Persons { public static void main(String[] args) { Person p = new Boy(); p.eat(); p.speak(); // The method speak() is undefined for the type Person } }
如果子类中没有定义覆盖方法,则会调用父类中的方法:
class Person { void eat() { System.out.println("Person.eat()"); } } class Boy extends Person { } public class Persons { public static void main(String[] args) { Person p = new Boy(); p.eat(); } }
【运行结果】:
Person.eat()
2.静态方法的绑定
将上面的方法都加上static关键字,变成静态方法:
class Person { static void eat() { System.out.println("Person.eat()"); } static void speak() { System.out.println("Person.speak()"); } } class Boy extends Person { static void eat() { System.out.println("Boy.eat()"); } static void speak() { System.out.println("Boy.speak()"); } } class Girl extends Person { static void eat() { System.out.println("Girl.eat()"); } static void speak() { System.out.println("Girl.speak()"); } } public class Persons { public static Person randPerson() { switch ((int)(Math.random() * 2)) { default: case 0: return new Boy(); case 1: return new Girl(); } } public static void main(String[] args) { Person[] p = new Person[4]; for (int i = 0; i < p.length; i++) { p[i] = randPerson(); // 随机生成Boy或Girl } for (int i = 0; i < p.length; i++) { p[i].eat(); } } }
【运行结果】:
Person.eat() Person.eat() Person.eat() Person.eat()
观察结果,对于静态方法而言,不管父类引用指向的什么子类对象,调用的都是父类的方法。
更多java相关文章请关注java基础教程栏目。
Das obige ist der detaillierte Inhalt vonDetaillierte Erklärung der Vererbung in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!