通过字节码展示Java8 Lambda的实现

Java8 增加了 Lambda 表达式,很大程度使代码变的更加简洁紧凑了,那么 Java8 是如何实现 Lambda 表达式的呢?

直接看一个简单的创建线程的例子。

public class TestLambda {
public static void main(String[] args) {
new Thread(() -> System.out.print(“thread”));
}
}
执行javac TestLambda.java编译生成文件TestLambda.class,然后用javap命令来分析这个class文件。
执行javap -p TestLambda显示所有类和成员。

// javap -p TestLambda
Compiled from “TestLambda.java”
public class TestLambda {
public TestLambda();
public static void main(java.lang.String[]);
private static void lambda$main$0(); // 编译后生成的
}
由上面的代码可以看出编译器根据 Lambda 表达式生成了一个私有静态方法。

private static void lambda$main$0();
使用命令javap -v -p TestLambda打印详细信息。

public class TestLambda
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #9.#24 // java/lang/Object.""?)V
#2 = Class #25 // java/lang/Thread
#3 = InvokeDynamic #0:#30 // #0:run:()Ljava/lang/Runnable;
#4 = Methodref #2.#31 // java/lang/Thread."":(Ljava/lang/Runnable;)V
#5 = Fieldref #32.#33 // java/lang/System.out:Ljava/io/PrintStream;
#6 = String #34 // thread
#7 = Methodref #35.#36 // java/io/PrintStream.print:(Ljava/lang/String;)V
#8 = Class #37 // TestLambda
#9 = Class #38 // java/lang/Object
#10 = Utf8
#11 = Utf8 ()V
#12 = Utf8 Code
#13 = Utf8 LineNumberTable
#14 = Utf8 LocalVariableTable
#15 = Utf8 this
#16 = Utf8 LTestLambda;
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 args
#20 = Utf8 [Ljava/lang/String;
#21 = Utf8 lambda$mainKaTeX parse error: Expected 'EOF', got '#' at position 5: 0 #̲22 = Utf8 …Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#28 = MethodType #11 // ()V
#29 = MethodHandle #6:#40 // invokestatic TestLambda.lambda$mainKaTeX parse error: Expected 'EOF', got '#' at position 9: 0:()V #̲30 = NameAndTyp…Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#40 = Methodref #8.#52 // TestLambda.lambda$mainKaTeX parse error: Expected 'EOF', got '#' at position 9: 0:()V #̲41 = Utf8 …Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#52 = NameAndType #21:#11 // lambda$mainKaTeX parse error: Expected 'EOF', got '#' at position 9: 0:()V #̲53 = Utf8 …Lookup
#56 = Utf8 Lookup
#57 = Utf8 InnerClasses
#58 = Utf8 (Ljava/lang/invoke/MethodHandlesKaTeX parse error: Expected 'EOF', got '#' at position 175: …ke/CallSite; #̲59 = Class …Lookup
#61 = Utf8 java/lang/invoke/MethodHandles
{
public TestLambda();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object.""?)V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LTestLambda;

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
0: new #2 // class java/lang/Thread
3: dup
4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
9: invokespecial #4 // Method java/lang/Thread."":(Ljava/lang/Runnable;)V
12: pop
13: return
LineNumberTable:
line 3: 0
line 4: 13
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 args [Ljava/lang/String;

private static void lambda$mainKaTeX parse error: Expected 'EOF', got '#' at position 149: … getstatic #̲5 …Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #27 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandlesKaTeX parse error: Expected 'EOF', got '#' at position 201: …guments: #̲28 ()V #2…main$0:()V
#28 ()V
如上所示:main() 方法创建线程的一行代码反编译后的字节码是

0: new #2 // class java/lang/Thread
3: dup
4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
9: invokespecial #4 // Method java/lang/Thread."":(Ljava/lang/Runnable;)V
12: pop
13: return
根据4: invokedynamic #3找到常量池第三行

#3 = InvokeDynamic #0:#30 // #0:run:()Ljava/lang/Runnable;
其中,#0指向BootstrapMethods:的静态工厂方法LambdaMetafactory.metafactory

BootstrapMethods:
0: #27 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandlesKaTeX parse error: Expected 'EOF', got '#' at position 201: …guments: #̲28 ()V #2…main$0:()V
#28 ()V
LambdaMetafactory.metafactory会利用 asm 可以为 Lambda 表达式生成内部类,metafactory 方法源码如下

public static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType)
throws LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
mf = new InnerClassLambdaMetafactory(caller, invokedType,
invokedName, samMethodType,
implMethod, instantiatedMethodType,
false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}
为了查看这个内部类,加上生成代理类的参数运行 TestLambda,即java -Djdk.internal.lambda.dumpProxyClasses TestLambda。运行结束后,发现在同级目录生成了一个class文件TestLambdaKaTeX parse error: Can't use function '$' in math mode at position 7: Lambda$̲1.class。执行javap…Lambda$1反编译这个类。

final class TestLambdaKaTeX parse error: Can't use function '$' in math mode at position 7: Lambda$̲1 implements ja…LambdaKaTeX parse error: Expected 'EOF', got '#' at position 6: 1 #̲2 = Class …$LambdaKaTeX parse error: Expected 'EOF', got '#' at position 6: 1 #̲3 = Utf8 …Hidden;
#13 = Utf8 TestLambda
#14 = Class #13 // TestLambda
#15 = Utf8 lambda$mainKaTeX parse error: Expected 'EOF', got '#' at position 5: 0 #̲16 = NameAndTyp…mainKaTeX parse error: Expected 'EOF', got '#' at position 9: 0:()V #̲17 = Methodref …mainKaTeX parse error: Expected 'EOF', got '#' at position 9: 0:()V #̲18 = Utf8 …$Lambda$1();
descriptor: ()V
flags: ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #10 // Method java/lang/Object.""?)V
4: return

public void run();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: invokestatic #17 // Method TestLambda.lambda$mainKaTeX parse error: Expected 'EOF', got '#' at position 66: …ions: 0: #̲12() } 由上可见,内部类…$Lambda 1 实 现 了 j a v a . l a n g . R u n n a b l e , r u n ( ) 方 法 即 线 程 启 动 后 执 行 的 方 法 , 它 会 触 发 执 行 T e s t L a m b d a . l a m b d a 1实现了 java.lang.Runnable,run()方法即线程启动后执行的方法,它会触发执行TestLambda.lambda 1java.lang.Runnablerun()线TestLambda.lambdamain$0:(),即编译TestLambda时生成的私有静态方法。

总结
创建线程的 Lambda 表达式在编译后会在 class 文件新增一个私有静态方法,运行这个类的期间,会使用 asm 操作字节码的技术,动态生成内部类,此内部类实现了 Runnable 接口,真正执行线程,线程方法 run 内部实际上就是调用了编译后生成的私有静态方法,从而执行线程代码
东莞网站建设www.zg886.cn

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值