引子
在学习03.synchronized的原理时,反编译获取锁的字节码后,发现有意思的一项:
Compiled from "InjectTest.java"
class InjectTest {
InjectTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #7 // class InjectTest
2: dup
3: astore_1
4: monitorenter
5: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #15 // String hello!
10: invokevirtual #17 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: aload_1
14: monitorexit
15: goto 23
18: astore_2
19: aload_1
20: monitorexit
21: aload_2
22: athrow
23: return
Exception table:
from to target type
5 15 18 any
18 21 18 any
这里面的invokespecial #1和invokevirtual #17 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
分别是调用父类object的构造函数,以及PrintStream类的println函数。
很好奇的一点是:为什么都是方法,需要用不同的invoke指令来执行呢?
补充解释:
假设你有一个简单的类,没有定义任何构造函数:
public class MyClass {
// 没有定义构造函数
}
编译器会为这个类生成以下代码:
public class MyClass {
public MyClass() {
super(); // 调用父类Object的无参构造函数
}
}
在这种情况下,super() 调用的是Object类的无参构造函数,因为所有类默认都继承自Object类(除非它们继承了其他类)。
方法指令介绍
能否回答一个问题,为什么需要不同的invoke指令来调用不同的方法?
我觉得最关键的一点就是效率。
例如:静态方法和普通方法不同,关联的是类的class对象,而非实例对象。静态方法的调用不涉及对象引用。JVM通过类名找到方法区中的类信息。。因此使用Invokestatic可以和其它方法区分。
而:私有方法和普通实例方法不同,因为普通实例方法可以被重写,私有方法无法重写,没有多态性,因此无需通过多态访问。