本篇文章為大家帶來了關於java的相關知識,其中主要介紹了關於如何看Lambda源碼的相關問題,使用Lambda 表達式可以對程式碼進行大量的優化,用幾行程式碼就可以做很多事情,下面一起來看一下,希望對大家有幫助。
推薦學習:《java影片教學》
大家都知道Java8 中新增了Lambda 表達式,使用Lambda 表達式可以對程式碼進行大量的最佳化,用幾行程式碼就可以做很多事情,本章以Lambda 為例,第一小節說明一下其底層的執行原理,第二小節說明一下Lambda 流在工作中常用的姿勢。
首先我們來看一個Lambda 表達式的Demo,如下圖:
info,cpinfo 由唯一識別( tag ) 名稱組成,目前tag 的型別一共有:
##貼出我們解析出來的部分圖:
,第一列的#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 類型我們說明下其意義:
我們從上圖標示紅色的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 指令,正是因為這個指令,才可以做到雖然編譯時不知道要幹啥,但動態運行時卻能找到具體要執行的代碼。
接著我們看一下在彙編指令輸出的最後,我們發現了異常判斷法中發現的內部類,如下圖:
上圖中箭頭很多,一層一層的表達清楚了目前內部類別的所有資訊。
我們總結一下,Lambda 表達式執行主要是依靠invokedynamic 的JVM 指令來實現,咱們演示的類別的全路徑為:demo.eight.Lambda 感興趣的同學可以自己試試看。
不囉嗦,文章結束,期待三連!
推薦學習:《java影片教學》
以上是Java技巧總結如何看Lambda源碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!