Skip to content

Commit 688634b

Browse files
committed
添加Lambda表达式实现原理
1 parent 8f60cdd commit 688634b

6 files changed

+98
-2
lines changed

2-Labmda and Anonymous Classes(II).md

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Labmda and Anonymous Classes(II)
2+
3+
# 前言
4+
5+
读过上一篇之后,相信对Lambda表达式的语法以及基本原理有了一定了解。对于编写代码,有这些知识已经够用。本文将**进一步区分Lambda表达式和匿名内部类在JVM层面的区别,如果对这一部分不感兴趣,可以跳过**
6+
7+
# 不仅是匿名内部类的简写
8+
9+
经过第一篇的的介绍,我们看到Lambda表达式似乎只是为了简化匿名内部类书写,这看起来仅仅通过语法糖在编译阶段把所有的Lambda表达式替换成匿名内部类就可以了。但实时并非如此。在JVM层面,Lambda表达式和匿名内部类有着明显的差别。
10+
11+
## 匿名内部类实现
12+
13+
**匿名内部类仍然是一个类,只是不需要程序员显示指定类名,编译器会自动为该类取名**。因此如果有如下形式的代码,编译之后将会产生两个class文件:
14+
15+
```java
16+
public class MainAnonymousClass {
17+
public static void main(String[] args) {
18+
new Thread(new Runnable(){
19+
@Override
20+
public void run(){
21+
System.out.println("Anonymous Class Thread run()");
22+
}
23+
}).start();;
24+
}
25+
}
26+
```
27+
编译之后文件分布如下,两个class文件分别是主类和匿名内部类产生的:
28+
29+
![2-AnonymousClass.png](./Figures/2-AnonymousClass.png)
30+
31+
进一步分析主类MainAnonymousClass.class的字节码,可发现其创建了匿名内部类的对象:
32+
33+
```java
34+
// javap -c MainAnonymousClass.class
35+
public class MainAnonymousClass {
36+
...
37+
public static void main(java.lang.String[]);
38+
Code:
39+
0: new #2 // class java/lang/Thread
40+
3: dup
41+
4: new #3 // class MainAnonymousClass$1 /*创建内部类对象*/
42+
7: dup
43+
8: invokespecial #4 // Method MainAnonymousClass$1."<init>":()V
44+
11: invokespecial #5 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
45+
14: invokevirtual #6 // Method java/lang/Thread.start:()V
46+
17: return
47+
}
48+
49+
```
50+
## Lambda表达式实现
51+
52+
**Lambda表达式通过*invokedynamic*指令实现,书写Lambda表达式不会产生新的类**。如果有如下代码,编译之后只有一个class文件:
53+
54+
```java
55+
public class MainLambda {
56+
public static void main(String[] args) {
57+
new Thread(
58+
() -> System.out.println("Lambda Thread run()")
59+
).start();;
60+
}
61+
}
62+
```
63+
编译之后的结果:
64+
65+
![2-Lambda](./Figures/2-Lambda.png)
66+
67+
通过javap反编译命名,我们更能看出Lambda表达式内部表示的不同:
68+
69+
```java
70+
// javap -c -p MainLambda.class
71+
public class MainLambda {
72+
...
73+
public static void main(java.lang.String[]);
74+
Code:
75+
0: new #2 // class java/lang/Thread
76+
3: dup
77+
4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; /*使用invokedynamic指令调用*/
78+
9: invokespecial #4 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
79+
12: invokevirtual #5 // Method java/lang/Thread.start:()V
80+
15: return
81+
82+
private static void lambda$main$0(); /*Lambda表达式变成了主类的私有方法*/
83+
Code:
84+
0: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
85+
3: ldc #7 // String Lambda Thread run()
86+
5: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
87+
8: return
88+
}
89+
90+
```
91+
92+
反编译之后我们发现Lambda表达式被封装成了主类的一个私有方法,并通过*invokedynamic*指令进行调用。
93+
94+
95+
96+

Figures/2-AnonymousClass.png

24 KB
Loading

Figures/2-Lambda.png

17 KB
Loading

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ Java 8已经发行两年多,但很多人仍然在使用JDK7。对企业来说
2020

2121
具体内容安排如下:
2222

23-
1. Labmda and Anonymous Classes(I),展示如何使用Lambda表达式替代匿名内部类
24-
2. Labmda and Anonymous Classes(II),Lambda表达式的实现原理
23+
1. [Labmda and Anonymous Classes(I)](./1-Labmda and Anonymous Classes(I).md),展示如何使用Lambda表达式替代匿名内部类
24+
2. [Labmda and Anonymous Classes(II)](./2-Labmda and Anonymous Classes(II).md),Lambda表达式的实现原理
2525
3. Lambda and Stream,展示Lambda表达式如何跟Stream配合使用
2626
4. Functional interfaces,展示函数接口的作用,和常见函数接口
2727
4. Stream API的使用,详细讲解Stream API的用法

diaFiles/2-AnonymousClass.dia

1.08 KB
Binary file not shown.

diaFiles/2-Lambda.dia

1.09 KB
Binary file not shown.

0 commit comments

Comments
 (0)