java8 lambda底层原理

Java8 Lambda表达式,常用接口,Stream流式处理
java8 stream 源码分析
java8 lambda底层原理
.
Java规范请求 — JSR 292:在Java™平台上支持动态类型语言
JVM规范— invokedynamic 指令

Java stream 知识点整理

Java stream 知识点整理

闭包(Closure)

闭包就是函数对象外界变量绑定在一起,形成的整体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ClosureTest1 {
interface Lambda {
int add(int y);
}

public static void main(String[] args) {
int x = 10;

highOrder(y -> x + y);
}

static void highOrder(Lambda lambda) {
System.out.println(lambda.add(20));
}
}

柯里化(Carrying)

第一次知道柯里化,还是在这里 》》》 Lambda函数的柯里化 — 2022-07-16 18:48:07

柯里化的作用是让函数对象分步执行(本质上是利用多个函数对象和闭包)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Carrying1Test {
public static void main(String[] args) {
highOrder(a -> b -> a + b);
}

static void highOrder(Step1 step1) {
Step2 step2 = step1.exec(10);
System.out.println(step2.exec(20));
System.out.println(step2.exec(50));
}

interface Step1 {
Step2 exec(int a);
}

interface Step2 {
int exec(int b);
}
}

优化效率

1
2
3
4
5
计算 => IntStream、LongStream、。。。  效率接近原生 for循环

并行流 => 量大时,再使用

合并流 => 量大时,并行时 例如: .parallel().collect(groupingByConcurrent(Function.identity(), counting()));

MyBatis-Plus Student::getName()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class MyTest {

// 2024-04-23 11:05 MyBatis-Plus 获取方法名
public static void main(String[] args) throws Exception {

// Function<Student, String> f = Student::getName;
// Class<? extends Function> aClass = f.getClass();
// Method[] declaredMethods = f.getClass().getDeclaredMethods();// length=1

// Type1 f = (Type1 & Serializable) Student::getName;
Type1 f = (Type1 & Serializable) stu -> stu.getName();

// 函数对象 <=> 字节码 会额外存储类和方法的信息, 运行时就可以根据这些信息找到属性, 从而进一步确定【列名】
// for (Method method : f.getClass().getDeclaredMethods()) {
// System.out.println(method);
// // public java.lang.String com.taopanfeng.junit.MyTest$$Lambda$2/1217467887.abc(com.taopanfeng.junit.web.Student)
// // private final java.lang.Object com.taopanfeng.junit.MyTest$$Lambda$2/1217467887.writeReplace()
// }

Method method = f.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(true);
SerializedLambda invoke = (SerializedLambda) method.invoke(f);

// invoke 是新对象,包含了原始函数对象的字节码,还包含了类和方法的额外信息
System.out.println(invoke.getClass());// "class java.lang.invoke.SerializedLambda"

System.out.println(invoke.getCapturingClass()); // 哪个类使用了这个函数对象 "com/taopanfeng/junit/MyTest"
System.out.println(invoke.getImplClass()); // 哪个类实现了函数对象的逻辑 "com/taopanfeng/junit/web/Student"
System.out.println(invoke.getImplMethodName()); // 哪个方法实现了函数对象的逻辑 "getName"

// Type1 f = (Type1 & Serializable) Student::getName;
// invoke.getImplClass() com/taopanfeng/junit/web/Student
// invoke.getImplMethodName() getName
// Type1 f = (Type1 & Serializable) stu -> stu.getName();
// invoke.getImplClass() com/taopanfeng/junit/MyTest
// invoke.getImplMethodName() lambda$main$a8c72da8$1
}

interface Type1 {
String abc(Student student);
}
}

lambda原理

Java规范请求 — JSR 292:在Java™平台上支持动态类型语言
JVM规范— invokedynamic 指令

在这里插入图片描述

lambda本质

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyRunnable1 {
// 2024-04-24 10:37
public static void main(String[] args) throws Throwable {
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("r1...");
}
};
r1.run();
}
}


public class MyRunnable2 {
// 2024-04-24 10:37
public static void main(String[] args) throws Throwable {
Runnable r2 = () -> System.out.println("r2...");
r2.run();
}
}

当时学 lambda 的时候,一直认为二者是相同的,最近才发现,完成不是的。(r2 会动态生成类和方法,而 r1 则不会)。
下面来一步步分析。

0、先了解两个知识点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1、javap 命令

常用: javap -p -c -v a.class > 1.txt

$ javap --help # (这里只显示3列,实际显示很多参数)
-v -verbose 输出附加信息
-p -private 显示所有类和成员
-c 对代码进行反汇编

--------------------------------

# 2、加JDK参数,生成class文件(lambda 生成的 class信息默认只会在内存中,而不会在磁盘创建 class文件)
jdk 21 -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles
jdk 21之前 -Djdk.internal.lambda.dumpProxyClasses

1、先来分析 r1

1
会生成一个 class文件,表示内部类,   真正执行的时候,会创建内部类 impl Runnable,调用内部类.run()。

在这里插入图片描述

2、再来分析 r2

1
2
3
4
5
6
1、当前类中生成方法,记作方法 a(只在内存中,磁盘中的class信息中不会有这个方法。  此方法用于 lambda类调用。)

2、生成 lambda 内部类(加参数才会生成对应的 class文件),lambda 类 并实现接口,重写的方法 会调用上面 1中的 生成方法 a。

不是编译器生成,而是运行时生成。
运行时,产生在内存中,运行完就没了。

在这里插入图片描述
在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MyRunnable2 {
// 2024-04-24 10:37
public static void main(String[] args) throws Throwable {
// Runnable r2 = () -> System.out.println("r2...");

// Runnable r2 = new MyRunnable1$$Lambda$1();
// r2.run();

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle impl = lookup.findStatic(MyRunnable2.class, "lambda$main$0",
MethodType.methodType(void.class));
// 内部:生成函数对象所需的类 ASM ===> 生产类 MyRunnable1$$Lambda$1
CallSite cs = LambdaMetafactory.metafactory(
lookup, // 1. lookup
"run", // 2. 接口方法名
MethodType.methodType(Runnable.class), // 3. lambda返回类型 Runnable factory()
MethodType.methodType(void.class), // 4. 接口方法定义(泛型,所以 Object.class
impl, // 5. 实现方法 (本例就是下面的静态方法 lambda$main$0)
MethodType.methodType(void.class) // 6. 接口实现定义
);

// Runnable factory() {return new MyRunnable1$$Lambda$1();}
MethodHandle mh = cs.getTarget(); // 就是函数对象工厂方法
Runnable invoke = (Runnable) mh.invoke();
invoke.run();
}

// // $FF: synthetic class
// final static class MyRunnable1$$Lambda$1 implements Runnable {
// private MyRunnable1$$Lambda$1() {
// }
//
// public void run() {
// MyRunnable2.lambda$main$0();
// }
// }

private static void lambda$main$0() {
System.out.println("r2...");
}
}

BiFunction 有参数和返回值

上面 Runnable 是无参、无返回值的,这里再来看一个有参、有返回值的。

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.function.BiFunction;

public class MyTest {

// 2024-04-23 13:47
public static void main(String[] args) throws Throwable {
// (a, b) -> a+b
// BiFunction<Integer, Integer, Integer> function = (a, b) -> a + b;

// BiFunction<Integer, Integer, Integer> function = new MyLambda();

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle impl = lookup.findStatic(MyTest.class, "lambda$main$0",
MethodType.methodType(Integer.class, Integer.class, Integer.class));
// 内部:生成函数对象所需的类 ASM
CallSite cs = LambdaMetafactory.metafactory(
lookup, // 1. lookup
"apply", // 2. 接口方法名
MethodType.methodType(BiFunction.class), // 3. Lambda返回类型 BiFunction factory()
MethodType.methodType(Object.class, Object.class, Object.class), // 4. 接口方法定义(泛型,所以 Object.class
impl, // 5. 实现方法 (本例就是下面的静态方法 lambda$main$0)
MethodType.methodType(Integer.class, Integer.class, Integer.class) // 6. 接口实现定义
);

// BiFunction factory() {return new MyLambda();}
MethodHandle mh = cs.getTarget(); // 就是函数对象工厂方法
BiFunction<Integer, Integer, Integer> function = (BiFunction<Integer, Integer, Integer>) mh.invoke();
System.out.println(function.apply(1, 2));// 3
}

// static final class MyLambda implements BiFunction<Integer, Integer, Integer> {
// private MyLambda() {
// }
//
// @Override
// public Integer apply(Integer a, Integer b) {
// return lambda$main$0(a, b);
// }
// }

// 运行时,根据 lambda 生成的方法
private static Integer lambda$main$0(Integer a, Integer b) {
return a + b;
}

}
/*
lambda 表达式是一种语法糖,它仍然会被翻译成 类、对象、方法
1. 方法从哪来 : 编译器发现代码中出现了 lambda,就会在当前类中生成 private static 方法,方法内包含的就是 lambda 的逻辑
实验代码
for (Method method : MyTest.class.getDeclaredMethods()) {
System.out.println(method);
// private static java.lang.Integer com.taopanfeng.junit.mylambda.MyTest.lambda$main$0(java.lang.Integer,java.lang.Integer)
// public static void com.taopanfeng.junit.mylambda.MyTest.main(java.lang.String[]) throws java.lang.Exception
}

2. 类和对象从哪来 : 运行期间动态生成

3. MethodHandle 铺垫
MethodHandle 的执行权限与上下文相关
原本有权限调用的方法,正常能调用,通过 MethodHandle 反射也能调用
原本没权限调用的方法,正常不能调用,MethodHandle 反射也调用不了

// a) 反射调用静态方法
MethodHandle mh = MethodHandles.lookup().findStatic(MyTest.class, "lambda$main$0",
MethodType.methodType(Integer.class, Integer.class, Integer.class));
System.out.println(mh.invoke(1, 2)); // 相当于 MyTest.lambda$main$0(1, 2);
// 输出:3

// b) 反射调用非静态方法
MethodHandle mh2 = MethodHandles.lookup().findVirtual(MyLambda.class, "apply",
MethodType.methodType(Integer.class, Integer.class, Integer.class));
System.out.println(mh2.invoke(new MyLambda(), 3, 4)); // 相当于 new MyLambda().apply(3, 4);
// 输出:7

// c) 反射调用构造
MethodHandle mh3 = MethodHandles.lookup().findConstructor(MyLambda.class, MethodType.methodType(void.class));
System.out.println(mh3.invoke()); // 相当于 new MyLambda();
// 输出:com.taopanfeng.junit.mylambda.MyTest$MyLambda@4c39bec8

4. 工厂方法 LambdaMetafactory.metafactory
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle impl = lookup.findStatic(MyTest.class, "lambda$main$0",
MethodType.methodType(Integer.class, Integer.class, Integer.class));
// 内部:生成函数对象所需的类 ASM
CallSite cs = LambdaMetafactory.metafactory(
lookup, // 1. lookup
"apply", // 2. 接口方法名
MethodType.methodType(BiFunction.class), // 3. Lambda返回类型 BiFunction factory()
MethodType.methodType(Object.class, Object.class, Object.class), // 4. 接口方法定义(泛型,所以 Object.class)
impl, // 5. 实现方法 (本例就是下面的静态方法 lambda$main$0)
MethodType.methodType(Integer.class, Integer.class, Integer.class) // 6. 接口实现定义
);

// BiFunction factory() {return new MyLambda();}
MethodHandle mh = cs.getTarget(); // 就是函数对象工厂方法
BiFunction<Integer, Integer, Integer> invoke = (BiFunction<Integer, Integer, Integer>) mh.invoke();
System.out.println(invoke.apply(5, 6));// 11
*/

Student::getName 方法引用

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.function.Function;

public class MyTest02_MethodReference {

// 2024-04-23 15:38
public static void main(String[] args) throws Throwable {
// Function<Student, String> f = Student::getName; // stu -> stu.getName()

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle impl = lookup.findVirtual(Student.class, "getName", MethodType.methodType(String.class));
CallSite cs = LambdaMetafactory.metafactory(
lookup, // 1. lookup
"apply", // 2. 接口方法名
MethodType.methodType(Function.class), // 3. Lambda返回类型 Function factory()
MethodType.methodType(Object.class, Object.class), // 4. 接口方法定义(泛型,所以 Object.class
impl, // 5. 实现方法 (本例就是 Student.getName 方法)
MethodType.methodType(String.class, Student.class) // 6. 接口实现定义
);

Function<Student, String> invoke = (Function<Student, String>) cs.getTarget().invoke();
Student stu = new Student();
stu.name = "张三";
String apply = invoke.apply(stu);// "张三"
}

// // $FF: synthetic class
// static final class MyTest02_MethodReference$$Lambda$1 implements Function {
// private MyTest02_MethodReference$$Lambda$1() {
// }
//
// @Hidden
// public Object apply(Object var1) {
// return ((MyTest02_MethodReference.Student)var1).getName();
// }
// }

static class Student {
private String name;

public String getName() {
return name;
}
}

}

闭包 局部变量

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.lang.reflect.Method;
import java.util.function.BinaryOperator;

public class MyTest03_Closure {

// 2024-04-23 18:58
public static void main(String[] args) throws Throwable {
int c = 10;
BinaryOperator<Integer> lambda = (a, b) -> a + b + c; // invoke dynamic new MyTest03_Closure$$Lambda$2(10, a, b)

for (Method method : MyTest03_Closure.class.getDeclaredMethods()) {
System.out.println(method);
// public static void com.taopanfeng.junit.mylambda.MyTest03_Closure.main(java.lang.String[]) throws java.lang.Throwable
// private static java.lang.Integer com.taopanfeng.junit.mylambda.MyTest03_Closure.lambda$main$0(int,java.lang.Integer,java.lang.Integer)
}

}

/*

// $FF: synthetic class
final class MyTest03_Closure$$Lambda$2 implements BinaryOperator {
private final int arg$1;

private MyTest03_Closure$$Lambda$2(int var1) {
this.arg$1 = var1;
}

private static BinaryOperator get$Lambda(int var0) {
return new MyTest03_Closure$$Lambda$2(var0);
}

@Hidden
public Object apply(Object var1, Object var2) {
return MyTest03_Closure.lambda$main$0(this.arg$1, (Integer)var1, (Integer)var2);
}
}

private static Integer lambda$main$0(int c, Integer a, Integer b){
return a + b + c;
}

*/
}

闭包 静态变量

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import java.lang.reflect.Method;
import java.util.function.BinaryOperator;

public class MyTest04_Closure {

static int c = 10;

// 2024-04-23 18:58
public static void main(String[] args) throws Throwable {
BinaryOperator<Integer> lambda = (a, b) -> a + b + c; // invoke dynamic new MyTest03_Closure$$Lambda$2(10, a, b)

for (Method method : MyTest04_Closure.class.getDeclaredMethods()) {
System.out.println(method);
// public static void com.taopanfeng.junit.mylambda.MyTest04_Closure.main(java.lang.String[]) throws java.lang.Throwable
// private static java.lang.Integer com.taopanfeng.junit.mylambda.MyTest04_Closure.lambda$main$0(java.lang.Integer,java.lang.Integer)
}

}

/*

// $FF: synthetic class
final class MyTest04_Closure$$Lambda$1 implements BinaryOperator {
private MyTest04_Closure$$Lambda$1() {
}

@Hidden
public Object apply(Object var1, Object var2) {
return MyTest04_Closure.lambda$main$0((Integer)var1, (Integer)var2);
}
}

private static Integer lambda$main$0(Integer a, Integer b){
return a + b + MyTest04_Closure.c;
}

*/
}

闭包 成员变量

这里不同的是,lambda 对象也是成员变量,生成的方法不再是 static 静态方法。
在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.lang.reflect.Method;
import java.util.function.BinaryOperator;

public class MyTest05_Closure {

int c = 10;
BinaryOperator<Integer> lambda = (a, b) -> a + b + c;

// 2024-04-24 09:27
public static void main(String[] args) throws Throwable {
Integer apply = new MyTest05_Closure().lambda.apply(3, 4);// 17

for (Method method : MyTest05_Closure.class.getDeclaredMethods()) {
System.out.println(method);
// public static void com.taopanfeng.junit.mylambda.MyTest05_Closure.main(java.lang.String[]) throws java.lang.Throwable
// private java.lang.Integer com.taopanfeng.junit.mylambda.MyTest05_Closure.lambda$new$0(java.lang.Integer,java.lang.Integer)
}
}

/*

// $FF: synthetic class
final class MyTest05_Closure$$Lambda$1 implements BinaryOperator {
private final MyTest05_Closure arg$1;

private MyTest05_Closure$$Lambda$1(MyTest05_Closure var1) {
this.arg$1 = var1;
}

private static BinaryOperator get$Lambda(MyTest05_Closure var0) {
return new MyTest05_Closure$$Lambda$1(var0);
}

@Hidden
public Object apply(Object var1, Object var2) {
return this.arg$1.lambda$new$0((Integer)var1, (Integer)var2);
}
}

private Integer lambda$new$0(Integer a, Integer b){
return a + b + this.c;
}

*/
}