Java将Lambda表达式对应的类保存到class文件中的方法示例

 更新时间:2026年05月19日 08:32:07   作者:金銀銅鐵  
大家在日常开发中应该都用过 Lambda表达式,以下面的 java代码为例,我们在运行 HelloWorld中的 main方法时,可以看到 r的类型信息,但是在编译时、运行时都没有看到 r对应的类的 class文件,本文给大家介绍了Java将Lambda表达式对应的类保存到class文件中的方法示例

背景

大家在日常开发中应该都用过 Lambda表达式。以下面的 java代码为例,我们在运行 HelloWorld中的 main方法时,可以看到 r的类型信息,但是在编译时、运行时都没有看到 r对应的类的 class文件,有没有办法将 r对应的类保存到 class文件里呢?本文会对此进行探讨。

public class HelloWorld {
    public static void main(String[] args) {
        Runnable r = () -> System.out.println("Hello World");
        r.run();
        System.out.println("class name of r: " + r.getClass().getName());	
    }
}

要点

运行 java命令时,使用以下两个选项中的任何一个,都可以把 Lambda表达式对应的类自动保存到 class 文件中

  • -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles
  • -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true

正文

javac版本和java版本

查看javac版本

使用如下命令可以查看 javac命令的版本

javac -version

该命令在我电脑上的运行结果如下

javac 21

查看java版本

使用如下命令可以查看 java命令的版本

java -version

该命令在我电脑上的运行结果如下

openjdk version "21" 2023-09-19
OpenJDK Runtime Environment (build 21+35-2513)
OpenJDK 64-Bit Server VM (build 21+35-2513, mixed mode, sharing)

代码

请将如下代码保存为 HelloWorld.java ⬇️

public class HelloWorld {
    public static void main(String[] args) {
        Runnable r = () -> System.out.println("Hello World");
        r.run();
        System.out.println("class name of r: " + r.getClass().getName());	
    }
}

编译

使用如下的命令可以编译 HelloWorld.java\

javac -parameters HelloWorld.java

编译之后,执行 tree . 命令,会看到这样的结果 ⬇️ (当前目录多了 HelloWorld.class 文件)

.
├── HelloWorld.class
└── HelloWorld.java

1 directory, 2 files

运行

使用如下的命令可以编译 HelloWorld中的 main方法 ⬇️

java HelloWorld

在我电脑上,该命令的运行结果如下 ⬇️ (在您的电脑上,最后一行输出的类名可能是其他内容)

Hello World
class name of r: HelloWorld$$Lambda/0x000000d001000a00

看来 r的精确类型(为了便于描述,我们把它简称为 L吧)是一个很特殊的类(至少命名很特殊😂)。在运行完 HelloWorld中的 main方法之后,当前目录并没有新的文件生成。如果我们想看看 L的内容,一个思路是让 java虚拟机把 L保存到 class文件里(我们可以用 javap命令来查看 class文件的内容)。

在 InnerClassLambdaMetafactory.java 里可以找到如下的选项 ⬇️

看起来只要加上以下两个选项的任何一个,就可以将 r对应的类保存到 class文件里

  • -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles
  • -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true

我们就用第一种方法吧,完整的命令如下 ⬇️

java -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles HelloWorld

运行完这个命令后,再运行 tree . 命令,会得到以下结果 ⬇️ (在您的电脑上,所看到的 class\text{class}class 文件的名称可能会有差异)

.
├── DUMP_LAMBDA_PROXY_CLASS_FILES
│   └── HelloWorld$$Lambda.0x000000f801000a00.class
├── HelloWorld.class
└── HelloWorld.java
2 directories, 3 files

当前目录多了一个名为 DUMP_LAMBDA_PROXY_CLASS_FILES 的目录,这个目录下有一个 class文件。我们用如下的命令可以查看这个 class文件的内容

javap -v -p DUMP_LAMBDA_PROXY_CLASS_FILES/*class

我电脑上的运行结果如下 ⬇️ (这里略去了结果中的前三行)

final class HelloWorld$$Lambda implements java.lang.Runnable
  minor version: 0
  major version: 65
  flags: (0x1030) ACC_FINAL, ACC_SUPER, ACC_SYNTHETIC
  this_class: #2                          // HelloWorld$$Lambda
  super_class: #4                         // java/lang/Object
  interfaces: 1, fields: 0, methods: 2, attributes: 0
Constant pool:
   #1 = Utf8               HelloWorld$$Lambda
   #2 = Class              #1             // HelloWorld$$Lambda
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             // java/lang/Object
   #5 = Utf8               java/lang/Runnable
   #6 = Class              #5             // java/lang/Runnable
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = NameAndType        #7:#8          // "<init>":()V
  #10 = Methodref          #4.#9          // java/lang/Object."<init>":()V
  #11 = Utf8               run
  #12 = Utf8               HelloWorld
  #13 = Class              #12            // HelloWorld
  #14 = Utf8               lambda$main$0
  #15 = NameAndType        #14:#8         // lambda$main$0:()V
  #16 = Methodref          #13.#15        // HelloWorld.lambda$main$0:()V
  #17 = Utf8               Code
{
  private HelloWorld$$Lambda();
    descriptor: ()V
    flags: (0x0002) ACC_PRIVATE
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #10                 // Method java/lang/Object."<init>":()V
         4: return

  public void run();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=0, locals=1, args_size=1
         0: invokestatic  #16                 // Method HelloWorld.lambda$main$0:()V
         3: return
}

由于它的内容并不多,我们可以尝试手动反编译它,反编译的结果如下 ⬇️

// 以下内容是我手动反编译的结果,不保证完全准确,仅供参考

final class HelloWorld$$Lambda implements java.lang.Runnable {
  private HelloWorld$$Lambda() {
    super();
  }

  public void run() {
      HelloWorld.lambda$main$0();
  }
}

可以看到 run() 方法里会调用 HelloWorld 中的静态方法 lambda$main$0。但是我们在 Hello.java中并没有定义这样的静态方法,那我们顺便把 HelloWorld.class的内容也看一下吧 ⬇️ (使用如下的命令就可以查看 HelloWorld.class文件的详细内容)

javap -v -p HelloWorld

完整的结果有点长,这里就不展示了。我手动对 HelloWorld.class\text{HelloWorld.class}HelloWorld.class 的内容进行了反编译,结果如下 ⬇️

// 以下内容是我手动反编译的结果,不保证完全准确,仅供参考

public class HelloWorld {
  public HelloWorld() {
    super();
  }
  
  public static void main(String[] arg) {
    // 这里涉及一些特殊字节码指令,有点复杂,所以略去具体的内容
  }
  
  // 这是一个合成方法,原始的 java 代码中没有它
  private static void lambda$main$0() {
    System.out.println("Hello World");
  }
}

简要的类图如下 ⬇️

其他

绘制“简要的类图”所用到的代码

我用了 IntelliJ IDEA 中的 PlantUML 插件来画那张图,完整的代码如下 ⬇️

@startuml
'https://plantuml.com/component-diagram

title 简要的类图
caption \n\n
' caption 的内容是为了防止掘金平台生成的水印遮盖图中的文字

class HelloWorld {
  + HelloWorld()
  + {static} void main(String[] arg)
  - {static} void lambda$main$0()
}

interface java.lang.Runnable
class HelloWorld$$Lambda
java.lang.Runnable <|.. HelloWorld$$Lambda

interface java.lang.Runnable {
    void run()
}

class HelloWorld$$Lambda {
  - HelloWorld$$Lambda()
  + void run()
}

note left of HelloWorld$$Lambda::run
<code>
public void run() {
  HelloWorld.lambda$main$0();
}
</code>
end note

note left of HelloWorld::lambda$main$0
这是一个合成方法,
可以认为它的代码是这样的 <:point_down:>
<code>
private static void lambda$main$0() {
  System.out.println("Hello World");
}
</code>
end note

@enduml

以上就是Java将Lambda表达式对应的类保存到class文件中的方法示例的详细内容,更多关于Java将Lambda对应类保存到class的资料请关注脚本之家其它相关文章!

相关文章

  • Java中枚举类型的一种使用方式

    Java中枚举类型的一种使用方式

    这篇文章主要介绍了Java中枚举类型的一种使用方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • 详解Java内部类与对象的打印概念和流程

    详解Java内部类与对象的打印概念和流程

    在 Java 中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类
    2021-10-10
  • Spring IOC 和 AOP 从入门到精通

    Spring IOC 和 AOP 从入门到精通

    本文将深入浅出地讲解Spring框架的两大核心特性——IOC(控制反转)和AOP(面向切面编程),本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2026-03-03
  • JDK源码之线程并发协调神器CountDownLatch和CyclicBarrier详解

    JDK源码之线程并发协调神器CountDownLatch和CyclicBarrier详解

    我一直认为程序是对于现实世界的逻辑描述,而在现实世界中很多事情都需要各方协调合作才能完成,就好比完成一个平台的交付不可能只靠一个人,而需要研发、测试、产品以及项目经理等不同角色人员进行通力合作才能完成最终的交付
    2022-02-02
  • java获取机器码简单实现demo

    java获取机器码简单实现demo

    这篇文章主要为大家介绍了java获取机器码的简单实现demo,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Java中ArrayList的removeAll方法详解

    Java中ArrayList的removeAll方法详解

    这篇文章主要给大家介绍了关于Java中ArrayList的removeAll方法的相关资料,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面跟着小编一起来看看吧。
    2017-07-07
  • Spring组件初始化扩展点BeanPostProcessor的作用详解

    Spring组件初始化扩展点BeanPostProcessor的作用详解

    本文通过实战案例和常见应用场景详细介绍了BeanPostProcessor的使用,并强调了其在Spring扩展中的重要性,感兴趣的朋友一起看看吧
    2025-03-03
  • JVM的垃圾回收算法工作原理详解

    JVM的垃圾回收算法工作原理详解

    这篇文章主要介绍了JVM的垃圾回收算如何判断对象是否可以被回收,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,,需要的朋友可以参考下
    2019-06-06
  • 一文详解MQ消息丢失问题的5种解决方案

    一文详解MQ消息丢失问题的5种解决方案

    有些小伙伴在工作中,一提到消息队列就觉得很简单,但真正遇到线上消息丢失时,排查起来却让人抓狂,今天这篇文章,专门跟大家一起聊聊这个话题,希望对你会有所帮助
    2025-10-10
  • java Date获取年月日时分秒的实现方法

    java Date获取年月日时分秒的实现方法

    下面小编就为大家带来一篇java Date获取年月日时分秒的实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06

最新评论