04. 方法调用指令

46 阅读2分钟

引子

在学习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类(除非它们继承了其他类)。

方法指令介绍

image-20240814134908035.png

能否回答一个问题,为什么需要不同的invoke指令来调用不同的方法?

我觉得最关键的一点就是效率。

例如:静态方法和普通方法不同,关联的是类的class对象,而非实例对象。静态方法的调用不涉及对象引用。JVM通过类名找到方法区中的类信息。。因此使用Invokestatic可以和其它方法区分。

而:私有方法和普通实例方法不同,因为普通实例方法可以被重写,私有方法无法重写,没有多态性,因此无需通过多态访问。