最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java源碼難點(diǎn)突破Lambda表達(dá)式執(zhí)行原理

 更新時(shí)間:2022年03月11日 10:53:27   作者:Q.E.D  
這篇文章主要為大家介紹了Java難點(diǎn)突破Lambda表達(dá)式執(zhí)行原理分析及示例的實(shí)現(xiàn)源碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步

引導(dǎo)語

大家都知道 Java8 中新增了 Lambda 表達(dá)式,使用 Lambda 表達(dá)式可以對(duì)代碼進(jìn)行大量的優(yōu)化,用幾行代碼就可以做很多事情,本章以 Lambda 為例,第一小節(jié)說明一下其底層的執(zhí)行原理,第二小節(jié)說明一下 Lambda 流在工作中常用的姿勢(shì)。

1、Demo

首先我們來看一個(gè) Lambda 表達(dá)式的 Demo,如下圖:

代碼比較簡(jiǎn)單,就是新起一個(gè)線程打印一句話,但對(duì)于圖中 () -> System.out.println ( “ lambda is run “ ) 這種代碼,估計(jì)很多同學(xué)都感覺到很困惑,Java 是怎么識(shí)別這種代碼的? 

 如果我們修改成匿名內(nèi)部類的寫法,就很清楚,大家都能看懂,如下圖:

 那是不是說 () -> System.out.println ( “ lambda is run “ ) 這種形式的代碼,其實(shí)就是建立了內(nèi)部類呢?其實(shí)這就是最簡(jiǎn)單 Lambda 表達(dá)式,我們是無法通過 IDEA 看到源碼和其底層結(jié)構(gòu)的,下面我們就來介紹幾種可看到其底層實(shí)現(xiàn)的方式。

2、異常判斷法

我們可以在代碼執(zhí)行中主動(dòng)拋出異常,打印出堆棧,堆棧會(huì)說明其運(yùn)行軌跡,一般這種方法簡(jiǎn)單高效,基本上可以看到很多情況下的隱藏代碼,我們來試一下,如下圖:

從異常的堆棧中,我們可以看到 JVM 自動(dòng)給當(dāng)前類建立了內(nèi)部類(錯(cuò)誤堆棧中出現(xiàn)多次的 $ 表示有內(nèi)部類),內(nèi)部類的代碼在執(zhí)行過程中,拋出了異常,但這里顯示的代碼是 Unknown Source,所以我們也無法 debug 進(jìn)去,一般情況下,異常都能暴露出代碼執(zhí)行的路徑,我們可以打好斷點(diǎn)后再次運(yùn)行,但對(duì)于 Lambda 表達(dá)式而言,通過異常判斷法我們只清楚有內(nèi)部類,但無法看到內(nèi)部類中的源碼。 

3、javap 命令法

 javap 是 Java 自帶的可以查看 class 字節(jié)碼文件的工具,安裝過 Java 基礎(chǔ)環(huán)境的電腦都可以直接執(zhí)行 javap 命令,如下圖:

 命令選項(xiàng)中,我們主要是用-v -verbose 這個(gè)命令,可以完整輸出字節(jié)碼文件的內(nèi)容。

接下來我們使用 javap 命令查看下 Lambda.class 文件,在講解的過程中,我們會(huì)帶上一些關(guān)于 class 文件的知識(shí)。

我們?cè)诿畲翱谥姓业?Lambda.class 所在的位置,執(zhí)行命令:javap -verbose Lambda.class,然后你會(huì)看到一長串的東西,這些叫做匯編指令,接下來我們來一一講解下( 所有的參考資料來自 Java 虛擬機(jī)規(guī)范,不再一一引用說明):

匯編指令中我們很容易找到 Constant pool 打頭的一長串類型,我們叫做常量池,官方英文叫做 Run-Time Constant Pool,我們簡(jiǎn)單理解成一個(gè)裝滿常量的 table ,table 中包含編譯時(shí)明確的數(shù)字和文字,類、方法和字段的類型信息等等。table 中的每個(gè)元素叫做 cpinfo,cpinfo 由唯一標(biāo)識(shí) ( tag ) + 名稱組成,目前 tag 的類型一共有:

圖片描述

貼出我們解析出來的部分圖:

圖中 Constant pool 字樣代表當(dāng)前信息是常量池;

每行都是一個(gè) cp_info ,第一列的 #1 代表是在常量池下標(biāo)為 1 的位置 ;

每行的第二列,是 cp_info 的唯一標(biāo)識(shí) ( tag ) ,比如 Methodref 對(duì)應(yīng)著上表中的 CONSTANT_Methodref(上上圖中表格中 value 對(duì)應(yīng) 10 的 tag),代表當(dāng)前行是表示方法的描述信息的,比如說方法的名稱,入?yún)㈩愋停鰠?shù)類型等,具體的含義在 Java 虛擬機(jī)規(guī)范中都可以查詢到,Methodref 的截圖如下:

圖片描述

每行的第三列,如果是具體的值的話,直接顯示具體的值,如果是復(fù)雜的值的話,會(huì)顯示 cp_info 的引用,比如說圖中標(biāo)紅 2 處,引用兩個(gè) 13 和 14 位置的 cp_info,13 表示方法名字是 init,14 表示方法無返回值,結(jié)合起來表示方法的名稱和返回類型,就是一個(gè)無參構(gòu)造器;

每行的第四列,就是具體的值了。 

 對(duì)于比較重要的 cp_info 類型我們說明下其含義:

  • InvokeDynamic 表示動(dòng)態(tài)的調(diào)用方法,后面我們會(huì)詳細(xì)說明;
  • Fieldref 表示字段的描述信息,如字段的名稱、類型;
  • NameAndType 是對(duì)字段和方法類型的描述;
  • MethodHandle 方法句柄,動(dòng)態(tài)調(diào)用方法的統(tǒng)稱,在編譯時(shí)我們不知道具體是那個(gè)方法,但運(yùn)行時(shí)肯定會(huì)知道調(diào)用的是那個(gè)方法;
  • MethodType 動(dòng)態(tài)方法類型,只有在動(dòng)態(tài)運(yùn)行時(shí)才會(huì)知道其方法類型是什么。

 我們從上上圖中標(biāo)紅的 3 處,發(fā)現(xiàn) Ljava/lang/invoke/MethodHandles$Lookup,java/lang/invoke/LambdaMetafactory.metafactory 類似這樣的代碼,MethodHandles 和 LambdaMetafactory 都是 java.lang.invoke 包下面的重要方法,invoke 包主要實(shí)現(xiàn)了動(dòng)態(tài)語言的功能,我們知道 java 語言屬于靜態(tài)編譯語言,在編譯的時(shí)候,類、方法、字段等等的類型都已經(jīng)確定了,而 invoke 實(shí)現(xiàn)的是一種動(dòng)態(tài)語言,也就是說編譯的時(shí)候并不知道類、方法、字段是什么類型,只有到運(yùn)行的時(shí)候才知道。

比如這行代碼:Runnable runnable = () -> System.out.println(“lambda is run”); 在編譯器編譯的時(shí)候 () 這個(gè)括號(hào)編譯器并不知道是干什么的,只有在運(yùn)行的時(shí)候,才會(huì)知道原來這代表著的是 Runnable.run() 方法。invoke 包里面很多類,都是為了代表這些 () 的,我們稱作為方法句柄( MethodHandler ),在編譯的時(shí)候,編譯器只知道這里是個(gè)方法句柄,并不知道實(shí)際上執(zhí)行什么方法,只有在執(zhí)行的時(shí)候才知道,那么問題來了,JVM 執(zhí)行的時(shí)候,是如何知道 () 這個(gè)方法句柄,實(shí)際上是執(zhí)行 Runnable.run() 方法的呢?

首先我們看下 simple 方法的匯編指令:

從上圖中就可以看出 simple 方法中的 () -> System.out.println(“lambda is run”) 代碼中的 (),實(shí)際上就是 Runnable.run 方法。

我們追溯到 # 2 常量池,也就是上上圖中標(biāo)紅 1 處,InvokeDynamic 表示這里是個(gè)動(dòng)態(tài)調(diào)用,調(diào)用的是兩個(gè)常量池的 cp_info,位置是 #0:#37 ,我們往下找 #37 代表著是 // run:()Ljava/lang/Runnable,這里表明了在 JVM 真正執(zhí)行的時(shí)候,需要?jiǎng)討B(tài)調(diào)用 Runnable.run() 方法,從匯編指令上我們可以看出 () 實(shí)際上就是 Runnable.run(),下面我們 debug 來證明一下。

我們?cè)谏仙蠄D中 3 處發(fā)現(xiàn)了 LambdaMetafactory.metafactory 的字樣,通過查詢官方文檔,得知該方法正是執(zhí)行時(shí), 鏈接到真正代碼的關(guān)鍵,于是我們?cè)?metafactory 方法中打個(gè)斷點(diǎn) debug 一下,如下圖:

圖片描述

 metafactory 方法入?yún)?caller 代表實(shí)際發(fā)生動(dòng)態(tài)調(diào)用的位置,invokedName 表示調(diào)用方法名稱,invokedType 表示調(diào)用的多個(gè)入?yún)⒑统鰠?,samMethodType 表示具體的實(shí)現(xiàn)者的參數(shù),implMethod 表示實(shí)際上的實(shí)現(xiàn)者,instantiatedMethodType 等同于 implMethod。

以上內(nèi)容總結(jié)一下:

1:從匯編指令的 simple 方法中,我們可以看到會(huì)執(zhí)行 Runnable.run 方法;

2:在實(shí)際的運(yùn)行時(shí),JVM 碰到 simple 方法的 invokedynamic 指令,會(huì)動(dòng)態(tài)調(diào)用 LambdaMetafactory.metafactory 方法,執(zhí)行具體的 Runnable.run 方法。

所以可以把 Lambda 表達(dá)值的具體執(zhí)行歸功于 invokedynamic JVM 指令,正是因?yàn)檫@個(gè)指令,才可以做到雖然編譯時(shí)不知道要干啥,但動(dòng)態(tài)運(yùn)行時(shí)卻能找到具體要執(zhí)行的代碼。

接著我們看一下在匯編指令輸出的最后,我們發(fā)現(xiàn)了異常判斷法中發(fā)現(xiàn)的內(nèi)部類,如下圖:

圖片描述

 上圖中箭頭很多,一層一層的表達(dá)清楚了當(dāng)前內(nèi)部類的所有信息。

4、總結(jié)

 我們總結(jié)一下,Lambda 表達(dá)式執(zhí)行主要是依靠 invokedynamic 的 JVM 指令來實(shí)現(xiàn),咱們演示的類的全路徑為:demo.eight.Lambda 感興趣的同學(xué)可以自己嘗試一下。

以上就是Java源碼難點(diǎn)突破Lambda表達(dá)式執(zhí)行原理的詳細(xì)內(nèi)容,更多關(guān)于Java難點(diǎn)Lambda表達(dá)式依靠的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java遍歷并刪除Map的四種方法對(duì)比

    Java遍歷并刪除Map的四種方法對(duì)比

    在Java中,遍歷并刪除?Map?中的元素有四種常見的方法,每種方法都有其適用場(chǎng)景和優(yōu)缺點(diǎn),下面小編就來和大家詳細(xì)介紹一下這幾種方法的具體實(shí)現(xiàn)吧
    2024-10-10
  • 使用feign客戶端傳參接收不到的問題及解決

    使用feign客戶端傳參接收不到的問題及解決

    這篇文章主要介紹了使用feign客戶端傳參接收不到的問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。
    2022-03-03
  • spring?@Conditional的使用與擴(kuò)展源碼分析

    spring?@Conditional的使用與擴(kuò)展源碼分析

    這篇文章主要介紹了spring?@Conditional的使用與擴(kuò)展,這里需要注意如果Condition返回的是false,那么spirng就不會(huì)對(duì)方法或類進(jìn)行解析,具體源碼分析跟隨小編一起看看吧
    2022-03-03
  • jpa?EntityManager?復(fù)雜查詢實(shí)例

    jpa?EntityManager?復(fù)雜查詢實(shí)例

    這篇文章主要介紹了jpa?EntityManager?復(fù)雜查詢實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Idea和Docker如何集成

    Idea和Docker如何集成

    今天就介紹下idea和Docker如何集成,要集成之前,需要我們本機(jī)能夠訪問docker服務(wù),也就是我們的docker服務(wù)需要開啟遠(yuǎn)程模式,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • Java中HashTable和HashMap的區(qū)別_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java中HashTable和HashMap的區(qū)別_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    HashTable和HashMap主要的區(qū)別有:線程安全性,同步(synchronization),以及速度。接下來通過本文給大家簡(jiǎn)單介紹下HashTable和HashMap的區(qū)別,需要的的朋友參考下吧
    2017-04-04
  • SpringBoot整合Mybatis注解開發(fā)的實(shí)現(xiàn)代碼

    SpringBoot整合Mybatis注解開發(fā)的實(shí)現(xiàn)代碼

    這篇文章主要介紹了SpringBoot整合Mybatis注解開發(fā)的實(shí)現(xiàn)代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • mybatis中string和date的轉(zhuǎn)換方式

    mybatis中string和date的轉(zhuǎn)換方式

    這篇文章主要介紹了mybatis中string和date的轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java?將Excel轉(zhuǎn)為UOS的操作方法

    Java?將Excel轉(zhuǎn)為UOS的操作方法

    以.uos為后綴的文件,表示Uniform?Office?Spreadsheet文件,是一種國產(chǎn)的辦公文件格式,該格式以統(tǒng)一辦公格式(UOF)創(chuàng)建,使用XML和壓縮保存電子表格,這篇文章主要介紹了Java?將Excel轉(zhuǎn)為UOS,需要的朋友可以參考下
    2022-09-09
  • Java類加載器ClassLoader用法解析

    Java類加載器ClassLoader用法解析

    這篇文章主要介紹了Java類加載器ClassLoader用法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02

最新評(píng)論

中卫市| 梅河口市| 鄯善县| 天峻县| 兰西县| 定远县| 德阳市| 三亚市| 阿合奇县| 房山区| 山东| 镇原县| 永宁县| 黄梅县| 叙永县| 林西县| 乃东县| 西峡县| 珲春市| 榆中县| 吉安市| 镇巴县| 青州市| 哈密市| 沁阳市| 昭苏县| 绍兴县| 罗甸县| 正定县| 胶州市| 靖远县| 曲阜市| 宝鸡市| 乌审旗| 阳新县| 美姑县| 锦州市| 石首市| 阜康市| 额尔古纳市| 当涂县|