先看一段代码,猜猜结果是什么?
public class IntegerDemo {
public static void main(String[] args) {
Integer a =1, b = 2;
System.out.println("before, a="+a+",b="+b);
swap(a,b);
System.out.println("after, a="+a+",b="+b);
}
private static void swap(Integer i1, Integer i2) {
Integer temp = i1;
i1 = i2;
i2 = temp;
}
}
结果:
before, a=1,b=2
after, a=1,b=2
为什么会这样?图示看下
i1,i2 的引用的改变,对a,b没有任何影响
再来看,我们想要更改 a 和 b 的值
public class IntegerDemo {
public static void main(String[] args) {
Integer a =1, b = 2;//自动装箱 相当于 Integer a = Integer.valueOf(1);
System.out.println("before, a="+a+",b="+b);
//swap(a,b);
change(a,b);
System.out.println("after, a="+a+",b="+b);
}
private static void change(Integer i1, Integer i2) {
i1 = 4;
i2 = 5;
}
private static void swap(Integer i1, Integer i2) {
Integer temp = i1;
i1 = i2;
i2 = temp;
}
}
结果,仍然没有变化
before, a=1,b=2
after, a=1,b=2
为什么呢?来看源码,Integer 的value是final类型的,一旦赋值,不能被修改
难道就没办法了吗?
答案是有的,那就是用反射
private static void swapWithRef(Integer i1, Integer i2) throws NoSuchFieldException, IllegalAccessException {
Field field = Integer.class.getDeclaredField("value");
field.setAccessible(true);//绕过安全检查
Integer temp = i1;
field.set(i1,i2);
field.set(i2,temp);
}
这里还有一个面试题,就是setAccessible 底层是怎么实现的?
这里讲一个成员变量设置为你设置的值(true);
然后在set操作的时候,会根据这个成员变量的值去验证或者不验证权限
但是结果依旧不对
before, a=1,b=2
after, a=2,b=2
Integer a =1 相当于 Integer a = Integer.valueOf(1); // 自动装箱
从上面的源码可以得知,在[-128,127] 之间的范围,已经缓存起来了。
如果 a 和 b 在这个范围内[-128,127];
使用如下代码是相等的
a == b // true;
// 但是如果 超出范围,是返回的false;
Integer c = 129;
Integer d = 129;
c == d // false
我们继续来看刚才的问题
private static void swapWithRef(Integer i1, Integer i2) throws NoSuchFieldException, IllegalAccessException {
Field field = Integer.class.getDeclaredField("value");
field.setAccessible(true);//绕过安全检查
Integer temp = new Integer(i1);// 问题出在这,这里使用一个新的地址,不受自动装箱和缓存的影响
field.set(i1,i2);
field.set(i2,temp);
}
before, a=1,b=2
after, a=2,b=1
这次结果是正确的。