03 爲Map.put的增加的checkcast & 增加了一段業務無關的 instance.getClass() 的調用

前言

1. 爲Map.put的增加的checkcast

前幾天 有位朋友問了這麼一個問題, mybatis 裏面自定義了一個 XXMapper.queryXXList, 返回值爲 List<Map<String, String>> 記名爲 queryResult(key爲字段名字, value爲具體的數據)

然後 之後查詢 queryResult 把他放到另外的一個 Map<String, String> 裏面根據 key 爲 field01 對應的值, value 爲 field02 對應的值 

然後 field01 對應的字段在數據庫中是 int, field02 對應的字段在數據庫中爲 varchar, 類似代碼如下 

    List<Map<String, String>> queryResult = XXExMapper.queryXXList();
    Map<String, String> resultByField01 = new HashMap<>();
    for(Map<String, String> row : queryResult) {
      resultByField01.put(row.get("$field01Name"), row.get("$field02Name"));
    }

然後 他發現 臥槽根據 字符串的 field01的值 獲取不到值, 然後 根據 Integer 的 field01的值 才能夠獲取到... 

然後, 我之後寫了一個示例代碼給他看了一下, 大致的原因是 怎樣怎樣  

然後 我把我的代碼 在 jdk 7, 8 上面編譯了一下 跑了一下, 沒得問題, 然後 換了一個 jdk9, 就直接 gg 了, 呵呵 藉此機會探究一下這個問題  

 

 

2. 增加了一段業務無關的 instance.getClass() 的調用

另外還有一個問題是這樣的 有段代碼 他本身的業務很簡單, 就是一個賦值操作 

但是, 編譯出來的字節碼, 我們卻發現 多了一個 instance.getClass() 的一個方法調用, 這個就感覺 神奇怪 

這個問題來自於一個 技術交流的qq羣 

 

 

問題1測試用例如下

package com.hx.test04;

import java.util.HashMap;
import java.util.Map;

/**
 * GenericTypePollution
 *
 * @author Jerry.X.He <[email protected]>
 * @version 1.0
 * @date 2020-03-23 19:38
 */
public class Test16GenericTypePollution {

  // Test16GenericTypePollution
  public static void main(String[] args) {

    Map row = new HashMap<>();
    row.put("key", 1);

    Map<String, String> result = (Map<String, String>) row;
    Object value = result.get("key");

    Map<String, String> bizMap = new HashMap<>();
    bizMap.put(result.get("key"), "newVal");

    System.out.println(value);

  }

}

 

jdk 8 上面運行結果如下 

jdk9 運行結果如下 

 

然後這個結果也很容易猜到原因, 估計就是 25 行 result.get("key") 增加一個 checkcast 的驗證 

編譯出來具體的字節碼信息如下 

 

 

jdk7 的 javac 

在 TransType 階段, 的 'bizMap.put(result.get("key"), "newVal")'; 的 'result.get("key")'

這裏可以看到 result.get 的結果爲 java.lang.Object(get 方法聲明的結果類型)

result.get 被期望返回的結果爲 java.lang.Object(bizMap.put 的第一個參數, put 方法聲明的參數類型) 

然後 因爲這裏 實際結果 和 期望結果 相同, 因此 不用生成類型轉換 

 

 

jdk9 的 javac 

同樣的配方, 同樣的味道, 但是 到了 jdk9 這邊 

result.get 被期望返回的類型變了, 變成了 java.lang.String(由於 bizMap 爲 Map<String, String>) 

這裏可以看到 result.get 的結果爲 java.lang.Object(get 方法聲明的結果類型)

 

 

result.get 被期望返回的類型 來自於這裏, 這裏是直接取的 編譯時的類型信息, java.lang.String 

因此 這裏多生成了一個 java.lang.Object 轉換 java.lang.String 的 checkcast 

 

如果 result.get 的返回的類型, 也取編譯時的類型信息的話(java.lang.String), 那麼這個 checkcast 也就沒有了 

這裏也暴露了 泛型的很多問題 

 

 

問題2測試用例

package com.hx.test04;

/**
 * FinalAssign
 *
 * @author Jerry.X.He <[email protected]>
 * @version 1.0
 * @date 2020-03-15 20:08
 */
public class Test09FinalAssign {

  // s3
  public final String s3 = "public final String s3";

  // Test09FinalAssign
  // getClass 的調用依據?
  public static void main(String[] args) {

    Test09FinalAssign instance = new Test09FinalAssign();
//    Test09FinalAssign instance = null;
    String newS3 = instance.s3;
    int x = 0;

  }

  public void foo() {

    Test09FinalAssign instance = new Test09FinalAssign();
    String newS3 = instance.s3;

  }

}

我們這裏主要關注的是 main 方法, 我們發現 紅框處的部分字節碼 在源碼中似乎是沒有體現啊?, 那麼 他的作用主要是什麼呢 ?

 

 

在生成class的階段, 訪問到 "String newS3 = instance.s3;" 的時候, 發現 instance.s3 編譯時就能夠確定它得值 

那麼 就做了一些優化, 生成了一個 null check, 一個 ldc 加載當前  instance.s3 對應的常量 

 

生成的 null check 如下 

jdk 9 的 null check 如下 

allowBetterNullChecks 的判斷方式是 目標字節碼版本 大於等於 jdk7 

 

 

完 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章