1.编译合成一个static方法
public static void main(String[] args) {
Strategy strategy1 = msg -> "strategy1 : "+msg.toUpperCase() + "!";
Strategy strategy2 = new Strategy() {
@Override
public String approach(String msg) {
return "strategy2 : "+msg.toUpperCase() + "!";
}
};
编译期多生成了一个函数lambda$main$0,并没有为lambda表达式多生成一个class。
lambda$main$0的命名:以lambda开头,因为是在main()函数里使用了lambda表达式,所以带有$main表示,因为是第一个,所以$0。
REF_invokeStatic xxx/Strategize.lambda$main$0:(Ljava/lang/String;)Ljava/lang/String;
2.查看运行期生成的class
添加-Djdk.internal.lambda.dumpProxyClasses=”path”, 在启动的时候生成的class会保存到目录path下面。
import com.enjoy.learn.core.lambdastream.test.test3.Strategize;
import com.enjoy.learn.core.lambdastream.test.test3.Strategy;
import java.lang.invoke.LambdaForm.Hidden;
// $FF: synthetic class
final class Strategize$$Lambda$1 implements Strategy {
@Hidden
public String approach(String var1) {
return Strategize.lambda$main$0(var1);
}
}
synthetic class说明这个类是通过字节码工具自动生成的,注意到,这个类是final,实现了Strategy接口,接口是实现很简单,就是调用了第一次编译时候生产的Strategize.lambda$main$0()方法。从命名上可以看出这个类是实现lambda表达式的类和以及Strategize的内部类。
3.在linux下面的验证
step1.查看合成的static方法
public class Lambda {
public static void PrintString(String s, Print<String> print) {
print.print(s);
}
public static void main(String[] args){
System.setProperty("jdk.internal.lambda.dumpProxyClasses","/root/work");
PrintString("test", (x) -> System.out.println(x));
}
}
javap -c -p Lambda.class
private static void lambda$main$0(java.lang.String);
Code:
0: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
7: return
step2.看main函数中看对lambda表达式的调用
有invokedynamic指令
public static void main(java.lang.String[]);
Code:
0: ldc #3 // String jdk.internal.lambda.dumpProxyClasses
2: ldc #4 // String /root/work
4: invokestatic #5 // Method java/lang/System.setProperty:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
7: pop
8: ldc #6 // String test
10: invokedynamic #7, 0 // InvokeDynamic #0:print:()LPrint;
15: invokestatic #8 // Method PrintString:(Ljava/lang/String;LPrint;)V
18: return
invokedynamic indexbyte1 indexbyte2 0 0
索引指向的常量池项的类型为CONSTANT_InvokeDynamic_info。
CONSTANT_InvokeDynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
invokedynamic #7, 0
查看class文件的常量池:
#7 = InvokeDynamic #0:#40 // #0:print:()LPrint;
#40 = NameAndType #48:#55 // print:()LPrint;
bootstrap_method_attr_index索引bootstrap_methods表,bootstrap_methods位于class文件的attributes表里。
如果类的常量池中存在CONSTANT_InvokeDynamic_info的话,那么attributes表中就必定有且仅有一个BootstrapMethods属性。BootstrapMethods属性是个变长的表
BootstrapMethods_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 num_bootstrap_methods;
{ u2 bootstrap_method_ref;
u2 num_bootstrap_arguments;
u2 bootstrap_arguments[num_bootstrap_arguments];
} bootstrap_methods[num_bootstrap_methods];
}
BootstrapMethods:
0: #36 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#37 (Ljava/lang/Object;)V
#38 invokestatic Lambda.lambda$main$0:(Ljava/lang/String;)V
#39 (Ljava/lang/String;)V
确实存在一个BootstrapMethods表,这个表中只有一个BootstrapMethod,它的bootstrap_method_ref是常量池#36,有三个bootstrap_arguments,分别指向常量池#37,#38和#39:
#36 = MethodHandle #6:#53 // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#37 = MethodType #49 // (Ljava/lang/Object;)V
#38 = MethodHandle #6:#54 // invokestatic Lambda.lambda$main$0:(Ljava/lang/String;)V
#39 = MethodType #24 // (Ljava/lang/String;)V
JVM规范:
CONSTANT_MethodHandle_info {
u1 tag;
u1 reference_kind;
u2 reference_index;
}
reference_kind是一个1到9之间的整数,6 (REF_invokeStatic)。引用的是java.lang.invoke.LambdaMetafactory类的静态方法metafactory()
step3.查看合成的类
System.setProperty("jdk.internal.lambda.dumpProxyClasses","/root/work");
javap -c -p Lambda$$Lambda$1.class
final class Lambda$$Lambda$1 implements Print {
private Lambda$$Lambda$1();
Code:
0: aload_0
1: invokespecial #10 // Method java/lang/Object."<init>":()V
4: return
public void print(java.lang.Object);
Code:
0: aload_1
1: checkcast #15 // class java/lang/String
4: invokestatic #21 // Method Lambda.lambda$main$0:(Ljava/lang/String;)V
7: return
}
通过上面的字节码指令可以发现实现上调用的是Lambda. lambda0这个私有的静态方法
4.lambda机制总结
编译时:
- 1)lambda表达式生产一个静态方法lambda$main$0,方法实现了表达式的代码逻辑;
- 2)编译生成invokedynamic指令,调用bootstrap方法,由java.lang.invoke.LambdaMetafactory. metafactory()方法实现;
运行时:
- 3)invokedynamic指令调用metafactory方法,返回一个callsite,此callsite返回目标类型的一个匿名实现类(MethodHandles.Lookup caller 的内部类),此类关联编译时产生的方法;
-
4)lambda表达式调用时会调用匿名实现类Lambda$$Lambda$1关联的方法lambda$main$0