Java语言中方法重写的本质

本文深入解析Java中的方法重写与动态分派。动态分派基于对象实际类型确定调用方法,若子类有重写,优先调用子类方法。非法访问错误(IllegalAccessError)发生在尝试访问无权访问的属性或方法时。示例代码展示了在对象初始化过程中方法调用的细节,强调成员变量和方法访问的规则。理解这些概念有助于增强对Java多态性的理解。
摘要由CSDN通过智能技术生成
Java语言中方法重写的本质
  • 找到操作数栈顶的第一个元素所执行的对象的实际类型,记作 C。
  • 如果在类型C中找到与常量中的描述符合简单名称都相符的方法,则进行访问权限校验
    • 如果通过则返回这个方法的直接引用,查找过程结束
    • 如果不通过,则返回java.lang.IllegalAccessError 异常
  • 否则,按照继承关系从下往上依次对C的各个父类进行第2步的搜索和验证过程
    • 如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。

上面这个过程也被称为动态分派:根据对象的实际类型,来确认调用的方法版本,子类有重写方法,则调用子类的重写方法,否则去父类找对应的方法,这就是 invokevirtual 指令的执行逻辑,这是 java 里面对于方法重写的本质。

IllegalAccessError介绍

  • 程序试图访问或修改一个属性或调用一个方法,这个属性或方法,你没有权限访问。一般的,这个会引起编译器异常。这个错误如果发生在运行时,就说明一个类发生了不兼容的改变
  • 比如,你把应该有的jar包放从工程中拿走了,或者Maven中存在jar包冲突
举例
public class FieldHasNoPolymorphic {
    static class Father{
        public int money = 1;
        public Father(){
            money = 2;
            showMeTheMoney();
        }
        public void showMeTheMoney(){
            System.out.println("I am Father, i have $" + money);
        }
    }
    static class Son extends Father{
        public int money = 3;
        public Son(){
            money = 4;
            showMeTheMoney();//第二行的结果
        }
        public void showMeTheMoney(){
            System.out.println("I am Son, i have $" + money);
        }
    }
    public static void main(String[] args){
        Father gay = new Son();
        System.out.println("This gay has $" + gay.money);
    }

}

上面代码的输出结果如下:
在这里插入图片描述

第一行结果的原因
  • 首先进入FieldHasNoPolymorphic 的init 函数,然后开始运行它的main函数。main函数的字节码如下:
    在这里插入图片描述
  • 接下来进入 Son 的init()

在这里插入图片描述

  • 进入 father 的 init():
    然后father里面有 invokevirtual,根据动态分配的原则,首先会选择调用实际类型的重写,方法。因此这里虽然 father已经给money赋值了,但是任然会调用 son 的showmoney 方法。
    在这里插入图片描述
  • 由于此时son里面的money还没有被复制,因此输出了 0 (这个0应该是在编译过程中的JVM类加载阶段为money付的默认值
    )。
第二行结果

正常执行下来,son中的showmoney

第三行结果

下图为 main 函数的字节码
在这里插入图片描述
可以看到最后main函数执行的是 father中的money:
成员变量,静态方法看左边;

总结

访问成员变量的两种方式下的不同规则

  • 直接访问时,查看父类: 直接通过对象名称访问成员变量:看等号左边是哪个对象(子类或父类),则优先用哪个对象的成员变量,没有则向上找。
  • 成员方法中查找当前域: 间接通过成员方法访问成员变量:看该方法属于哪个对象(子类或父类),则优先用哪个对象的方法,没有则向上找。

访问成员方法的规则:
看new的是哪个对象(子类或父类),则优先用哪个对象的成员方法,没有则向上找。

注意:

(1)成员变量指的是父类或子类中的属性,成员方法指的是父类或子类中的方法,如下图:
在这里插入图片描述

(2)只有成员方法才能覆盖重写,成员变量不能覆盖重写。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值