详解 JVM 字节码(6)重载
2019-05-04 本文已影响32人
zidea
jvm
字节码
栈帧
【图】
-
栈
-
帧
我们程序就是一个交互的电影
每一个线程都会有一个栈帧,栈帧本身是一种数据结构,封装了方法的局部变量表、动态链接信息、方法的返回值以及。在 JVM 调用方法无论方法返回值还是抛出异常都一定要返回到调用方。 -
动态链接
其实 class 文件是源代码经过编译后得到的字节码,学过编译原理都会知道,这个仅仅完成了一半的工作(词法分析、语法分析、语义分析、中间代码生成),接下来就是实际的运行了。而 Java 选择的是动态链接的方式,只有在用到某个类时(可能是类加载时候或者真正调用类的时候)才会将其加载进内存,而不是像C++那样使用静态链接:将所有类加载,不论是否使用到。 -
符号引用:在常量池中 A 类保存 B 类的唯一个名字,当调用到 B 类的符号引用转换为直接引用。
-
直接引用
- invokeinterface 调用接口在运行期决定的到底调用实现该接口的哪一个对象的特定方法
- invokestatic : 调用静态方法
- invokespecial : 调用自己私有方法和构造方法<int>,调用父类的方法
- invokevirtual : 调用虚方法,和多态紧密相关的,也是运行动态查找的过程。
- invokedanimc : 动态调用方法
public class ClientA {
public static void test(){
System.out.println("test invoked");
}
public static void main(String[] args) {
test();
}
}
静态调用
- invokestaic 调用 ClientA 的 test 静态方法
- 能够被 invokespecial 和 invokestatic 调用方法都是在解析阶段就可以确定。包括下面这些方法。
- 父类方法
- 静态方法
- 私有方法
- 构造方法
以上 4 中情况为静态解析。为什么公有方法不是呢?因为公有方法可能被复写所以不能确定,以上 4 种方法也称非虚方法。
class A {}
class B extends A{}
class C extends B{}
public class ClientB {
public void print(A a){
System.out.println("A class");
}
public void print(B b){
System.out.println("B class");
}
public void print(C c){
System.out.println("C class");
}
public static void main(String[] args) {
A b = new B();
A c = new C();
ClientB clientB = new ClientB();
clientB.print(b);
clientB.print(c);
}
}
A class
A class
方法的静态分派,用于 A 声明了 b ,所以 A 就是 b 的静态类型,而 b 的实际类型是 B 类型(也就是 b 的指向类型)。
b 的静态类型和实际类型,静态类型是不会改变的。即使强转也不会改变的 b 的静态类型。而实际类型是可以发生变化。这也是 JVM 对 java 多态实现的底层支持。
调用 print 的对象 clientB 这是在编译中已经确定,方法的重载对于 JVM 是一种静态行为,根据对象的静态类型来决定调用哪一个对应的方法。
字节码