0


15、Java 多态的详细介绍(参考官方教程)

文章目录

前言

初学 Java 的时候,博主觉得多态非常伤脑筋。今天这篇文章将通过官方教程、韩顺平老师、李明杰老师的讲解来学习多态。博主通过他们的视频来学习,但也有自己的想法,并不是照抄 PPT 哦!OK!Let’s study!😊

Java 有三大特性:封装、继承和多态(Polymorphism


一、多态官方教程

✏️ The dictionary 📙 definition of polymorphismrefers to a principle in biology(生物学) in which an organism(有机体) or species(物种) can have many different forms or stages.
在这里插入图片描述

📜 多态的字典定义是:在生物学上,一个有机体或物种可以有许多不同的形式或阶段(形式或状态)。


✏️ This principle can also be applied to object-oriented programming(面向对象编程) and languages like the Java language.

📜 这个原则(生物学的多态性)也可以应用于面向对象编程和编程语言(比如 Java)


✏️ Subclasses of a class can define their own unique behaviors and yet share(共享) some of the same functionality(功能) of the parent class.

📜 一个类的子类可以定义它们自己独特的行为,并且共享一些与父类相同的功能


多态代码案例(不是官方案例,代码按照官方案例编写的)
publicclassPerson{publicString name;publicint age;publicString gender;publicPerson(String name,int age,String gender){this.name = name;this.age = age;this.gender = gender;}publicvoidprintDescription(){System.out.println("\n Person: "+ name +"_"+ age +"_"+ gender);}}/**
 * Student 类继承 Person 类
 */classStudentextendsPerson{privatedouble score;publicStudent(String name,int age,String gender,double score){super(name, age, gender);this.score = score;}publicvoidprintDescription(){System.out.println("\n Student: "+ name +"_"+ age +"_"+ gender +"_"+ score);}}/**
 * Employee 类继承 Person 类
 */classEmployeeextendsPerson{privateint salary;publicEmployee(String name,int age,String gender,int salary){super(name, age, gender);this.salary = salary;}publicvoidprintDescription(){System.out.println("\n Employee: "+ name +"_"+ age +"_"+ gender +"_"+ salary);}}/**
 * 测试类
 */classTestDemo{publicstaticvoidmain(String[] args){Person person, student, employee;
        person =newPerson("张浩男",20,"男");
        student =newStudent("庆医",12,"男",99.5);
        employee =newEmployee("莫松",21,"男",12356);System.out.println("\nperson 的真正形态(类型):"+ person.getClass());System.out.println("student 的真正形态(类型):"+ student.getClass());System.out.println("employee 的真正形态(类型):"+ employee.getClass());// Person: 张浩男_20_男
        person.printDescription();// Student: 庆医_12_男_99.5
        student.printDescription();// Employee: 莫松_21_男_12356
        employee.printDescription();}}

📝 上面的代码中共有四个类:① Person;② Student(继承 Person 类);③ Employee(继承 Person 类);④ TestDemo 类(测试类)
📝 Student 类继承 Person 类,并添加了它独特的属性 score;printDescription 方法被重写,额外增加了打印的内容 score
📝 Employee 类继承 Person 类,并添加了它独特的属性 salary;printDescription 方法被重写,额外增加了打印的内容 salary
📝 TestDemo 类中定义了三个引用变量:person、student 和 employee(

注意:这三个引用变量都是 Person 类型的


📝 三个引用变量分别指向不同的对象:person 指向 Person 对象;student 指向 Student 对象;employee 指向 Employee 对象
📝 但最终打印的结果是:引用变量指向的对象所属于的类里面的 printDescription 的内容,而不是引用变量的类型 Person 类的 printDescription 的内容(这就是多态性:都是 Person 类型的引用,当引用不同的对象的时候所展示的形态是各不相同的)
1   9

✏️ The Java virtual machine (JVM) calls the appropriate method for the object that is referred to in each variable. It does not call the method that is defined by the variable’s type.

📄 Java 虚拟机会调用被变量引用的对象的方法,而不是调用变量的类型的方法。
📄 Java 虚拟机 (JVM) 为每个变量中引用的对象调用适当的方法。它不调用由变量类型定义的方法。


✏️ This behavior is referred to as virtual method invocation(虚方法调用) and demonstrates an aspect of the important polymorphism features in the Java language.

📄 这种行为被称为虚拟方法调用,并展示了 Java 语言中多态特性的一个方面。

在这里插入图片描述
在这里插入图片描述


二、关于多态

✒️ 多态:Polymorphism(具有多种形态)
✒️ 多态:同一类型的引用变量(如 Person),当变量指向(引用)不同的对象后,变量的类型就转变为了指向的对象的类型

✒️ 多态的体现: ① 父类(接口)类型的引用
指向子类对象;② 调用子类重写的方法

多态案例:
publicclassAnimal{publicvoidspeak(){System.out.println("Animal - speak");}}/**
 * Dog 类继承 Animal 类
 */classDogextendsAnimal{publicvoidspeak(){System.out.println("Dog - speak");}}/**
 * Cat 类继承 Animal 类
 */classCatextendsAnimal{publicvoidspeak(){System.out.println("Cat - speak");}}/**
 * 测试类
 */classTestDemo{publicstaticvoidmain(String[] args){// Animal - speakanimalSpeak(newAnimal());// Dog - speakanimalSpeak(newDog());// Cat - speakanimalSpeak(newCat());}privatestaticvoidanimalSpeak(Animal animal){
        animal.speak();}}

📝 JVM 会根据引用变量指向的具体对象调用对应的方法(调用对象中的方法,而不是引用变量类型的方法)
📝 这个行为叫做虚方法调用(virtual method invocation)
📝 类似 C++ 的虚函数调用

三、多态的体现方式

🔑 多态:方法或对象具有多种状态

(1) 方法的多态

方法的【重写】体现出方法的多态:
publicclassFather{publicvoidmakeMoney(){System.out.println("Father - makeMoney()");}}/**
 * Son 类继承 Father 类
 */classSonextendsFather{@OverridepublicvoidmakeMoney(){System.out.println("Son - makeMoney()");}}/**
 * 测试类
 */classTestDemo{publicstaticvoidmain(String[] args){Father father =newFather();Son son =newSon();// Father - makeMoney()
        father.makeMoney();// Son - makeMoney()
        son.makeMoney();}}

📔 上面的代码:Son 类继承 Father 类,并重写了 Father 类中的 makeMoney 方法
📔 用 Father 对象和 Son 对象调用 makeMoney 方法
📔 虽然调用的都是 makeMoney 方法,但实际上调用的不是同一个方法
📔 这里就体现了方法的多态(多种形态)


方法的【重载】体现出方法的多态:
publicclassCalculator{publicintsum(int n1,int n2){return n1 + n2;}publicintsum(int n1,int n2,int n3){return n1 + n2 + n3;}}/**
 * 测试类
 */classTestDemo{publicstaticvoidmain(String[] args){Calculator calculator =newCalculator();// 9System.out.println(calculator.sum(1,8));// 18System.out.println(calculator.sum(1,8,9));}}

📔 Calculator 类中的两个 sum 方法构成重载(因为它们的参数个数不同)
📔 同一个 Calculator 对象调用 sum 方法,当传入不同个数的参数(传两个或三个参数)的时候,返回的结果不一样
📔 调用的都是 Calculator 方法,但因为传入的参个数不同返回类型也不同,这里就体现了方法的多态


(2) 对象的多态

🌼 ① 一个对象的引用的编译类型和运行类型是可以不一致的

🌻 编译类型:使用

javac

命令把 java 文件编译为 class 文件的时候的类型
🌻运行类型:使用

java

命令运行 class 文件的时候

🌼 ② 编译类型在定义引用变量的时候就确定了,不会变化
🌼 ③ 运行类型是可以变化的
🌼 ④ 编译类型看赋值符号的左边;运行类型看赋值符号的右边

🌻 编译类型看赋值符号的左边;运行类型看赋值符号的右边只是一种记忆的方式,大部分时候可以按照这个规律判断。
🌻 实际上,编译类型是引用变量【被定义的类型】;运行类型是引用变量【实际指向的对象的类型】

对象多态案例:
publicclassAnimal{publicvoidspeak(){System.out.println("Animal_speak()");}}classFishextendsAnimal{@Overridepublicvoidspeak(){System.out.println("Fish_speak()");}}classDragonextendsAnimal{@Overridepublicvoidspeak(){System.out.println("Dragon_speak()");}}/**
 * 测试类
 */classTestDemo{publicstaticvoidmain(String[] args){// animal 的编译类型是【Animal】Animal animal;// animal 的运行类型是【Animal】
        animal =newAnimal();// Animal_speak()
        animal.speak();// animal 的运行类型变成了【Fish】
        animal =newFish();// Fish_speak()
        animal.speak();// animal 的运行类型变成了【Dragon】
        animal =newDragon();// Dragon_speak()
        animal.speak();}}

四、向上转型

🌼 多态的前提是:① 类之间存在继承关系;② 类与接口之间存在实现关系

🌼 向上转型:父类类型的h引用指向子类对象

🌻 引用可以调用编译类型中所有的非 private 成员(引用可以调用那些方法看的是编译类型:赋值符号左边的类型)
🌻 引用无法调用运行类型中特有的成员
🌻 最终运行效果看运行类型的具体实现(运行类型有,用运行类型的;否则用编译类型的)

向上转型案例:
publicclassAnimal{publicvoideat(){System.out.println("Animal_eat()");}publicvoiddrink(){System.out.println("Animal_drink()");}publicvoidplay(){System.out.println("Animal_play()");}publicvoidhappy(){System.out.println("Animal_happy()");}privatevoidbitePeople(){System.out.println("Animal_bitePeople()");}}classCatextendsAnimal{@Overridepublicvoidplay(){System.out.println("Cat_play()");}publicvoidcatchMouse(){System.out.println("Cat_catchMouse()");}}/**
 * 测试类
 */classTestDemo{publicstaticvoidmain(String[] args){// animal 是父类类型(Animal)的引用, 它指向了子类(Cat)对象// animal 的编译类型是 Animal; 运行类型是 CatAnimal animal =newCat();/*
            ① 只能调用 Animal(编译类型)中有的 4 个方法, 其中 bitePeople 方
            法是私有的, 不能调用
            ② 运行类型 Cat 中的 catchMouse 方法无法调用
            ③ 若想调用 catchMouse 方法, 只能用 Cat 类型的引用指向 Cat 对象
              或向下转型
         */// Animal_eat()
        animal.eat();// Animal_drink()
        animal.drink();// Cat_play()【运行类型(Cat)中重写了 play 方法,就调用运行类型中的】
        animal.play();// Animal_happy()
        animal.happy();// Cat 类型的引用指向 Cat 对象Cat cat =newCat();// Cat_catchMouse()
        cat.catchMouse();}}

五、向下转型

通过下面的代码引出向下转型:
publicclassAnimal{publicvoidplay(){System.out.println("Animal_play()");}publicvoidhappy(){System.out.println("Animal_happy()");}}classCatextendsAnimal{@Overridepublicvoidplay(){System.out.println("Cat_play()");}publicvoidcatchMouse(){System.out.println("Cat_catchMouse()");}}/**
 * 测试类
 */classTestDemo{publicstaticvoidmain(String[] args){// animal 的编译类型是 Animal; 运行类型是 CatAnimal animal =newCat();// Cat_play()
        animal.play();// Animal_happy()
        animal.happy();}}

✏️ 上面代码中:animal 的编译类型是 Animal,运行类型是 Cat
✏️ 只能调用 Animal 中有的方法(play 和 happy)
✏️ 无法调用 Cat 中独有的方法(catchMouse)
✏️ cat 引用指向的是 Cat 类型的对象,凭什么不能调用 catchMouse 方法呢?若想调用的话,咋个办呢?

📜 虽然 cat 引用指向的是 Cat 类型的对象,但它的编译类型是 Animal 类型。编译器只能识别编译类型中拥有的方法。
📜 若想通过 cat 引用调用 Cat 对象的方法(catchMouse ),可把 cat 引用向下转型为 Cat 类型

📰 cat 引用指向的对象是 Cat 类型,可以被向下转换(强制类型转换)为 Cat 类型
📰 向下转换为 Cat 类型后,运行类型就是 Cat 类型了,就可以调用 Cat 中的 catchMouse 方法了

/**
 * 测试类
 */classTestDemo{publicstaticvoidmain(String[] args){// animal 的编译类型是 Animal; 运行类型是 CatAnimal animal =newCat();// Cat_play()
        animal.play();Cat cat =(Cat) animal;// Cat_catchMouse()
        cat.catchMouse();// Cat_catchMouse()((Cat) animal).catchMouse();}}

☘️ 这个向下转换(强制类型转换)只是改变了引用的指向


六、多态中的属性问题

看下面的代码,思考打印结果:
publicclassFather{publicint num =1;}classSonextendsFather{publicint num =2;}/**
 * 测试类
 */classTestDemo{publicstaticvoidmain(String[] args){Father father =newSon();System.out.println(father.num);Son son =newSon();System.out.println(son.num);}}

☘️ 属性没有重写之说!属性的值直接看编译类型

/**
 * 测试类
 */classTestDemo{publicstaticvoidmain(String[] args){/*
            father 的编译类型是 Father
            所以, father.num 访问得是 Father 类中的 num
         */Father father =newSon();// 1System.out.println(father.num);/*
            son 的编译类型是 Son
            所以, son.num 访问得是 Son 类中的 num
         */Son son =newSon();// 2System.out.println(son.num);}}

七、instanceof 关键字

☘️ instanceof 关键字:判断引用的运行类型是否是某类型或某类型的子类型

publicclassFather{}classSonextendsFather{}classFlower{}/**
 * 测试类
 */classTestDemo{publicstaticvoidmain(String[] args){Father father;
        father =newFather();Son son =newSon();Flower flower =newFlower();System.out.println(father instanceofFather);// trueSystem.out.println(son instanceofFather);// trueSystem.out.println(flower instanceofObject);// true// 下面代码直接报错// System.out.println(flower instanceof Son);}}

八、Exercise

找出下面代码的错误:
publicclassTestDemo{publicstaticvoidmain(String[] args){double d =13.4;long l =(long) d;System.out.println(l);// 13int i =5;// ERRORboolean b =(boolean) i;Object obj ="hello";// 向上转型String objStr =(String) obj;// 向下转型System.out.println(objStr);// helloObject objPri =newInteger(5);// OK (向上转型) String str =(String) objPri;// ERROR (指向 Integer 的指针不能指向 String)// OK (向下转型)Integer str1 =(Integer) objPri;}}

输出下面代码的输出结果:
publicclassFather{int count =10;publicvoiddisplay(){System.out.println(this.count);}}/**
 * Son 类继承 Father 类
 */classSonextendsFather{int count =20;@Overridepublicvoiddisplay(){System.out.println(this.count);}}classMain{publicstaticvoidmain(String[] args){// s 的编译类型是 SonSon s =newSon();// s.count 访问的是 s 的编译类型里面的 countSystem.out.println(s.count);// 20
        s.display();// 20// 父类类型的引用指向子类对象Father f = s;// f 和 s 指向的是同一个对象System.out.println(f == s);// true// f 的编译类型是 Father// f.count 访问得是 f 的编译类型里面的 countSystem.out.println(f.count);// 10
        f.display();// 10}}

这里还涉及到一个特别有意思的重点【动态绑定机制】

,下篇文章看


今天是

今天即是中秋节,也是教师节!祝各位一帆风顺、双喜临门、万事顺心!

🎁 如文章有错误,请不吝赐教!

标签: java jvm 开发语言

本文转载自: https://blog.csdn.net/m0_54189068/article/details/126796676
版权归原作者 new Handsome() 所有, 如有侵权,请联系我们删除。

“15、Java 多态的详细介绍(参考官方教程)”的评论:

还没有评论