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

Java 8 動態(tài)類型語言Lambda表達式實現原理解析

 更新時間:2017年02月08日 08:43:54   作者:raintungli  
Java 8支持動態(tài)語言,看到了很酷的Lambda表達式,對一直以靜態(tài)類型語言自居的Java,讓人看到了Java虛擬機可以支持動態(tài)語言的目標。接下來通過本文給大家介紹Java 8 動態(tài)類型語言Lambda表達式實現原理分析,需要的朋友可以參考下

Java 8支持動態(tài)語言,看到了很酷的Lambda表達式,對一直以靜態(tài)類型語言自居的Java,讓人看到了Java虛擬機可以支持動態(tài)語言的目標。

import java.util.function.Consumer; 
public class Lambda { 
  public static void main(String[] args) { 
    Consumer<String> c = s -> System.out.println(s); 
    c.accept("hello lambda!"); 
  } 
} 

剛看到這個表達式,感覺java的處理方式是屬于內部匿名類的方式

public class Lambda { 
  static { 
    System.setProperty("jdk.internal.lambda.dumpProxyClasses", "."); 
  } 
  public static void main(String[] args) { 
    Consumer<String> c = new Consumer<String>(){ 
      @Override 
      public void accept(String s) { 
        System.out.println(s); 
      } 
      }; 
    c.accept("hello lambda"); 
  } 
} 

編譯的結果應該是Lambda.class , Lambda$1.class 猜測在支持動態(tài)語言java換湯不換藥,在最后編譯的時候生成我們常見的方式。

但是結果不是這樣的,只是產生了一個Lambda.class

反編譯吧,來看看真相是什么?

javap -v -p Lambda.class 

注意  -p 這個參數 -p 參數會顯示所有的方法,而不帶默認是不會反編譯private 的方法的

public Lambda(); 
  descriptor: ()V 
  flags: ACC_PUBLIC 
  Code: 
   stack=1, locals=1, args_size=1 
     0: aload_0 
     1: invokespecial #21         // Method java/lang/Object."<init>":()V 
     4: return 
   LineNumberTable: 
    line 3: 0 
   LocalVariableTable: 
    Start Length Slot Name  Signature 
      0    5   0 this  LLambda; 
 public static void main(java.lang.String[]); 
  descriptor: ([Ljava/lang/String;)V 
  flags: ACC_PUBLIC, ACC_STATIC 
  Code: 
   stack=2, locals=2, args_size=1 
     0: invokedynamic #30, 0       // InvokeDynamic #0:accept:()Ljava/util/function/Consumer; 
     5: astore_1 
     6: aload_1 
     7: ldc      #31         // String hello lambda 
     9: invokeinterface #33, 2      // InterfaceMethod java/util/function/Consumer.accept:(Ljava/lang/Object;)V 
    14: return 
   LineNumberTable: 
    line 8: 0 
    line 9: 6 
    line 10: 14 
   LocalVariableTable: 
    Start Length Slot Name  Signature 
      0   15   0 args  [Ljava/lang/String; 
      6    9   1   c  Ljava/util/function/Consumer; 
   LocalVariableTypeTable: 
    Start Length Slot Name  Signature 
      6    9   1   c  Ljava/util/function/Consumer<Ljava/lang/String;>; 
 private static void lambda$0(java.lang.String); 
  descriptor: (Ljava/lang/String;)V 
  flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC 
  Code: 
   stack=2, locals=1, args_size=1 
     0: getstatic   #46         // Field java/lang/System.out:Ljava/io/PrintStream; 
     3: aload_0 
     4: invokevirtual #50         // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     7: return 
   LineNumberTable: 
    line 8: 0 
   LocalVariableTable: 
    Start Length Slot Name  Signature 
      0    8   0   s  Ljava/lang/String; 
} 
SourceFile: "Lambda.java" 
BootstrapMethods: 
 0: #66 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; 
  Method arguments: 
   #67 (Ljava/lang/Object;)V 
   #70 invokestatic Lambda.lambda$0:(Ljava/lang/String;)V 
   #71 (Ljava/lang/String;)V 
InnerClasses: 
   public static final #77= #73 of #75; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles 

在這里我們發(fā)現了幾個與我們常見的java不太一樣的地方,由于常量定義太多了,文章中就不貼出了

1. Invokedynamic 指令

Java的調用函數的四大指令(invokevirtual、invokespecial、invokestatic、invokeinterface),通常方法的符號引用在靜態(tài)類型語言編譯時就能產生,而動態(tài)類型語言只有在運行期才能確定接收者類型,改變四大指令的語意對java的版本有很大的影響,所以在JSR 292 《Supporting Dynamically Typed Languages on the Java Platform》添加了一個新的指令

Invokedynamic

0: invokedynamic #30,  0             // InvokeDynamic #0:accept:()Ljava/util/function/Consumer; 

 #30 是代表常量#30 也就是后面的注釋InvokeDynamic #0:accept:()Ljava/util/function/Consumer;

0 是占位符號,目前無用

2. BootstrapMethods

每一個invokedynamic指令的實例叫做一個動態(tài)調用點(dynamic call site), 動態(tài)調用點最開始是未鏈接狀態(tài)(unlinked:表示還未指定該調用點要調用的方法), 動態(tài)調用點依靠引導方法來鏈接到具體的方法.  引導方法是由編譯器生成, 在運行期當JVM第一次遇到invokedynamic指令時, 會調用引導方法來將invokedynamic指令所指定的名字(方法名,方法簽名)和具體的執(zhí)行代碼(目標方法)鏈接起來, 引導方法的返回值永久的決定了調用點的行為.引導方法的返回值類型是java.lang.invoke.CallSite, 一個invokedynamic指令關聯一個CallSite, 將所有的調用委托到CallSite當前的target(MethodHandle)
InvokeDynamic #0 就是BootstrapMethods表示#0的位置

0: #66 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; 
 Method arguments: 
  #67 (Ljava/lang/Object;)V 
  #70 invokestatic Lambda.lambda$0:(Ljava/lang/String;)V 
  #71 (Ljava/lang/String;)V 

我們看到調用了LambdaMetaFactory.metafactory 的方法

參數:

LambdaMetafactory.metafactory(Lookup, String, MethodType, MethodType, MethodHandle, MethodType)有六個參數, 按順序描述如下

1. MethodHandles.Lookup caller : 代表查找上下文與調用者的訪問權限, 使用invokedynamic指令時, JVM會自動自動填充這個參數

2. String invokedName : 要實現的方法的名字, 使用invokedynamic時, JVM自動幫我們填充(填充內容來自常量池InvokeDynamic.NameAndType.Name), 在這里JVM為我們填充為 "apply", 即Consumer.accept方法名.

3. MethodType invokedType : 調用點期望的方法參數的類型和返回值的類型(方法signature). 使用invokedynamic指令時, JVM會自動自動填充這個參數(填充內容來自常量池InvokeDynamic.NameAndType.Type), 在這里參數為String, 返回值類型為Consumer, 表示這個調用點的目標方法的參數為String, 然后invokedynamic執(zhí)行完后會返回一個即Consumer實例.

4. MethodType samMethodType :  函數對象將要實現的接口方法類型, 這里運行時, 值為 (Object)Object 即 Consumer.accept方法的類型(泛型信息被擦除).#67 (Ljava/lang/Object;)V

5. MethodHandle implMethod : 一個直接方法句柄(DirectMethodHandle), 描述在調用時將被執(zhí)行的具體實現方法 (包含適當的參數適配, 返回類型適配, 和在調用參數前附加上捕獲的參數), 在這里為 #70 invokestatic Lambda.lambda$0:(Ljava/lang/String;)V 方法的方法句柄.

6. MethodType instantiatedMethodType : 函數接口方法替換泛型為具體類型后的方法類型, 通常和 samMethodType 一樣, 不同的情況為泛型:

比如函數接口方法定義為 void accept(T t)  T為泛型標識, 這個時候方法類型為(Object)Void,  在編譯時T已確定, 即T由String替換, 這時samMethodType就是 (Object)Void, 而instantiatedMethodType為(String)Void.

第4, 5, 6 三個參數來自class文件中的. 如上面引導方法字節(jié)碼中Method arguments后面的三個參數就是將應用于4, 5, 6的參數.

Method arguments: 
  #67 (Ljava/lang/Object;)V 
  #70 invokestatic Lambda.lambda$0:(Ljava/lang/String;)V 
  #71 (Ljava/lang/String;)V 

我們來看metafactory 的方法里的實現代碼

public static CallSite metafactory(MethodHandles.Lookup caller, 
                    String invokedName, 
                    MethodType invokedType, 
                    MethodType samMethodType, 
                    MethodHandle implMethod, 
                    MethodType instantiatedMethodType) 
      throws LambdaConversionException { 
    AbstractValidatingLambdaMetafactory mf; 
    mf = new InnerClassLambdaMetafactory(caller, invokedType, 
                       invokedName, samMethodType, 
                       implMethod, instantiatedMethodType, 
                       false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY); 
    mf.validateMetafactoryArgs(); 
    return mf.buildCallSite(); 
  } 

在buildCallSite的函數中

CallSite buildCallSite() throws LambdaConversionException { 
    final Class<?> innerClass = spinInnerClass(); 

函數spinInnerClass 構建了這個內部類,也就是生成了一個Lambda$$Lambda$1/716157500 這樣的內部類,這個類是在運行的時候構建的,并不會保存在磁盤中,如果想看到這個構建的類,可以通過設置環(huán)境參數

System.setProperty("jdk.internal.lambda.dumpProxyClasses", "."); 

會在你指定的路徑 . 當前運行路徑上生成這個內部類

3.靜態(tài)類

Java在編譯表達式的時候會生成lambda$0靜態(tài)私有類,在這個類里實現了表達式中的方法塊 system.out.println(s);

private static void lambda$0(java.lang.String); 
  descriptor: (Ljava/lang/String;)V 
  flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC 
  Code: 
   stack=2, locals=1, args_size=1 
     0: getstatic   #46         // Field java/lang/System.out:Ljava/io/PrintStream; 
     3: aload_0 
     4: invokevirtual #50         // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     7: return 
   LineNumberTable: 
    line 8: 0 
   LocalVariableTable: 
    Start Length Slot Name  Signature 
      0    8   0   s  Ljava/lang/String;

當然了在上一步通過設置的jdk.internal.lambda.dumpProxyClasses里生成的Lambda$$Lambda$1.class

public void accept(java.lang.Object); 
  descriptor: (Ljava/lang/Object;)V 
  flags: ACC_PUBLIC 
  Code: 
   stack=1, locals=2, args_size=2 
    0: aload_1 
    1: checkcast   #15         // class java/lang/String 
    4: invokestatic #21         // Method Lambda.lambda$0:(Ljava/lang/String;)V 
    7: return 
  RuntimeVisibleAnnotations: 
   0: #13() 

調用了Lambda.lambda$0靜態(tài)函數,也就是表達式中的函數塊

總結

這樣就完成的實現了Lambda表達式,使用invokedynamic指令,運行時調用LambdaMetafactory.metafactory動態(tài)的生成內部類,實現了接口,內部類里的調用方法塊并不是動態(tài)生成的,只是在原class里已經編譯生成了一個靜態(tài)的方法,內部類只需要調用該靜態(tài)方法

以上所述是小編給大家介紹的Java 8 動態(tài)類型語言Lambda表達式實現原理解析,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!

相關文章

  • SpringBoot的依賴管理配置

    SpringBoot的依賴管理配置

    一般來講SpringBoot項目是不需要指定版本,而SSM項目是需要指定版本,SpringBoot的核心依賴就是spring-boot-starter-parent和spring-boot-starter-web兩個依賴,關于這兩個依賴的相關介紹具體今天小編給大家介紹下
    2022-07-07
  • Java17和springboot3.0使用shiro報ClassNotFoundException的解決

    Java17和springboot3.0使用shiro報ClassNotFoundException的解決

    本文主要介紹了Java17和springboot3.0使用shiro報ClassNotFoundException的解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-04-04
  • SpringBoot中日志切面實現小結

    SpringBoot中日志切面實現小結

    本文介紹了SpringBoot中日志切面實現小結,通過定義一個自定義注解和創(chuàng)建一個日志切面類,為方法添加日志記錄功能,感興趣的可以了解一下
    2024-11-11
  • java中xml進行報文發(fā)送和解析操作

    java中xml進行報文發(fā)送和解析操作

    這篇文章主要介紹了java中xml進行報文發(fā)送和解析操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • java文件上傳下載功能實現代碼

    java文件上傳下載功能實現代碼

    這篇文章主要為大家詳細介紹了java文件上傳下載功能實現代碼,具有一定的參考價值,感興趣的朋友可以參考一下
    2016-06-06
  • SWT(JFace) 圖片瀏覽器 實現代碼

    SWT(JFace) 圖片瀏覽器 實現代碼

    SWT(JFace)小制作:圖片瀏覽器
    2009-06-06
  • Java Web Filter 過濾器學習教程(推薦)

    Java Web Filter 過濾器學習教程(推薦)

    Filter也稱之為過濾器,它是Servlet技術中最激動人心的技術.這篇文章主要介紹了Java Web Filter 過濾器學習教程的相關資料,需要的朋友可以參考下
    2016-05-05
  • Scala中如何中斷循環(huán)詳解

    Scala中如何中斷循環(huán)詳解

    在Scala中,可以使用break和continue語句來中斷循環(huán)。break語句可以在滿足某個條件時中斷循環(huán),而continue語句可以在滿足某個條件時跳過當前循環(huán)迭代,直接執(zhí)行下一次迭代
    2023-04-04
  • Java8中使用流方式查詢數據庫的方法

    Java8中使用流方式查詢數據庫的方法

    這篇文章主要介紹了Java8中使用流方式查詢數據庫的相關資料,需要的朋友可以參考下
    2016-01-01
  • JAVA隨機數隨機字母的實現(微信搶紅包小練習)

    JAVA隨機數隨機字母的實現(微信搶紅包小練習)

    這篇文章主要介紹了JAVA隨機數隨機字母的實現(微信搶紅包小練習),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-04-04

最新評論

淮滨县| 黄梅县| 襄城县| 商丘市| 德保县| 灵宝市| 蒲城县| 莱西市| 伊吾县| 郁南县| 宜黄县| 湖南省| 翁牛特旗| 灵璧县| 富锦市| 桂阳县| 余江县| 尼玛县| 江源县| 新津县| 常熟市| 长泰县| 华蓥市| 鲜城| 宁德市| 宁陕县| 容城县| 陕西省| 清涧县| 抚顺县| 图木舒克市| 新郑市| 龙门县| 崇州市| 宁南县| 黄龙县| 贵溪市| 云林县| 乡宁县| 资中县| 台州市|