栈帧是一种用于帮助虚拟机执行方法调用和方法执行的数据结构。
栈帧本身是一种数据结构,封装了方法的局部变量表、动态链接信息、方法的返回地址以及操作数栈等信息。
方法调用字节码指令
- invokeinterface 调用接口中的方法,实际上是在运行期决定的,决定到底调用哪个实现该接口的对象的特定的方法。
- invokestatis 调用静态方法
- invokespecial 调用自己的私有方法、构造方法(<init>)以及调用父类的方法
- invokevirtual 调用虚方法,运行期动态查找
- invokedynamic 动态调用方法
- 静态方法
- 父类方法
- 构造方法
- 私有方法
以上四类方法称为非虚方法,他们是在类加载阶段就可以将符号引用转换为直接引用
方法的静态分派
public class MyTest1 {
public void test(Grandpa grandpa){
System.out.println("Grandpa");
}
public void test(Father father){
System.out.println("father");
}
public void test(Son son){
System.out.println("son");
}
public static void main(String[] args) {
Grandpa g1 = new Father();
Grandpa g2 = new Son();
MyTest1 test = new MyTest1();
test.test(g1);
test.test(g2);
}
}
class Grandpa{ }
class Father extends Grandpa{ }
class Son extends Father{ }
上述代码输出
Grandpa
Grandpa
因为g1的静态类型是Grandpa,而g1的实际类型是Fatcher。所以变量的静态类型是不会发生变化的,而变量的实际类型则是可以发生变化的(多态的一种体现),实际类型是在运行期才能确定。并且方法的重载是一种静态行为。
方法的动态分派
public class MyTest2 {
public static void main(String[] args) {
Fruti apple = new Apple();
Fruti orange = new Orange();
apple.test();
orange.test();
apple = new Orange();
apple.test();
}
}
class Fruti{
public void test(){
System.out.println("fruit");
}
}
class Apple extends Fruti{
@Override
public void test() {
System.out.println("Apple");
}
}
class Orange extends Fruti{
@Override
public void test() {
System.out.println("Orange");
}
}
上述代码输出
Apple
Orange
Orange
方法的动态分派涉及到一个重要的概念:方法的接收者
网友评论