D.2 生成字节码
Java的源代码文件会经由Java编译器编译为Java字节码。之后JVM可以执行这些生成的字节码运行应用。编译时,匿名类和Lambda表达式使用了不同的字节码指令。你可以通过下面这条命令查看任何类文件的字节码和常量池:
javap -c -v ClassName
我们试着使用Java 7中旧的格式实现了Function接口的一个实例,代码如下所示。
代码清单 D-1 以匿名内部类的方式实现的一个
Function接口
import java.util.function.Function;public class InnerClass {Function<Object, String> f = new Function<Object, String>() {@Overridepublic String apply(Object obj) {return obj.toString();}};}
这种方式下,和Function对应,以匿名内部类形式生成的字节码看起来就像下面这样:
0: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: aload_05: new #2 // class InnerClass$18: dup9: aload_010: invokespecial #3 // Method InnerClass$1."<init>":(LInnerClass;)V13: putfield #4 // Field f:Ljava/util/function/Function;16: return
这段代码展示了下面这些编译中的细节。
- 通过字节码操作
new,一个InnerClass$1类型的对象被实例化了。与此同时,一个指向新创建对象的引用会被压入栈。 dup操作会复制栈上的引用。- 接着,这个值会被
invokespecial指令处理,该指令会初始化对象。 - 栈顶现在包含了指向对象的引用,该值通过
putfield指令保存到了LambdaBytecode类的f1字段。
InnerClass$1是由编译器为匿名类生成的名字。如果你想要再次确认这一情况,也可以查看InnerClass$1类文件,你可以看到Function接口的实现代码如下:
class InnerClass$1 implementsjava.util.function.Function<java.lang.Object, java.lang.String> {final InnerClass this$0;public java.lang.String apply(java.lang.Object);Code:0: aload_11: invokevirtual #3 // Methodjava/lang/Object.toString:()Ljava/lang/String;4: areturn}
