關於Integer和反射的一個緩衝區注意事項

關於Integer一些隱藏關卡

反射繞過安全保護修改integer時候的典型錯誤,使用反射強行修改的時候請慎重

Integer在方法中沒有提供value的get和set方法,若是如今須要你編寫一個函數,使得交換倆個值,你會發現java在這裏的值傳遞在Integer裏面是copy了一個副本指向值,而不是直接地址。

那若是就是須要擁有這麼一個方法的話,那麼應該怎麼作呢:

private static void swap(Integer a, Integer b) throws NoSuchFieldException, IllegalAccessException {
        //利用反射修改
        Field field = Integer.class.getDeclaredField("value");
        //繞過安全檢查修改final值
        field.setAccessible(true);
        //暫存a的值
        int temp = a.intValue();
        交換
        field.set(a, b);  
        field.set(b, temp)    //參數須要倆個object 因此這裏會裝箱
    }
}

這段代碼不難理解,其實就是:java

int temp = a;
a = b;
b = temp

只不過這裏是使用的反射的機制,本質仍是不變的;

既然方法編寫成功啦,那麼咱們編寫一個main函數開始測試吧!

public static void main(String[] args) throws NoSuchFieldException,             IllegalAccessException {
        Integer a = 1;
        Integer b = 2;
        System.out.println("a = " + a + ", b = " + b);
        swap(a, b);
        System.out.println("a = " + a + ", b = " + b);
}

在你們內心確定是已經有答案了吧,不過如今的結果可能會大跌眼鏡:

a = 1, b = 2
a = 2, b = 2

誒!這裏爲啥倆個值都會變成2呢,其實這裏也是讓我本身都感受到難以想象

先讓咱們知道裝箱的概念

若是不理解的能夠看看個人另一篇文章Java裝箱和拆箱

如今咱們再次看看valueOf這個函數

public static Integer valueOf(int i) {
      if (i >= IntegerCache.low && i <= IntegerCache.high)
          return IntegerCache.cache[i + (-IntegerCache.low)];
      return new Integer(i);
  }

其實這裏就是準備-128 到 127 緩衝區來應對高頻率的整數使用

field.set(a, b);

問題就是出如今這一句話裏面,

這裏的a和b須要看main函數的初始化,不難發現他們就是由自動裝箱來的,那麼他們倆個其實就是

a = IntegerCache.cache[1 + -(-128)]  //cache[129]
 b = IntegerCache.cache[2 + -(-128)]     //cache[130]

那麼重點來啦,如今field使用反射強行修改值,這樣的操做一直都是不安全的操做,這裏就是一個很是典型的例子, 這句話直接把緩衝區修改了!

cache[129] = b.intValue()  // cache[129] = 2;

因此接下來的

field.set(b, temp)

這裏set方法參數須要倆個object 因此這裏會裝箱, 也就是 temp裝箱執行代碼

IntegerCache.cache[temp + -(-128)]  // temp = 1

結果已經顯而易見啦!這個時候的temp裝箱之後竟然變成了2!因此致使了錯誤

看到這裏已經明瞭了把,其實就是一句話,

使用反射修改了緩衝區,使得緩衝區徹底錯亂

那在進行裝箱就不會有正確的結果啦,這裏爲了讓你們更加可以理解和看到錯誤的緩衝區,咱們再來一段代碼

main函數聲明

Integer a = 1;
  Integer l = 1;
  Integer m = 1;
  Integer b = 2;

方法中

field.set(a, b);
  field.set(b, new Integer(10));
  field.set(3, new Integer(11));
  field.set(5, new Integer(100));

輸出緩衝區0 到 10 的元素

for (int i = 0; i <= 10; i++){
  if(i % 10 == 0){
      System.out.println("");
  }
  System.out.print(i + " " + Integer.valueOf(i) + " , ");
}

答案

0 0 , 1 2 , 2 10 , 3 11 , 4 4 , 5 100 , 6 6 , 7 7 , 8 8 , 9 9

如今很清楚了把,緩衝區被垂手可得的修改啦,因此你們要牢記不要濫用反射去修改那些java保護起來的東西,java作好的保護不讓你碰其實就是怕你掉進坑了,因此使用反射的時候千萬要思考周全之後再來使用

博文是做者本來在其餘平臺的,現遷移過來

相關文章
相關標籤/搜索