今天问一个简单的问题,如何用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了呢?这就很奇怪了,也好理解,因为前面我们把缓存数组的值改变了。因此慎用。