亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文搞懂Java橋接方法

 更新時(shí)間:2022年07月25日 16:43:44   作者:默念x  
這篇文章主要介紹了Java中的橋接方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

1.橋接方法簡(jiǎn)介

橋接方法是jdk1.5引入泛型后,為使java泛型方法生成的字節(jié)碼與jdk1.5版本之前的字節(jié)碼兼容由編譯器自動(dòng)生成的。

可用method.isBridge()判斷method是否是橋接方法,在生成的字節(jié)碼中會(huì)有flags標(biāo)記 ACC_BRIDGE, ACC_SYNTHETIC ,根據(jù)來自深入理解java虛擬機(jī)的一張?jiān)L問標(biāo)志圖可以看到 ACC_BRIDGE表示方法是由編譯器產(chǎn)生的橋接方法,ACC_SYNTHETIC表示方法由編譯器自動(dòng)產(chǎn)生不屬于源碼。

2. 什么時(shí)候會(huì)生成橋接方法

當(dāng)子類繼承父類(繼承接口)實(shí)現(xiàn)抽象泛型方法的時(shí)候,編譯器會(huì)為子類自動(dòng)生成橋接方法

#父類
public abstract class SuperClass<T> {

  public abstract T get(T t) ;
}


#子類
public class SubClass extends SuperClass<String> {

  @Override
  public String get(String s) {
    return s;
  }
}

使用javap -v SubClass.class命令查看類SubClass的字節(jié)碼:

Classfile /Users/xudong/project-maven/test/person-study/dubbo-provider/target/classes/com/monian/dubbo/provider/study/generic/SubClass.class
  Last modified 2022年7月25日; size 777 bytes
  MD5 checksum 1328a7043cde4b809a156e7a239335a6
  Compiled from "SubClass.java"
public class com.monian.dubbo.provider.study.generic.SubClass extends com.monian.dubbo.provider.study.generic.SuperClass<java.lang.String>
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #4                          // com/monian/dubbo/provider/study/generic/SubClass
  super_class: #5                         // com/monian/dubbo/provider/study/generic/SuperClass
  interfaces: 0, fields: 0, methods: 3, attributes: 2
Constant pool:
   #1 = Methodref          #5.#23         // com/monian/dubbo/provider/study/generic/SuperClass."<init>":()V
   #2 = Class              #24            // java/lang/String
   #3 = Methodref          #4.#25         // com/monian/dubbo/provider/study/generic/SubClass.get:(Ljava/lang/String;)Ljava/lang/String;
   #4 = Class              #26            // com/monian/dubbo/provider/study/generic/SubClass
   #5 = Class              #27            // com/monian/dubbo/provider/study/generic/SuperClass
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               LocalVariableTable
  #11 = Utf8               this
  #12 = Utf8               Lcom/monian/dubbo/provider/study/generic/SubClass;
  #13 = Utf8               get
  #14 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
  #15 = Utf8               s
  #16 = Utf8               Ljava/lang/String;
  #17 = Utf8               MethodParameters
  #18 = Utf8               (Ljava/lang/Object;)Ljava/lang/Object;
  #19 = Utf8               Signature
  #20 = Utf8               Lcom/monian/dubbo/provider/study/generic/SuperClass<Ljava/lang/String;>;
  #21 = Utf8               SourceFile
  #22 = Utf8               SubClass.java
  #23 = NameAndType        #6:#7          // "<init>":()V
  #24 = Utf8               java/lang/String
  #25 = NameAndType        #13:#14        // get:(Ljava/lang/String;)Ljava/lang/String;
  #26 = Utf8               com/monian/dubbo/provider/study/generic/SubClass
  #27 = Utf8               com/monian/dubbo/provider/study/generic/SuperClass
{
  public com.monian.dubbo.provider.study.generic.SubClass();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method com/monian/dubbo/provider/study/generic/SuperClass."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/monian/dubbo/provider/study/generic/SubClass;

  public java.lang.String get(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: aload_1
         1: areturn
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       2     0  this   Lcom/monian/dubbo/provider/study/generic/SubClass;
            0       2     1     s   Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      s

  public java.lang.Object get(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: checkcast     #2                  // class java/lang/String
         5: invokevirtual #3                  // Method get:(Ljava/lang/String;)Ljava/lang/String;
         8: areturn
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcom/monian/dubbo/provider/study/generic/SubClass;
    MethodParameters:
      Name                           Flags
      s                              synthetic
}
Signature: #20                          // Lcom/monian/dubbo/provider/study/generic/SuperClass<Ljava/lang/String;>;
SourceFile: "SubClass.java"

可以看到字節(jié)碼中有兩個(gè)get方法,第二個(gè)方法參數(shù)和返回值類型都是java.lang.Object 并且可以看到flags有相應(yīng)標(biāo)志ACC_BRIDGE, ACC_SYNTHETIC說明此方法就是有編譯器自動(dòng)生成的橋接方法。再看code屬性:

aload_0:把this變量裝載到操作數(shù)棧中

aload_1:把方法變量s裝載到操作數(shù)棧中

checkcast # 2:校驗(yàn)棧頂變量s是否為java.lang.String類型

invokevirtual # 3: 調(diào)用方法 public String get(String s)

areturn: 返回結(jié)果 

根據(jù)上述code解釋可以看出編譯器生成的橋接方法為這個(gè)樣子的,橋接方法實(shí)際上調(diào)用了實(shí)際的泛型方法

public String get(String s) {
 return s;
}

#橋接方法
public Object get(Object s) {
  return get((String) s);
}

泛型-類型擦除

public class SubClass extends SuperClass<String> {

  @Override
  public String get(String s) {
    return s;
  }

  public static void main(String[] args) {
    SuperClass subClass = new SubClass();
    Object s = "hello world";
    System.out.println(subClass.get(s));
  }
}

java的泛型在運(yùn)行時(shí)會(huì)進(jìn)行泛型擦除替換成非泛型上邊界,java虛擬機(jī)無法知道準(zhǔn)確的類型。 上述代碼能編譯通過并且會(huì)調(diào)用子類SubClass的橋接方法由橋接方法再去調(diào)用實(shí)際泛型方法。如果定義為SuperClass<String> subClass = new SubClass();那么get方法入?yún)⒅荒転镾tring變量,因?yàn)榫幾g器在編譯期間會(huì)進(jìn)行類型校驗(yàn),不符合類型將直接報(bào)編譯失敗。

3. 為什么生成泛型方法

{
  public com.monian.dubbo.provider.study.generic.SuperClass();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/monian/dubbo/provider/study/generic/SuperClass;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/monian/dubbo/provider/study/generic/SuperClass<TT;>;

  public abstract T get(T);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
    MethodParameters:
      Name                           Flags
      t
    Signature: #18                          // (TT;)TT;
}

為了能夠正確的編譯,可以看到源碼中父類SuperClass get方法參數(shù)類型為T(T t),而在字節(jié)碼層面可以看到,經(jīng)過編譯后,get方法入?yún)⒑头祷刂殿愋投紴镺bject。

可以想象一下,如果沒有編譯器自動(dòng)生成的橋接方法,那么編譯是不會(huì)通過的。父類SubClass get方法經(jīng)過編譯后入?yún)⒑头祷刂殿愋投紴镺bject,而子類get方法入?yún)⒑头祷刂殿愋蜑镾tring,子類并沒有重寫父類的get方法(重寫:訪問的方法的實(shí)現(xiàn)過程進(jìn)行重新編寫, 返回值和形參都不能改變)。所有編譯器需要生成一個(gè)橋接方法,Object get(Object) 就可以編譯通過了。

4. 根據(jù)橋接方法獲取實(shí)際泛型方法 

主要借助Spring的BridgeMethodResolver#findBridgedMethod找到被橋接的方法,原理是首先找到類聲明的所有方法,找到與橋接方法簡(jiǎn)單名稱和方法參數(shù)數(shù)量相同的候選方法,若只要一個(gè)則直接返回,若有多個(gè)則循環(huán)判斷方法參數(shù)類型是否相同或者候選方法都有相同的方法簽名則從其中任選一個(gè)方法作為被橋接的方法。

@Slf4j
public class SubClass extends SuperClass<String> {

  @Override
  public String get(String s) {
    return s;
  }

  public static void main(String[] args) throws Exception {

    SubClass subClass = new SubClass();
    Method bridgeMethod = subClass.getClass().getDeclaredMethod("get", Object.class);
    log.info("bridgeMethod is bridge:" + bridgeMethod.isBridge());
    log.info("bridgeMethod:" + bridgeMethod.toString());

    // 實(shí)際泛型方法
    Method actualMethod = subClass.getClass().getDeclaredMethod("get", String.class);
    log.info("actualMethod:" + actualMethod.toString());
    // 通過spring #BridgeMethodResolver由橋接方法獲取到實(shí)際泛型方法
    Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(bridgeMethod);
    log.info("bridgedMethod:" + bridgedMethod.toString());
  }
}

輸出如下:

以上就是一文搞懂Java橋接方法的詳細(xì)內(nèi)容,更多關(guān)于Java橋接方法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • @Query注解的原生用法和native用法解析

    @Query注解的原生用法和native用法解析

    這篇文章主要介紹了@Query注解的原生用法和native用法解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • IDEA生成serialVersionUID的方法圖文詳解

    IDEA生成serialVersionUID的方法圖文詳解

    Java的序列化機(jī)制是通過在運(yùn)行時(shí)判斷類的serialVersionUID來驗(yàn)證版本一致性的,下面這篇文章主要給大家介紹了關(guān)于IDEA生成serialVersionUID的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • spring注解@Service注解的使用解析

    spring注解@Service注解的使用解析

    這篇文章主要介紹了spring注解@Service注解的使用解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 淺談java封裝

    淺談java封裝

    封裝封裝就是將屬性私有化,提供公有的方法訪問私有的屬性。*實(shí)現(xiàn)封裝的步驟:(1)修改屬性的可見性來限制對(duì)屬性的訪問。(2)為每個(gè)屬性創(chuàng)建一對(duì)賦值方法和取值方法,用于對(duì)這些屬性的訪問。(3)在賦值和取值方法中,加入對(duì)屬性的存取限制。
    2015-03-03
  • Java中List集合去重的幾種方式詳細(xì)解析

    Java中List集合去重的幾種方式詳細(xì)解析

    這篇文章主要介紹了Java中List集合去重的幾種方式詳細(xì)解析,在日常的業(yè)務(wù)開發(fā)中,偶爾會(huì)遇到需要將 List 集合中的重復(fù)數(shù)據(jù)去除掉的場(chǎng)景,那么今天我們來看看幾種LIst集合去重的方式,需要的朋友可以參考下
    2023-11-11
  • 詳解Springboot @Cacheable 注解(指定緩存位置)

    詳解Springboot @Cacheable 注解(指定緩存位置)

    這篇文章主要介紹了詳解Springboot @Cacheable 注解(指定緩存位置),使用? @Cacheable ?注解就可以將運(yùn)行結(jié)果緩存,以后查詢相同的數(shù)據(jù),直接從緩存中取,不需要調(diào)用方法,需要的朋友可以參考下
    2023-09-09
  • Spring Boot報(bào)錯(cuò):No session repository could be auto-configured, check your configuration的解決方法

    Spring Boot報(bào)錯(cuò):No session repository could be auto-configured

    這篇文章主要給大家介紹了關(guān)于Spring Boot報(bào)錯(cuò):No session repository could be auto-configured, check your configuration的解決方法,文中給出了詳細(xì)的解決方法,對(duì)遇到這個(gè)問題的朋友們具有一定參考價(jià)值,需要的朋友下面來一起看看吧。
    2017-07-07
  • @RequestBody的使用案例代碼

    @RequestBody的使用案例代碼

    @RequestBody主要用來接收前端傳遞給后端的json字符串中的數(shù)據(jù)的,這篇文章主要介紹了@RequestBody的使用,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02
  • 關(guān)于SpringBoot整合redis使用Lettuce客戶端超時(shí)問題

    關(guān)于SpringBoot整合redis使用Lettuce客戶端超時(shí)問題

    使用到Lettuce連接redis,一段時(shí)間后不操作,再去操作redis,會(huì)報(bào)連接超時(shí)錯(cuò)誤,在其重連后又可使用,糾結(jié)是什么原因?qū)е碌哪兀旅嫘【幗o大家?guī)砹薙pringBoot整合redis使用Lettuce客戶端超時(shí)問題及解決方案,一起看看吧
    2021-08-08
  • SpringBoot整合TomCat實(shí)現(xiàn)本地圖片服務(wù)器代碼解析

    SpringBoot整合TomCat實(shí)現(xiàn)本地圖片服務(wù)器代碼解析

    這篇文章主要介紹了SpringBoot整合TomCat實(shí)現(xiàn)本地圖片服務(wù)器代碼解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08

最新評(píng)論