关于Integer和反射的一个缓冲区注意事项

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Mackyhuang/article/details/82820825

关于Integer一些隐藏关卡

Mackyhuang


反射绕过安全保护修改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 所以这里会装箱
        }
}

这段代码不难理解,其实就是:

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

猜你喜欢

转载自blog.csdn.net/Mackyhuang/article/details/82820825
今日推荐