总结1:从动态绑定到jvm从加载到运行方法的过程.

原文链接:http://www.jianshu.com/p/56a7c4b26b14

提出问题:为什么静态方法不能动态绑定?

public class Demo1 {
    public static void main(String[] args) {
        A a = new B();
        a.a();
    }
}

class A {
    public static void a() {
        System.out.println("父类A的方法a");
    }
}

class B extends A {
    public static void a() {
        System.out.println("子类A的方法a");
    }
}

输出结果: 父类A的方法a

在网上找了很多例子基本说因为静态方法是类相关,和单个对象相关联,或者是静态方法没有重写.仅仅只是将父类被重写的方法隐藏,并不是覆盖.但是并不能彻底解决心中的疑惑.看到转载文章后,对jvm加载类并执行静态方法有了一个清晰的概念.

首先,jvm的运行步骤: 加载 --> 连接(验证,准备,解析) --> 初始化 --> 使用 --> 卸载

当对main方法进行run as时,首先进行对main方法所属类进行加载.跳过连接初始化,开始使用main方法

第一步: 执行A a = new B();

加载B的class文件.并对B进行初始化准备,检查B有父类A,对A的class文件进行加载,并在方法区内分配地址,形成A类的静态数据,并反射形成java.lang.Class对象存储在方法区最上层,代表A类.并能通过A对象访问到该方法区内的数据.并在堆内生成对象A..同理再生成对象B.最后再进行初始化,基于变量有意义的值.

第二步:执行a.a();

jvm提供如下调用字节码的命令

invokestatic:调用静态方法

invokespecial:调用构造方法,私有方法,父类方法

invokeinterface:调用接口方法,在运行时确定一个该接口对应的实例对象

invokedynamic:运行时动态解析真正实例,并运行该实例的方法

也就是a.a()的执行字节码肯定是通过invokedstatic该指令来执行的.该指令只会找引用对应类的静态方法.并执行.静态方法的调用到底结束.

只有重写的非静态方法才会通过invokedynamic指令来执行,并在运行时通过以下步骤来确定执行真正对象的真正方法

  1. 找到操作数栈的栈顶元素所指向的对象的实际类型,记为C;
  2. 如果C中存在描述符和简单名称都相符的方法,则进行访问权限验证,如果验证通过,则直接返回这个方法的直接引用,否则返回java.lang.IllegalAccessError异常;
  3. 如果C中不存在对应的方法,则按照继承关系对C的各个父类进行第2步的操作;
  4. 如果各个父类也没对应的方法,则返回异常;

由于动态绑定的频繁工作,基于性能的考虑,在类的连接阶段,产生一个虚的方法表.如果子类没有重写父类的非静态方法,该子类的方法将直接指向父类实例.这样在进行方法调用的时候,在运行时确定好真正的实例对象,直接通过虚方发表找到对应的方法进行调用.



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值