Maison > Java > JavaBase > Explication détaillée de l'héritage en Java

Explication détaillée de l'héritage en Java

Libérer: 2019-11-25 16:45:12
avant
2307 Les gens l'ont consulté

Explication détaillée de l'héritage en Java

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 {

}
Copier après la connexion

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;
}
Copier après la connexion

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);     // ①
    }
}
Copier après la connexion

[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);
    }
}
Copier après la connexion

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);
}
Copier après la connexion
Copier après la connexion

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);
}
Copier après la connexion
Copier après la connexion

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 :

Explication détaillée de lhéritage en Java

Utilisez le code pour le résumer comme suit :

Shape s = new Shape();
Copier après la connexion
Copier après la connexion

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();
Copier après la connexion
Copier après la connexion

À 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();
        }
    }
}
Copier après la connexion

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
    }
}
Copier après la connexion

如果子类中没有定义覆盖方法,则会调用父类中的方法:

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();
    }
}
Copier après la connexion

【运行结果】:
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();
        }
    }
}
Copier après la connexion

【运行结果】:

Person.eat() 
Person.eat() 
Person.eat() 
Person.eat()
Copier après la connexion

观察结果,对于静态方法而言,不管父类引用指向的什么子类对象,调用的都是父类的方法。

更多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!

Étiquettes associées:
source:cnblogs.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal