Java交换两个Integer-一道无聊的题的思考
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
1.最近網(wǎng)上看到的一道題,有人說(shuō)一道很無(wú)聊的題,但我覺(jué)得有必要記錄一下。
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 請(qǐng)實(shí)現(xiàn)邏輯}? 看了題目之后,首先想到的是 加減法,異或操作交換等。但仔細(xì)思考之后,發(fā)現(xiàn)考察點(diǎn)并不是這個(gè)。至少,你先要了解java的引用和值傳遞的知識(shí)。
3. Java中都是值傳遞
也就是說(shuō),函數(shù)的參數(shù)變量都是對(duì)原來(lái)值的copy,這也是java和c的一個(gè)明顯區(qū)別。舉個(gè)例子。
1處和2處兩個(gè)引用的指向都是同一塊內(nèi)存,但是count == countCopy答案是false。
你在家看電視,用遙控器正在更換頻道,這時(shí)候你爸跟你說(shuō)“把遙控器給我!剛才那個(gè)節(jié)目很好看”。此時(shí),你為了不丟失對(duì)電視的控制權(quán),你從抽屜里拿了一個(gè)新的遙控器給了你爸(復(fù)制一個(gè)新的)。新、舊兩個(gè)遙控器就如同上面的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.回到題目
如果給出的不是引用類(lèi)型Integer而是int交換,這題是無(wú)解的。因?yàn)閟wap函數(shù)里的a,b都是引用的copy。所以你改變swap中a,b的引用指向是沒(méi)用的,因?yàn)闊o(wú)法影響到主函數(shù)中的引用a,b的指向。所以思路還是只能從更改引用指向的真實(shí)內(nèi)存值來(lái)解決(要拆開(kāi)電視,更換零件;只拿著遙控器一噸操作是沒(méi)法讓電視機(jī)硬件產(chǎn)生變化的),所以自然要用到反射了。最初的我解答如下(下面這份代碼是有問(wèn)題的)
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);} //程序輸出結(jié)果before swap: a=3,b=5 3========>5 5========>5 after swap: a=5,b=5發(fā)生了什么?為什么交換后b=5而不是3?別急我們根據(jù)上面的代碼,進(jìn)行DEBUG。
這里要補(bǔ)充一個(gè)細(xì)節(jié),你可以在valueOf函數(shù)里面打個(gè)斷點(diǎn),發(fā)現(xiàn)的確會(huì)進(jìn)去。
Integer a = 3; //等價(jià)與 Integer a = Integer.valueOf(3);那么上面有問(wèn)題的代碼??valueField.set(b,tmpA); 因?yàn)閠mpA是int類(lèi)型,在賦值的時(shí)候也會(huì)隱式調(diào)用Integer.valueOf封裝成對(duì)象,然后再進(jìn)行set賦值。懷疑問(wèn)題就是在set這個(gè)方法了嗎?但是 valueField.set(a,tmpB);是有效的,valueField.set(b,tmpA)是無(wú)效的。稍微改動(dòng)一下程序,進(jìn)一步探索。
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可以發(fā)現(xiàn)在第一個(gè)進(jìn)行反射賦值valueField.set(a,tmpB);后,Integer.valueOf(3) 等于 5 ???
進(jìn)去看看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是個(gè)什么鬼?而且IntegerCache.low = -128,?IntegerCache.high?= 127。valueOf(3)肯定會(huì)命中緩存,那么通過(guò)Debug調(diào)試,發(fā)現(xiàn)IntegerCache的確出錯(cuò)了,cache[3] = 5 (其實(shí)真實(shí)3的緩存下標(biāo)并不是3,而是i + (-IntegerCache.low),這里便于說(shuō)明理解)。
經(jīng)過(guò)這些分析,問(wèn)題表現(xiàn)在?valueField.set(a,tmpB); 賦值后?
命中IntegerCache,獲取cache(5)即5,并更新緩存cache(3)=5那么如果解決呢,其實(shí)只要避開(kāi)調(diào)用valueOf即可,也就是通過(guò)new Integer()來(lái)繞開(kāi)緩存。修改后的代碼如下:
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。所以說(shuō)盡量不要用反射去改變類(lèi)的私有變量。
?
轉(zhuǎn)載于:https://my.oschina.net/eqshen/blog/3036658
總結(jié)
以上是生活随笔為你收集整理的Java交换两个Integer-一道无聊的题的思考的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Python3的深拷贝和浅拷贝
- 下一篇: 折腾Java设计模式之建造者模式