今天问一个简单的问题,如何用JAVA写一个函数交换两个数并在main函数中输出交换呢?
01简单方案-----错误方法
首先想到的是这种方法,用一个中间数,然后直接交换。
代码如下
/**
* @author
* @email
* @create 2019-06-26
*/
public class App {
public static void main(String[] args) {
int a =10;
int b = 11;
exChange(a,b);
System.out.println("a: "+a+" : "+"b: "+b);
System.out.println("-----------------------------------");
int c =132;
int d = 145;
exChange(c,d);
System.out.println("c: "+c+" : "+"d: "+d);
}
private static void exChange(Integer a,Integer b){
int t;
t = a;
a = b;
b = t;
}
}
最后输出结果:
a: 10 : b: 11
-----------------------------------
c: 132 : d: 145
分析:为什么传递一个对象过来还是没有用,原因就在于JAVA中是值传递,没有引用传递的
02反射方案一------ 只能成功交换一个数
通过反射改变Integer中的字段value值,从而交换两个数。
代码如下:
import org.omg.PortableInterceptor.INACTIVE;
import java.lang.reflect.Field;
/**
* @author
* @email
* @create 2019-06-26
*/
public class App {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Integer a =10;
Integer b = 11;
exChange(a,b);
System.out.println("a: "+a+" : "+"b: "+b);
System.out.println("-----------------------------------");
Integer c =132;
Integer d = 145;
exChange(c,d);
System.out.println("c: "+c+" : "+"d: "+d);
}
private static void exChange(Integer a,Integer b) throws NoSuchFieldException, IllegalAccessException {
Integer exchangeNum = a;
Field filda = a.getClass().getDeclaredField("value");
filda.setAccessible(true);
filda.set(a,b);
Field fildb = b.getClass().getDeclaredField("value");
fildb.setAccessible(true);
fildb.set(b,exchangeNum);
}
}
结果:
a: 11 : b: 11
-----------------------------------
c: 145 : d: 145
很奇怪的是第一个数交换成功了,但是第二个数却但还是原来的数。这是为什么呢?因此我们猜测一定是这里处理问题。我们去看写的代码,写的地方 fildb.set(b,exchangeNum);我们在交换函数的上面有这样一个代码Integer exchangeNum = a;
exchangeNum实际上是a的一个副本,a变化就导致exchangeNum变化。因此由于我们首先设置了a的值,后来改变第二个数值,从而导致错误。
03反射方案二------ -127~128的数交换是错的,其余是对的
为了不会导致复制的情况发生,那么我们直接来一个int类型的数作为中间值呢?
代码如下
import org.omg.PortableInterceptor.INACTIVE;
import java.lang.reflect.Field;
/**
* @author
* @email
* @create 2019-06-26
*/
public class App {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Integer a =10;
Integer b = 11;
exChange(a,b);
System.out.println("a: "+a+" : "+"b: "+b);
System.out.println("-----------------------------------");
Integer c =132;
Integer d = 145;
exChange(c,d);
System.out.println("c: "+c+" : "+"d: "+d);
}
private static void exChange(Integer a,Integer b) throws NoSuchFieldException, IllegalAccessException {
int exchangeNum = a;
Field filda = a.getClass().getDeclaredField("value");
filda.setAccessible(true);
filda.set(a,b);
Field fildb = b.getClass().getDeclaredField("value");
fildb.setAccessible(true);
fildb.set(b,exchangeNum);
}
}
结果:
a: 11 : b: 11
-----------------------------------
c: 145 : d: 132
为什么11和10交换的时候,第一个数交换成功,第二个数交换错误呢?难道中间值会跟着第一个数变化,那么开始Debug,发现中间值没有根治变化,那么为什么变化了呢?通过苦思冥想,发现反射给a设置值得时候调用的是, public void set(Object obj, Object value)。传入的是两个对象,而我们的中间值是int类型,那么就会进行自动装箱,即调用
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
我们分析这个代码,但我们传入的值在-128~127的时候回从IntegerCache.cache中取值,取得是当前位置为 传入的数+128 位置的值。当我们传入一个10的时候,取得是位置为138的值。而对于一个缓存而言,他原来的样子应该是0~256分别对存储-128~127。因此原来的138位置是10。我们通过Debug发现已经变成了11。那么为什么会这样呢,其实很好理解在第一步,我们把缓存数组,138的位置的value已经修改成了11。所以导致错误。
04反射方案三------正确
中间值是通过new一个Integer的形式。
代码如下:
import org.omg.PortableInterceptor.INACTIVE;
import java.lang.reflect.Field;
/**
* @author
* @email
* @create 2019-06-26
*/
public class App {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Integer a =10;
Integer b = 11;
exChange(a,b);
System.out.println("a: "+a+" : "+"b: "+b);
System.out.println("-----------------------------------");
Integer c =132;
Integer d = 145;
exChange(c,d);
System.out.println("c: "+c+" : "+"d: "+d);
}
private static void exChange(Integer a,Integer b) throws NoSuchFieldException, IllegalAccessException {
Integer exchangeNum = new Integer(a);
Field filda = a.getClass().getDeclaredField("value");
filda.setAccessible(true);
filda.set(a,b);
Field fildb = b.getClass().getDeclaredField("value");
fildb.setAccessible(true);
fildb.set(b,exchangeNum);
}
}
结果:
a: 11 : b: 10
-----------------------------------
c: 145 : d: 132
显然答案是对,第一规避方案一副本,第二不会有自动装箱的操作。
反思
我们在实际的场景中能否这样做呢?答案自然是不行的,原因是我们已经改变了缓存数组中的值了,在main函数的最后加上这段代码:
import org.omg.PortableInterceptor.INACTIVE;
import java.lang.reflect.Field;
/**
* @author
* @email
* @create
*/
public class App {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Integer a =10;
Integer b = 11;
exChange(a,b);
System.out.println("a: "+a+" : "+"b: "+b);
System.out.println("-----------------------------------");
Integer c =132;
Integer d = 145;
exChange(c,d);
System.out.println("c: "+c+" : "+"d: "+d);
Integer f =10;
System.out.println("f: "+f);
}
private static void exChange(Integer a,Integer b) throws NoSuchFieldException, IllegalAccessException {
Integer exchangeNum = new Integer(a);
Field filda = a.getClass().getDeclaredField("value");
filda.setAccessible(true);
filda.set(a,b);
Field fildb = b.getClass().getDeclaredField("value");
fildb.setAccessible(true);
fildb.set(b,exchangeNum);
}
}
输出的结果:
a: 11 : b: 10
-----------------------------------
c: 145 : d: 132
f: 11
f怎么变成11了呢?这就很奇怪了,也好理解,因为前面我们把缓存数组的值改变了。因此慎用。