关于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 所以这里会装箱
}
}
这段代码不难理解,其实就是:
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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。