public static void main(String[] args) throws Exception { Integer a = 3; Integer b = 5; System.out.println("before swap: a="+ a + ",b=" + b); swap(a,b); System.out.println("after swap: a="+ a + ",b=" + b); } public static void swap(Integer a,Integer b) throws Exception { //TODO 请实现逻辑 }
看了题目以后,首先想到的是 加减法,异或操做交换等。但仔细思考以后,发现考察点并非这个。至少,你先要了解java的引用和值传递的知识。java
也就是说,函数的参数变量都是对原来值的copy,这也是java和c的一个明显区别。举个例子。缓存
1处和2处两个引用的指向都是同一块内存,可是count == countCopy答案是false。函数
你在家看电视,用遥控器正在更换频道,这时候你爸跟你说“把遥控器给我!刚才那个节目很好看”。此时,你为了避免丢失对电视的控制权,你从抽屉里拿了一个新的遥控器给了你爸(复制一个新的)。新、旧两个遥控器就如同上面的count,countCopy。spa
public static void main(String[] args) throws Exception { Integer count = new Integer(100);//1 test(count); } public static void test(Integer countCopy){//2 System.out.println(countCopy); }
若是给出的不是引用类型Integer而是int交换,这题是无解的。由于swap函数里的a,b都是引用的copy。因此你改变swap中a,b的引用指向是没用的,由于没法影响到主函数中的引用a,b的指向。因此思路仍是只能从更改引用指向的真实内存值来解决(要拆开电视,更换零件;只拿着遥控器一吨操做是无法让电视机硬件产生变化的),因此天然要用到反射了。最初的我解答以下(下面这份代码是有问题的)调试
public static void swap(Integer a,Integer b) throws Exception { Field valueField = Integer.class.getDeclaredField("value"); valueField.setAccessible(true); int tmpA = a.intValue();//3 int tmpB = b.intValue();//5 valueField.set(a,tmpB); valueField.set(b,tmpA); }
//程序输出结果 before swap: a=3,b=5 3========>5 5========>5 after swap: a=5,b=5
发生了什么?为何交换后b=5而不是3?别急咱们根据上面的代码,进行DEBUG。code
这里要补充一个细节,你能够在valueOf函数里面打个断点,发现的确会进去。对象
Integer a = 3; //等价与 Integer a = Integer.valueOf(3);
那么上面有问题的代码 valueField.set(b,tmpA); 由于tmpA是int类型,在赋值的时候也会隐式调用Integer.valueOf封装成对象,而后再进行set赋值。怀疑问题就是在set这个方法了吗?可是 valueField.set(a,tmpB);是有效的,valueField.set(b,tmpA)是无效的。稍微改动一下程序,进一步探索。内存
public static void swap(Integer a,Integer b) throws Exception { Field valueField = Integer.class.getDeclaredField("value"); valueField.setAccessible(true); int tmpA = a.intValue();//3 int tmpB = b.intValue();//5 System.out.println(Integer.valueOf(3)+"======" + Integer.valueOf(5)); valueField.set(a,tmpB); System.out.println(Integer.valueOf(3)+"======" + Integer.valueOf(5)); valueField.set(b,tmpA); System.out.println(Integer.valueOf(3)+"======" + Integer.valueOf(5)); }
程序输出get
before swap: a=3,b=5 3======5 5======5 5======5 after swap: a=5,b=5
能够发如今第一个进行反射赋值valueField.set(a,tmpB);后,Integer.valueOf(3) 等于 5 ???源码
进去看看valueOf的源码:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
IntegerCache是个什么鬼?并且IntegerCache.low = -128, IntegerCache.high = 127。valueOf(3)确定会命中缓存,那么经过Debug调试,发现IntegerCache的确出错了,cache[3] = 5 (其实真实3的缓存下标并非3,而是i + (-IntegerCache.low),这里便于说明理解)。
通过这些分析,问题表如今 valueField.set(a,tmpB); 赋值后
命中IntegerCache,获取cache(5)即5,并更新缓存cache(3)=5
那么若是解决呢,其实只要避开调用valueOf便可,也就是经过new Integer()来绕开缓存。修改后的代码以下:
public static void swap(Integer a,Integer b) throws Exception { Field valueField = Integer.class.getDeclaredField("value"); valueField.setAccessible(true); int tmpA = a.intValue();//3 int tmpB = b.intValue();//5 System.out.println(Integer.valueOf(3)+"======" + Integer.valueOf(5)); valueField.set(a,new Integer(tmpB)); System.out.println(Integer.valueOf(3)+"======" + Integer.valueOf(5)); valueField.set(b,new Integer(tmpA)); System.out.println(Integer.valueOf(3)+"======" + Integer.valueOf(5)); } //输出 before swap: a=3,b=5 3======5 5======5 5======3 after swap: a=5,b=3
可是 Integer.valueOf(3)的值仍是5,若是程序的其余地方也用到了Integer.value(3)那么将形成致命bug。因此说尽可能不要用反射去改变类的私有变量。