Java中多态(方法的重写)是如何实现的

本文探讨了Java中多态的实现,特别是方法重写时,父类引用如何调用子类的方法。通过分析invokevirtual指令的运行时解析过程,解释了在运行时如何根据对象的实际类型找到并调用正确的方法,从而实现动态绑定和多态。重点强调了这是运行时多态,静态类型在编译时确定,实际类型在运行时确定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文参考《深入理解Java虚拟机第三版》8.3章节

首先我们来看下面这段代码:

public class Test1 {
    public static void main(String[] args) {
        People p1=new Man();
        People p2=new Woman();
        p1.say();
        p2.say();
    }
}
class People{
    void say(){
        System.out.println("你好,爷爷");
    }
}
class Man extends People{
    void say(){
        System.out.println("你好,爸爸");
    }
}
class Woman extends People{
    void say() {
        System.out.println("你好,女儿");
    }
}

输出结果:
在这里插入图片描述
对于这个结果大家都不会意外,子类对象赋给父类引用,通过父类引用调用的方法是子类中重写的方法。

那么问题来了,为什么通过父类引用调用方法时会调用子类中定义的方法呢?
很多人是这样理解的:当我们在子类中重写方法时,会将父类中定义的方法覆盖掉,也就是说当执行People p1=new Man();时,只会将子类中定义的方法加载到方法区中。

但是,事实并非如此,创建对象时,父类与子类中定义的方法会依次加载到方法区中。
那么父类的引用是如何定位到子类中重写的方法的呢?
在解答这个问题之前需要先了解一下方法调用指令。实际上,对于不同类型的方法,字节码指令集中设计了不同的调用指令,不同的指令有不同的实现逻辑。Java中默认的方法都是虚方法,也就是可以重写的方法(一般只会Java的人没有虚方法的概念,该概念由C++衍生而来,实际上在Java中我们可以重写的方法都是虚方法),同时还存在其它类型的方法,例如:static方法、final方法。虚方法的调用指令是invokevirtual。

invokevirtual指令的运行时解析过程大致如下:
1,找到引用所指向的对象的实际类型。 //对于People p1=new Man();,其静态类型为People,实际类型为Man。
2,如果在实际类型中找到名称相符的方法,则返回该方法的直接引用,接着调用该方法。 //对于p1.say();,实际调用的是Man中定义的方法
3,如果没找到,按照继承关系从下往上在其各个父类中进行第2步

至此,终于明白,当我们使用父类引用调用方法时,会从引用指向的对象的实际类型中去找该方法。如果引用指向的是不同实际类型的对象,则调用的方法也会不同。多态也因此而实现。
需要注意的是,该多态是运行时多态,对于People p1=new Man();,其静态类型在编译器就已确定,实际类型在编译器无法确定,运行时才确定。
例如:People p3=(new Random()).nextBoolean() ? new Man() : new Woman();,p3的静态类型为People,编译器就可以确定,但其实际类型无法确定,只有程序执行到这行代码时才能确定。

注:本人也在学习阶段,如有误请指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我待Java如初恋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值