文章目录
- invokespecial
- invokevirtual
- invokestatic
- invokeinterface
- invokedynamic
java里的五大invoke指令分别是invokespecial、invokevirtual、invokestatic、invokeinterface、invokedynamic。
静态方法的调用,生成的是invokestatic指令。
接口方法的调用,生成的是invokeinterface指令。
其他的方法,一般生成invokevirtual指令,尽管final方法不可能被继承覆盖重写,但还是生成invokevirtual指令。
调用构造器是生成invokespecial指令,此外super关键字方法调用,也生成invokespecial指令。
最复杂的当属于invokedynamic指令了,其目的是实现动态类型。众所周知,java是一种静态类型语言。举个例子,python可以这样写代码:
class A:
def print(self):
print('A')
class B:
def print(self):
print('B')
def f(x):
x.print()
if __name__ == '__main__':
# 取命令行参数
f(A())
f(B())
如果换成java,这两个类就必须实现同一个接口。但是现在java也可以了,JDK7引入的invokedynamic指令就可以实现这种动态类型,但是JDK7的java语法并不支持,只有JVM上动态语言比如closure、kotlin、Scala等语言提供了支持。但是到了JDK8,lambda表达式的出现使得java编译器可以生成invokedynamic指令。这个指令底层依靠methodhandle来实现,其实现原理超级复杂。但简而言之,就是把callsite传给invokedynamic指令,从call site中找到bootstrap method来调用真正的方法。
invokespecial
我举个例子:
package com.youngthing.matrix.test.image;
/**
* 11/22/2022 9:35 AM 创建
*
* @author 花书粉丝
*/
public final class InvokeSpecialTest {
public void test() {
super.hashCode();
}
}
使用javap -c反编译后的结果如下:
Compiled from "InvokeSpecialTest.java"
public final class com.youngthing.matrix.test.image.InvokeSpecialTest {
public com.youngthing.matrix.test.image.InvokeSpecialTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void test();
Code:
0: aload_0
1: invokespecial #2 // Method java/lang/Object.hashCode:()I
4: pop
5: return
}
反编译里,#2代表常量池里的第二条,这里代表java/lang/Object.hashCode:()I这个方法,I代表返回值为int。
invokevirtual
私有方法的调用和其他方法的调用一样,都是invokevirtual,我举个例子:
package com.youngthing.matrix.test.image;
/**
* 11/22/2022 9:35 AM 创建
*
* @author 花书粉丝
*/
public final class InvokeVirtualTest {
private void print() {
System.out.println("test");
}
public void test() {
print();
}
}
反编译如下:
Compiled from "InvokeVirtualTest.java"
public final class com.youngthing.matrix.test.image.InvokeVirtualTest {
public com.youngthing.matrix.test.image.InvokeVirtualTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void test();
Code:
0: aload_0
1: invokevirtual #5 // Method print:()V
4: return
}
invokestatic
我举个例子:
package com.youngthing.matrix.test.image;
/**
* 11/22/2022 9:35 AM 创建
*
* @author 花书粉丝
*/
public final class InvokeStaticTest {
public void test() {
System.currentTimeMillis();
}
}
编译结果如下:
Compiled from "InvokeStaticTest.java"
public final class com.youngthing.matrix.test.image.InvokeStaticTest {
public com.youngthing.matrix.test.image.InvokeStaticTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void test();
Code:
0: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
3: pop2
4: return
}
需要注意的是在这里,方法调用前没有任何load指令。因为System.currentTimeMillis()这个方法是静态方法,又没有任何方法参数,所以不需要加载任何变量。而非静态方法,哪怕没有参数,也要加载对象本身的。
invokeinterface
我就以常见的List举例吧:
package com.youngthing.matrix.test.image;
import java.util.ArrayList;
import java.util.List;
/**
* 11/22/2022 9:35 AM 创建
*
* @author 花书粉丝
*/
public final class InvokeInterfaceTest {
public void test() {
final List<Integer> list = new ArrayList<>();
list.add(1);
}
}
反编译的结果很容易看到:
Compiled from "InvokeInterfaceTest.java"
public final class com.youngthing.matrix.test.image.InvokeInterfaceTest {
public com.youngthing.matrix.test.image.InvokeInterfaceTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void test();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: iconst_1
10: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
18: pop
19: return
}
invokedynamic
最后一个比较复杂了,为此,我写了一个简单的lambda表达式:
package com.youngthing.matrix.test.image;
import java.util.ArrayList;
import java.util.List;
/**
* 11/22/2022 9:35 AM 创建
*
* @author 花书粉丝
*/
public final class InvokeDynamicTest {
public void test() {
final List<Integer> list = new ArrayList<>();
list.stream().map(x->x++);
}
}
反编译后的指令里可以看到invokedynamic:
Compiled from "InvokeDynamicTest.java"
public final class com.youngthing.matrix.test.image.InvokeDynamicTest {
public com.youngthing.matrix.test.image.InvokeDynamicTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void test();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: invokeinterface #4, 1 // InterfaceMethod java/util/List.stream:()Ljava/util/stream/Stream;
14: invokedynamic #5, 0 // InvokeDynamic #0:apply:()Ljava/util/function/Function;
19: invokeinterface #6, 2 // InterfaceMethod java/util/stream/Stream.map:(Ljava/util/function/Function;)Ljava/util/stream/Stream;
24: pop
25: return
}