Java將Lambda表達式對應的類保存到class文件中的方法示例
背景
大家在日常開發(fā)中應該都用過 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
編譯之后,執(zhí)行 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 中的靜態(tài)方法 lambda$main$0。但是我們在 Hello.java中并沒有定義這樣的靜態(tài)方法,那我們順便把 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) {
// 這里涉及一些特殊字節(jié)碼指令,有點復雜,所以略去具體的內容
}
// 這是一個合成方法,原始的 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動態(tài)獲取實現(xiàn)某個接口下所有的實現(xiàn)類對象集合
今天小編就為大家分享一篇關于Java動態(tài)獲取實現(xiàn)某個接口下所有的實現(xiàn)類對象集合,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12
Java調用Python代碼實現(xiàn)Word轉化為PDF格式
這篇文章主要為大家詳細介紹了Java如何實現(xiàn)調用Python代碼實現(xiàn)Word轉化為PDF格式,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2026-03-03
Spring boot JPA實現(xiàn)分頁和枚舉轉換代碼示例
這篇文章主要介紹了Spring boot JPA實現(xiàn)分頁和枚舉轉換代碼示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-09-09
Java數(shù)據結構之有向圖設計與實現(xiàn)詳解
有向圖是具有方向性的圖,由一組頂點和一組有方向的邊組成,每條方向的邊都連著一對有序的頂點。本文為大家介紹的是有向圖的設計與實現(xiàn),需要的可以參考一下2022-11-11

