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
1实现了java.lang.Runnable,run()方法即线程启动后执行的方法,它会触发执行TestLambda.lambdamain$0:(),即编译TestLambda时生成的私有静态方法。
总结
创建线程的 Lambda 表达式在编译后会在 class 文件新增一个私有静态方法,运行这个类的期间,会使用 asm 操作字节码的技术,动态生成内部类,此内部类实现了 Runnable 接口,真正执行线程,线程方法 run 内部实际上就是调用了编译后生成的私有静态方法,从而执行线程代码
东莞网站建设www.zg886.cn