【java面试题】不定义新变量的情况下交换两个Integer变量

题目:
不定义新变量的情况下交换两个Integer变量,完善swap()方法:


public class Main {
    public static void main(String[] args) {
        Integer a = 10;
        Integer b = 20;
        swap(a, b);
        System.out.printf("a is %d,b is %d", a, b);
    }

    public static void swap(Integer a, Integer b) {
       //完善代码
    }

}

首先完善swap先,这题目还有关于java到底是引用传递还是值传递的问题。

解题思路:
没有引入新变量的话,只能在a、b之间互相计算,将两个变量值信息混合在一起赋值给其中一个变量如a保存,再用另外一个变量b将混合变量a分离出a值赋值给b,那么现在b变量就是原来a的值,再用现在b变量(即原来的a的值)去将混合变量分离出b原来的值赋值给a。这样就可以实现没有新变量值就可以交换值。而其中的混合算法和分离算法很重要,要保证求值的唯一性(即y=fun(x),一个x值只能有一个y值对应)。下面给出三种算法:
1、异或算法


public class Main {
    public static void main(String[] args) {
        Integer a = 10;
        Integer b = 20;

        swap(a, b);

        System.out.printf("a is %d,b is %d", a, b);
    }

    public static void swap(Integer a, Integer b) {
        a = a ^ b;//混合算法
        b = a ^ b;//分离算法
        a = a ^ b;//分离算法
        System.out.printf(" a is %d,b is %d \n", a, b);
    }
}

运行结果:

 a is 20,b is 10 
a is 10,b is 20

为什么异或算法可以?因为它满足唯一性,例如 1 和 0的异或是1 ,1和1的异或是0,
反推的话,知道了异或结果是1,而一个值为1,那么另外一个参与异或的必定是0。

具体举个例子:

a = 10101
b = 10011

a^b = 00110

a = 10101
b = 10011
----------
    00110   

将异或结果与a异或可以求出b,得出的结果是 10101,等于b的值

a^b = 00110
a   = 10011
----------
      10101

也就是可以得出一个结论:
c=a^b, 那么 a= c^b,b=c^a;

2、加减法

代码:


public class Main {
    public static void main(String[] args) {
        Integer a = 10;
        Integer b = 20;
        swap(a, b);
        System.out.printf("a is %d,b is %d", a, b);
    }
    public static void swap(Integer a, Integer b) {
        a = a + b;
        b = a - b;
        a = a - b;
        System.out.printf(" a is %d,b is %d \n", a, b);
    }
}

运行结果:

 a is 20,b is 10 
a is 10,b is 20

加减法的原理很好理解,c= a+b, a= c-b,b= c-a。

3、乘除法
代码:


public class Main {
    public static void main(String[] args) {
        Integer a = 10;
        Integer b = 20;
        swap(a, b);
        System.out.printf("a is %d,b is %d", a, b);
    }
    public static void swap(Integer a, Integer b) {
        a = a * b;
        b = a / b;
        a = a / b;
        System.out.printf(" a is %d,b is %d \n", a, b);
    }
}

运行结果:

 a is 20,b is 10 
a is 10,b is 20

乘除法的原理也很简单:c= a*b , a= c / b ,b= c / a;

4、讨论
4.1 与算法行不行?

举个例子: 1 与 0 结果是0,反过来,知道了与的结果是0,一个值为1,那么可以推出,另外一个值必定是0。但是要是一下面这种情况就失效了,知道了与的结果是0,一个值为0,那么推出另外一个值可能为0,也可能1,这种模棱两可的结果不满足推导结果的唯一性,所以就不能用在这道题中。

4.2 关于Java是值传递还是引用传递的问题讨论

某面试官(liwen zaozhi)说Java是引用传递。说main方法中的 System.out.printf("a is %d,b is %d", a, b);可以输出交换后的结果。他说对了一半。上面的变量是包装类:Integer ,传的值是a对象对应的引用句柄值,按理在swap方法处理之后可以在main方法中获取值。为什么输出结果没有呢?
分析一下过程:
【java面试题】不定义新变量的情况下交换两个Integer变量_第1张图片
那传递的是引用的句柄值,那么应该可以修改引用句柄对应的对象的值。现实上没有发生这样的事,为何?原因出在Integer 包装类中的 private final int value;,不可变对象。
用反编译工具看看字节码之后的对代码:

public class Main {
  public static void main(String[] args) {
    Integer a = Integer.valueOf(10);
    Integer b = Integer.valueOf(20);
    swap(a, b);
    System.out.printf("a is %d,b is %d", new Object[] { a, b });
  }
  
  public static void swap(Integer a, Integer b) {
    a = Integer.valueOf(a.intValue() ^ b.intValue());
    b = Integer.valueOf(a.intValue() ^ b.intValue());
    a = Integer.valueOf(a.intValue() ^ b.intValue());
    System.out.printf(" a is %d,b is %d \n", new Object[] { a, b });
  }
}

编译语法糖的机制,自动拆箱装箱。swap方法中 a = Integer.valueOf(a.intValue() ^ b.intValue());,结果就是swap方法栈中的局部变量值a的值指向了新的引用句柄。而main方法中a的值还是原来的应用句柄,不影响它的值。所以为什么main中的打印输出显示不了交换后的情况。

给出个结论:Java只有值传递,没有引用传递。

值传递:是在调用函数时将实际参数复制一份到函数中,这样如果对参数进行修改,将不会影响到实际的参数。

引用传递:是指在调用函数时将实际参数地址直接传递到函数中,那么如果函数中对参数进行修改,将影响到实际的参数。

你可能感兴趣的:(java,java,开发语言)