欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程语言 > java >内容正文

java

Java交换两个Integer-一道无聊的题的思考

发布时间:2025/4/5 java 44 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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-一道无聊的题的思考的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。