1 Star 0 Fork 2

Emotion404 / JavaBooks

forked from 帝八哥 / JavaBooks 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
谈谈多态.md 5.14 KB
一键复制 编辑 原始数据 按行查看 历史
DreamCats 提交于 2020-07-28 18:58 . 谈谈String

要说,封装和继承呀,问的比较少,还是多态问的多一点,能体现出来一定的钻研程度。

面试官:咱们聊聊多态?你是怎么理解多态的呀?

我:那我可就要按照我的意思去讲了。

概念

三要素:加黑的地方!

首先我觉得即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

例子

举个例子:

任何事物的多个姿态,多个形态。比如,你说一个猫在吃东西,同样的,你也能说一个动物在吃东西。

public class Test {
    public static void main(String[] args){
        Animal animal = new Cat();
        animal.eat() // 猫也会吃饭
        // 你看到了一只猫,同样它也是动物
        // 比如有很多其他种类继承了动物哈,
        // 当编译期间的animal引用变量,到底指的哪个实例对象,(重要)(主语是引用变量)
        // 或者该引用调用的eat方法,到底是哪个实例对象的eat,编译期间恐怕不知道哦(主语是引用变量)
        // 只有运行期间,哦哦, 原来是猫的eat方法哇...
    }
}

表现形式

所以多态的表现形式:

  • Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但可具有不同的参数列表、返回值类型。调用方法时通过传递的参数类型来决定具体使用哪个方法,这就是多态性。
  • Java的方法重写,是父类与子类之间的多态性,子类可继承父类中的方法,但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。重写的参数列表和返回类型均不可修改。这也是多态性。

底层

首先要说:首先当程序运行需要某个类时,类加载器会将相应的class文件载入到JVM中,并在方法区建立该类的类型信息(包括方法代码,类变量、成员变量以及方法表。(标黑的这个玩意)

面试官:方法表有啥?

我:方法表的结构如同字段表一样,依次包括了访问标志、名称索引、描述符索引、属性表集合几项。

接着回答:方法表是实现动态调用的核心。为了优化对象调用方法的速度,方法区的类型信息会增加一个指针,该指针指向记录该类方法的方法表,方法表中的每一个项都是对应方法的指针

到这里:就要分情况讨论了,一个是方法调用,一个是接口

方法调用

先说方法调用:举个例子

class Person {
    // 重写object的toString
    public String toString(){
        return "I'm a person.";
    }
    public void eat(){}
    public void speak(){}

}

class Boy extends Person{
    // 重写object的toString
    public String toString(){
        return "I'm a boy";
    }
    // 继承Person的speak
    public void speak(){}
    // 自己实现的自定义方法
    public void fight(){}
}

class Girl extends Person{
    // 重写object的toString
    public String toString(){
        return "I'm a girl";
    }
    // 继承Person的speak
    public void speak(){}
    // 自己实现的自定义方法
    public void sing(){}
}

参考

这张图的指向:你可以根据颜色对应上,注意方法表条目指向的具体的方法地址。其次注意蓝色部分其继承自于 Person 的方法 eat() 和 speak() 分别指向 Person 的方法实现和本身的实现。如果子类改写了父类的方法,那么子类和父类的那些同名的方法共享一个方法表项。因此,所有继承父类的子类的方法表中,其父类所定义的方法的偏移量也总是一个定值。Person 或 Object中的任意一个方法,在它们的方法表和其子类 Girl 和 Boy 的方法表中的位置 (index) 是一样的。这样 JVM 在调用实例方法其实只需要指定调用方法表中的第几个方法即可。

调用过程:

  1. 在常量池里找到方法调用的符号引用(肯定先看到Person定义引用类型)
  2. 查看Person的方法表,得到speak方法在该方法表的偏移量(假设为15),这样就得到该方法的直接引用。
  3. 根据this(invoker this字节码)指针得到具体的对象(即 girl 所指向的位于堆中的对象)。
  4. 根据对象得到该对象对应的方法表,根据偏移量15查看有无重写(override)该方法,如果重写,则可以直接调用(Girl的方法表的speak项指向自身的方法而非父类);如果没有重写,则需要拿到按照继承关系从下往上的基类(这里是Person类)的方法表,同样按照这个偏移量15查看有无该方法。

接口调用

一个类可以实现多个接口,那么就像多继承一样,这样的话,在方法表中的索引就会不一样,所以Java 对于接口方法的调用是采用搜索方法表的方式。

参考

马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/Emotion404/JavaBooks.git
git@gitee.com:Emotion404/JavaBooks.git
Emotion404
JavaBooks
JavaBooks
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891