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。因此說盡可能不要用反射去改變類的私有變量。