Lambda 表达式不是匿名内部类!
误区:Lambda = 匿名类语法糖?
// 很多人以为 Lambda 等价于:
list.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}
});
❌ 错!
真相:Lambda 通过 invokedynamic
实现
使用 javap -c -p YourClass.class
反编译:
// 编译后实际调用:
0: invokedynamic #2, 0 // CallSite bootstrap: java/lang/invoke/LambdaMetafactory.metafactory
✅ Lambda 的创建被推迟到运行时,由
LambdaMetafactory
动态生成实现类!
invokedynamic
核心机制:三大组件
1. 调用点(Call Site)
- 运行时可变的方法引用
- 如
ConstantCallSite
、MutableCallSite
2. 引导方法(Bootstrap Method)
- 在
.class
文件中定义 - 负责初始化
CallSite
- Lambda 使用
LambdaMetafactory.metafactory()
3. 方法句柄(MethodHandle)
- JDK 7 引入,“强类型的反射”
- 比反射更快、更安全
// MethodHandle 示例
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findStatic(System.class, "currentTimeMillis", MethodType.methodType(long.class));
long time = (long) mh.invokeExact();
✅
MethodHandle
可被 JIT 优化,性能接近直接调用!
实战:Lambda 字节码分析
源码
public class LambdaDemo {public static void main(String[] args) {List<String> list = Arrays.asList("A", "B", "C");list.forEach(s -> System.out.println(s));}
}
反编译结果(关键部分)
main([Ljava/lang/String;)V// ... 初始化 listaload_1invokedynamic #2, 0 // InvokeDynamic #0:accept:()Ljava/util/function/Consumer;invokevirtual List.forEach(Ljava/util/function/Consumer;)Vreturn// 引导方法
BootstrapMethods:0: #27 invokestatic java/lang/invoke/LambdaMetafactory.metafactoryMethod arguments:#28 MethodType interface java/util/function/Consumer.accept:(Ljava/lang/Object;)V#30 invokestatic LambdaDemo.lambda$main$0:(Ljava/lang/String;)V#28 MethodType interface java/util/function/Consumer.accept:(Ljava/lang/Object;)V
📌 解读:
invokedynamic
调用LambdaMetafactory.metafactory
- 引导方法返回一个
CallSite
,指向lambda$main$0
静态方法 - Lambda 实例在运行时生成,不生成
.class
文件(默认)
✅ 这就是 Lambda 启动更快、内存更省的原因!