关于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作好的保护不让你碰其实就是怕你掉进坑了,因此使用反射的时候千万要思考周全之后再来使用

博文是做者本来在其余平台的,现迁移过来

相关文章
相关标签/搜索