
文章收录专栏Java 核心原理全解源码・并发・面试实战系列②把局部和匿名内部类讲完了。这一篇是重头戏Lambda 与匿名内部类的本质差异。网上 90% 的文章说 “Lambda 是匿名内部类的语法糖”——这是错的。两者在编译产物、外部引用捕获、this 语义、序列化能力上有本质区别。这一篇讲透面试直接封神。一、Lambda 核心特性编译阶段不生成静态 class 文件运行期通过invokedynamic动态生成隐藏类。调试导出动态类的标准 JVM 参数-Djdk.internal.lambda.dumpProxyClasses/输出目录。Lambda没有独立词法作用域this 直接继承外层上下文。class Demo { int x 1; static int s 1; Runnable noCapture() { return () - System.out.println(s); // 只用静态 → 不捕获 this } Runnable capture() { return () - System.out.println(this.x); // 访问实例字段 → 捕获 this } Runnable anon() { return new Runnable() { // 匿名内部类无条件持有 this$0 Override public void run() {} }; } }二、匿名内部类 vs Lambda 精准对比维度匿名内部类Lambda 表达式编译产物静态 class 文件Outer$N.class无静态文件运行期 invokedynamic 动态生成外部引用实例上下文无条件持有 this$0无论用没用到外部成员条件捕获只有 Lambda 体访问外部实例字段 / 实例方法时才捕获 this只用静态资源、局部变量时不持有成员能力可自定义字段、方法、重写 Object 方法不能自定义成员不能重写 Object 方法this 语义拥有独立 this直接继承外层 this异常堆栈出现 Outer$N 类名展示 Lambda 方法名无实体类名性能每次实例化开销固定首次执行有微小开销动态生成后续通常更优三、重点展开条件捕获 this这是两者差异最核心、也最容易被忽略的一点。匿名内部类只要在实例上下文创建无条件生成 this$0即使它的方法体根本没用到外部任何成员 —— 这就是为什么 “看起来人畜无害” 的匿名内部类也常常造成内存泄漏。Lambda编译器会精确分析只有 Lambda 体真正访问外部实例字段 / 实例方法时才捕获 this只用静态资源和局部变量的 Lambda 不持有外部实例引用。所以同样一段代码用 Lambda 往往更省内存、更安全。但要注意间接引用链如果 Lambda 捕获的局部变量自身持有外部实例依旧会形成引用链带来隐性泄漏。四、Lambda 序列化生产红线Lambda默认不可序列化。强制强转(Runnable Serializable)后仍然要求所有被捕获变量都可序列化。底层依赖SerializedLambda记录方法元信息类重命名、方法改名、方法签名变更、类移动都会造成反序列化失败。业务代码禁止序列化 Lambda仅底层框架可有限使用。// 想序列化 Lambda强转也只是看起来可以 Runnable r (Runnable Serializable) () - {}; // 底层靠 SerializedLambda 记录元信息类一改名就反序列化失败五、选型原则简单函数回调优先 Lambda。需要自定义成员、继承普通类、多抽象方法、需要自身 this使用匿名内部类。一句话能用 Lambda 就用 Lambda需要 “类的完整能力” 时才退回匿名内部类。六、小结与下篇预告Lambda 不是匿名内部类的语法糖而是底层机制完全不同的另一种实现 ——invokedynamic 动态生成、条件捕获、继承外层 this、默认不可序列化。把这四点讲清面试官就知道你是真懂不是背的。下一篇是系列收官底层合成机制与版本演进——access$xxx、nestmates、JDK18编译优化、内部类继承、序列化规范外加全套面试背诵表。关注「木雨」收官篇见。