一、方法调用
直接分析下面案例代码:
public class Demo5 {
public Demo5() {
}
private void test1() {
}
private final void test2() {
}
public void test3() {
}
public static void test4() {
}
public static void main(String[] args) {
Demo5 demo5 = new Demo5();
demo5.test1();
demo5.test2();
demo5.test3();
Demo5.test4();
}
}
1、JVM底层对不同方法,有不同的实现方式
在JVM底层中不同的方法在调用的时候,对应的虚拟机指令也不同
-
私有(private)、构造方法、final修饰的方法:
在调用的时候都是使用
invokespecial
指令。 -
普通成员方法:
使用
invokespecial
指令。因为编译期间 无法确定 该方法的内容,只有在运行期间才能确定。 -
静态方法:
在调用时使用
invokestatic
指令。
Code:
stack=2, locals=2, args_size=1
0: new #2 // class com/nyima/JVM/day5/Demo5
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokespecial #4 // Method test1:()V
12: aload_1
13: invokespecial #5 // Method test2:()V
16: aload_1
17: invokevirtual #6 // Method test3:()V
20: invokestatic #7 // Method test4:()V
23: return
-
第一条指令
new
:创建一个【对象】,给【对象】分配内存空间,执行成功会将 【对象引用】压入操作数栈。 -
第二条指令
dup
:复制操作数栈栈顶的内容,这里复制的就是这个对象引用,那么为什么需要需要复制呢?因为调用方法前,首先需要使用
invokespecial
调用init( )V
方法,这里需要消耗掉栈顶的一份对象引用。另一份需要要 配合astore_1
赋值给局部变量。 -
终方法(final),私有方法(private),构造方法都是由
invokespecial
指令来调用,属于静态绑定。 -
普通成员方法是由
invokevirtual
调用,属于动态绑定,即支持多态。 成员方法与静态方法调用的另一个区别是,执行方法前是否需要【对象引用】。
二、多态原理
因为普通成员方法需要在运行时才能确定具体的内容,所以虚拟机需要调用invokevirtual
指令,即支持动态绑定,支持多态。
在执行invokevirtual
指令时,经历了以下几个步骤:
- 先通过栈帧中对象的引用找到对象。
- 分析对象头,找到对象的实际对象。
- Class结构中有vtable。
- 查询vtable找到方法的具体地址。
- 执行方法的字节码。
引用黑马JVM课程。