1、Demo
首先我們來看一個Lambda 表達式的Demo,如下圖:
程式碼比較簡單,就是新起一個執行緒印出一句話,但對於圖中() -> System.out.println ( “ lambda is run “ ) 這種代碼,估計很多同學都感覺到很困惑,Java 是怎麼辨識這種代碼的?
如果我們修改成匿名內部類別的寫法,就很清楚,大家都能看懂,如下圖:
那是不是說() -> System.out.println ( “ lambda is run “ ) 這種形式的程式碼,其實就是建立了內部類別呢?其實這就是最簡單 Lambda 表達式,我們是無法透過 IDEA 看到原始碼和其底層結構的,下面我們就來介紹幾種可看到其底層實現的方式。
2、異常判斷法
我們可以在程式碼執行中主動拋出異常,列印出堆疊,堆疊會說明其運行軌跡,一般這種方法簡單高效,基本上可以看到很多情況下的隱藏程式碼,我們來試一下,如下圖:
#從異常的堆疊中,我們可以看到JVM 自動給當前類別建立了內部類(錯誤堆疊中出現多次的$ 表示有內部類別),內部類別的程式碼在執行過程中,拋出了異常,但這裡顯示的程式碼是Unknown Source,所以我們也無法debug 進去,一般情況下,異常都能暴露出程式碼執行的路徑,我們可以打好斷點後再次運行,但對於Lambda 表達式而言,透過異常判斷法我們只清楚有內部類別,但無法看到內部類別中的原始碼。
3、javap 指令法
javap 是Java 內建的可以檢視class 字節碼檔案的工具,安裝過Java 基礎環境的電腦都可以直接執行javap 指令,如下圖:
在指令選項中,我們主要是用-v -verbose 這個指令,可以完整輸出字節碼檔案的內容。
接下來我們使用 javap 指令查看下 Lambda.class 文件,在講解的過程中,我們會帶一些關於 class 檔案的知識。
我們在指令視窗中找到Lambda.class 所在的位置,執行指令:javap -verbose Lambda.class,然後你會看到一長串的東西,這些叫做彙編指令,接下來我們來一一講解下( 所有的參考資料來自Java 虛擬機規範,不再一一引用說明):
彙編指令中我們很容易找到Constant pool 打頭的一長串類型,我們叫做常數池,官方英文叫做Run-Time Constant Pool,我們簡單理解成一個裝滿常數的table ,table 中包含編譯時明確的數字和文字,類別、方法和字段的類型資訊等等。 table 中的每個元素叫做cpinfo,cpinfo 由唯一識別( tag ) 名稱組成,目前tag 的型別一共有:
- 圖中Constant pool 字樣代表目前資訊是常數池;
- 每行都是一個
cp_info
,第一列的#1 代表是在常數池下標為1 的位置;
- 每行的第二列,是
cp_info
的唯一識別( tag ) ,例如Methodref 對應著上表中的CONSTANT_Methodref(上上圖中表格中value 對應10 的tag),代表當前行是表示方法的描述資訊的,比如說方法的名稱,入參類型,出參數型別等,具體的意義在Java 虛擬機器規格中都可以查詢到,Methodref 的截圖如下:
##每行的第三列,如果是具體的值的話,直接顯示具體的值,如果是複雜的值的話,會顯示 - cp_info
的引用,比如說圖中標紅2 處,引用兩個13 和14 位置的
cp_info
,13 表示方法名字是init,14 表示方法沒有回傳值,結合起來表示方法的名稱和回傳類型,就是一個無參構造子; 每行的第四列,就是具體的值了。 - 對於比較重要的 cp_info 類型我們說明下其意義:
InvokeDynamic 表示動態的呼叫方法,後面我們會詳細說明;
Fieldref 表示欄位的描述訊息,如欄位的名稱、類型;
NameAndType 是欄位和方法類型的描述;
#MethodHandle 方法句柄,動態呼叫方法的統稱,在編譯時我們不知道具體是那個方法,但運行時肯定會知道呼叫的是那個方法;
MethodType 動態方法類型,只有在動態運行時才會知道其方法類型是什麼。
我們從上方上圖標示紅色的3 處,發現Ljava/lang/invoke/MethodHandles$Lookup,java/lang/invoke/LambdaMetafactory.metafactory 類似這樣的程式碼,MethodHandles 和LambdaMetafactory 都是java.lang.invoke 套件下面的重要方法,invoke 套件主要實現了動態語言的功能,我們知道java 語言屬於靜態編譯語言,在編譯的時候,類別、方法、字段等等的類型都已經確定了,而invoke 實現的是一種動態語言,也就是說編譯的時候並不知道類別、方法、字段是什麼類型,只有到運行的時候才會知道。
例如這行程式碼:Runnable runnable = () -> System.out.println(“lambda is run”); 在編譯器編譯的時候() 這個括號編譯器並不知道是做什麼的,只有在運作的時候,才會知道原來這代表的是Runnable.run() 方法。 invoke 套件裡面很多類,都是為了代表這些() 的,我們稱作為方法句柄( MethodHandler ),在編譯的時候,編譯器只知道這裡是個方法句柄,並不知道實際上執行什麼方法,只有在執行的時候才知道,那麼問題來了,JVM 執行的時候,是如何知道() 這個方法句柄,實際上是執行Runnable.run() 方法的呢?
首先我們看下simple 方法的組譯指令:
#從上圖就可以看出simple 方法中的() -> System. out.println(“lambda is run”) 程式碼中的(),其實就是Runnable.run 方法。
我們追溯到# 2 常數池,也就是上上圖中標紅1 處,InvokeDynamic 表示這裡是個動態調用,調用的是兩個常數池的cp_info,位置是#0:#37 ,我們往下找#37 代表著是// run:()Ljava/lang/Runnable,這裡顯示了在JVM 真正執行的時候,需要動態呼叫Runnable.run() 方法,從組譯指令上我們可以看出()其實就是Runnable.run(),下面我們debug 來證明一下。
我們在上上圖3 處發現了LambdaMetafactory.metafactory 的字樣,透過查詢官方文檔,得知該方法正是執行時, 連結到真正程式碼的關鍵,於是我們在metafactory 方法中打個斷點debug 一下,如下圖:
metafactory 方法入參caller 代表實際發生動態呼叫的位置,invokedName 表示呼叫方法名稱,invokedType 表示呼叫的多個入參和出參,samMethodType 表示具體的實現者的參數,implMethod 表示實際上的實作者,instantiatedMethodType 等同於implMethod。
以上內容總結一下:
1:從組譯指令的simple 方法中,我們可以看到會執行Runnable.run 方法;
2:在實際的執行時,JVM 碰到simple 方法的invokedynamic 指令,會動態呼叫LambdaMetafactory.metafactory 方法,並執行特定的Runnable.run 方法。
所以可以把Lambda 表達值的具體執行歸功於invokedynamic JVM 指令,正是因為這個指令,才可以做到雖然編譯時不知道要幹啥,但動態運行時卻能找到具體要執行的代碼。
接著我們看一下在彙編指令輸出的最後,我們發現了異常判斷法中發現的內部類,如下圖:
上圖中箭頭很多,一層一層的表達清楚了當前內部類別的所有資訊。
以上是Java怎麼看Lambda原始碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于结构化数据处理开源库SPL的相关问题,下面就一起来看一下java下理想的结构化数据处理类库,希望对大家有帮助。

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于PriorityQueue优先级队列的相关知识,Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,下面一起来看一下,希望对大家有帮助。

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于java锁的相关问题,包括了独占锁、悲观锁、乐观锁、共享锁等等内容,下面一起来看一下,希望对大家有帮助。

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于多线程的相关问题,包括了线程安装、线程加锁与线程不安全的原因、线程安全的标准类等等内容,希望对大家有帮助。

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于枚举的相关问题,包括了枚举的基本操作、集合类对枚举的支持等等内容,下面一起来看一下,希望对大家有帮助。

本篇文章给大家带来了关于Java的相关知识,其中主要介绍了关于关键字中this和super的相关问题,以及他们的一些区别,下面一起来看一下,希望对大家有帮助。

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于平衡二叉树(AVL树)的相关知识,AVL树本质上是带了平衡功能的二叉查找树,下面一起来看一下,希望对大家有帮助。

封装是一种信息隐藏技术,是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法;封装可以被认为是一个保护屏障,防止指定类的代码和数据被外部类定义的代码随机访问。封装可以通过关键字private,protected和public实现。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

禪工作室 13.0.1
強大的PHP整合開發環境