Java怎麼看Lambda原始碼
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中文網其他相關文章!

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

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

Dreamweaver CS6
視覺化網頁開發工具

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

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip

PHP成為許多網站首選技術棧的原因包括其易用性、強大社區支持和廣泛應用。 1)易於學習和使用,適合初學者。 2)擁有龐大的開發者社區,資源豐富。 3)廣泛應用於WordPress、Drupal等平台。 4)與Web服務器緊密集成,簡化開發部署。

PHP適用於Web開發和內容管理系統,Python適合數據科學、機器學習和自動化腳本。 1.PHP在構建快速、可擴展的網站和應用程序方面表現出色,常用於WordPress等CMS。 2.Python在數據科學和機器學習領域表現卓越,擁有豐富的庫如NumPy和TensorFlow。
