Java交换两个Integer-一道无聊的题的思考
為什么80%的碼農都做不了架構師?>>> ??
1.最近網上看到的一道題,有人說一道很無聊的題,但我覺得有必要記錄一下。
2.題目
?
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的引用和值傳遞的知識。
3. Java中都是值傳遞
也就是說,函數的參數變量都是對原來值的copy,這也是java和c的一個明顯區別。舉個例子。
1處和2處兩個引用的指向都是同一塊內存,但是count == countCopy答案是false。
你在家看電視,用遙控器正在更換頻道,這時候你爸跟你說“把遙控器給我!剛才那個節目很好看”。此時,你為了不丟失對電視的控制權,你從抽屜里拿了一個新的遙控器給了你爸(復制一個新的)。新、舊兩個遙控器就如同上面的count,countCopy。
public static void main(String[] args) throws Exception {Integer count = new Integer(100);//1test(count);}public static void test(Integer countCopy){//2System.out.println(countCopy);}?
4.回到題目
如果給出的不是引用類型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();//3int tmpB = b.intValue();//5valueField.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。
這里要補充一個細節,你可以在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();//3int tmpB = b.intValue();//5System.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)); }程序輸出
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();//3int tmpB = b.intValue();//5System.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。所以說盡量不要用反射去改變類的私有變量。
?
轉載于:https://my.oschina.net/eqshen/blog/3036658
總結
以上是生活随笔為你收集整理的Java交换两个Integer-一道无聊的题的思考的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python3的深拷贝和浅拷贝
- 下一篇: 折腾Java设计模式之建造者模式