Concepts de base de l'héritage et de la synthèse Java
Héritage : vous pouvez construire une nouvelle classe basée sur une classe existante . Hériter des classes existantes permet de réutiliser les méthodes et les champs de ces classes. Sur cette base, de nouvelles méthodes et champs peuvent être ajoutés, élargissant ainsi les fonctionnalités de la classe.
Synthèse : La création d'un objet original dans une nouvelle classe s'appelle la composition. De cette façon, vous pouvez réutiliser le code existant sans changer sa forme.
Tutoriels vidéo associés recommandés : Tutoriel vidéo Java
1. Syntaxe héritée
Le mot-clé extends indique qu'une nouvelle classe. est dérivé d’une classe existante. La classe existante est appelée classe parent ou classe de base, et la nouvelle classe est appelée sous-classe ou classe dérivée. Par exemple :
class Student extends Person { }
La classe Student hérite de Person. La classe Person est appelée classe parent ou classe de base, et la classe Student est appelée sous-classe ou classe dérivée.
2. Syntaxe de synthèse
La synthèse est relativement simple, elle consiste à créer une classe existante au sein d'une classe.
class Student { Dog dog; }
Modélisation mise à jour
Concepts de base
Le rôle de l'héritage réside dans le duplication de l’utilisation du code. Puisque l'héritage signifie que toutes les méthodes de la classe parent peuvent également être utilisées dans la classe enfant, les messages envoyés à la classe parent peuvent également être envoyés à la classe dérivée. S'il existe une méthode eat dans la classe Person, alors il y aura également cette méthode dans la classe Student, ce qui signifie que l'objet Student est également un type de Person.
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
La méthode show définie dans Person est utilisée pour recevoir le handle Person, mais ce qui est reçu en ① est une référence à l'objet Student. En effet, l'objet Student est également un objet Person. Dans la méthode show, le handle entrant (référence d’objet) peut être un objet Person et un objet de classe dérivé d’une Personne. Ce comportement de conversion d’un handle d’étudiant en handle de personne est appelé conversion ascendante.
2. Pourquoi devons-nous retracer la forme
Pourquoi ignorons-nous intentionnellement le type d'objet qui l'appelle lors de l'appel de eat ? Cela semble plus intuitif et plus facile à comprendre si la méthode show obtient simplement le handle Student, mais cela nécessiterait que chaque nouvelle classe dérivée de la classe Person implémente sa propre méthode show :
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); } }
Cette approche est évidente. L'inconvénient est que des méthodes étroitement liées doivent être définies pour chaque classe dérivée de la classe Person, ce qui entraîne beaucoup de code en double. En revanche, si vous oubliez de surcharger une méthode, aucune erreur ne sera signalée. Les trois méthodes show de l'exemple ci-dessus peuvent être combinées en une seule :
public static void show(Person p) { p.eat(Value.v1); }
Liaison dynamique
Lors de l'exécution de show(s), La sortie Le résultat est Student.eat(), qui est bien le résultat souhaité, mais il ne semble pas être exécuté sous la forme souhaitée. Jetons un coup d'œil à la méthode show :
public static void show(Person p) { p.eat(Value.v1); }
Elle reçoit le handle Person. . Lorsqu'il est exécuté lors de show(s), comment sait-il que le handle Person pointe vers un objet Student au lieu d'un objet Teacher ? Le compilateur n'a aucun moyen de le savoir, ce qui implique le problème de liaison expliqué ensuite.
1. Liaison des appels de méthode
La connexion d'une méthode avec un corps de méthode est appelée liaison. Si la liaison est effectuée avant l'exécution, elle est appelée « liaison anticipée ». Dans l’exemple ci-dessus, lorsqu’il n’y a qu’un seul handle Person, le compilateur ne sait pas quelle méthode appeler. Java implémente un mécanisme d'appel de méthode qui peut déterminer le type d'un objet pendant l'exécution, puis appeler la méthode correspondante. Cette liaison, effectuée pendant l'exécution et basée sur le type de l'objet, est appelée liaison dynamique. Sauf si une méthode est déclarée finale, toutes les méthodes Java sont liées dynamiquement.
Utilisez une image pour représenter la relation d'héritage de la modélisation en amont :
Utilisez le code pour le résumer comme suit :
Shape s = new Shape();
Selon la relation d'héritage, créer Il est légal d'attribuer le handle d'objet Circle à une forme car Circle est un type de forme.
Lorsque l'une des méthodes de la classe de base est appelée :
Shape s = new Shape();
À ce moment, Circle.draw() est appelée, ce qui est dû à une liaison dynamique.
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 établit une interface commune pour toutes les classes dérivées de Person, et toutes les classes dérivées ont deux comportements : manger et parler. Les classes dérivées remplacent ces définitions, redéfinissant les deux comportements.
Dans la classe principale, randPerson sélectionne aléatoirement le handle de l'objet Person. **La mise en forme de l'appel se produit dans l'instruction return. L'instruction **return obtient un handle Boy ou Girl et le renvoie en tant que type Person. Pour le moment, nous ne connaissons pas le type spécifique, nous savons seulement qu'il s'agit d'un handle d'objet Person.
Appelez la méthode randPerson dans la méthode main pour remplir l'objet Person dans le tableau, mais je ne connais pas la situation spécifique. Lorsque la méthode eat de chaque élément du tableau est appelée, le rôle de la liaison dynamique est d'exécuter la méthode redéfinie de l'objet.
Cependant, la liaison dynamique a un prérequis. La méthode de liaison doit exister dans la classe de base, sinon elle ne peut pas être compilée.
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基础教程栏目。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!