面试题1:Java中通过函数交换引用变量的值

先给出一道面试题:
在main中定义两个Integer变量,通过swap方法交换值
碰到这道题,大家的第一反应就是这样写

    public static void swap(Integer a1, Integer b1) {
        int temp = a1;
        a1 = b1;
        b1 = temp;
    }

这样写肯定是错误的。为什么呢,Integer不是引用类型吗, 我传参传的是引用类型,不就是地址吗,用地址去操作,为什么不能交换,通过内存模型图来看看为什么
在这里插入图片描述
当完成交换后,只是a1和b1交换了,对a和b没有影响
在这里插入图片描述
这就要引出Java中的传递方式,Java中有两种参数传递的方式,一种是值传递,一种是引用传递,但是严格意义上来讲,引用传递不算真正的引用传递,是值传递的特殊情况,传递的是引用的值。
我们在一个函数中去修改一个对象的数据时,引用传递会起到作用,因为函数拷贝了一份引用,两个引用同时指向了堆上的同一个对象,所以函数中对引用所指的对象进行操作是完全有效的。
在这里插入图片描述
所以针对这个特点,我们可以想到解决这个问题的一个方法

修改对象的内部数据

因为我们在函数中通过引用所做的操作在函数结束后是有效的,所以我们可以修改对象中的数据,先举个简单的例子
我们有一个Person类,通过传递Person的两个实例来交换两个Person实例

public class Test04 {
    public static void main(String[] args) {
        Person person1 = new Person("张三");
        Person person2 = new Person("李四");
        swap(person1,person2);
        System.out.println(person1);
        System.out.println(person2);
    }
    public static void swap(Person person1,Person person2) {
        String temp = person1.getName();
        person1.setName(person2.getName());
        person2.setName(temp);
    }
}
class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

在这里插入图片描述
可以看到结果是正确的。

通过数组辅助

还有一种方法是通过数组辅助的方式来进行交换,我们先通过内存图来了解
在这里插入图片描述
然后我们交换数组的0号和1号下标
在这里插入图片描述
这样就完成了交换
还是上面的案例,这下我们需要修改一下swap函数

    public static void main(String[] args) {
        Person person1 = new Person("张三");
        Person person2 = new Person("李四");
        Person[] persons = {person1,person2};
        swap(persons);
        System.out.println(persons[0]);
        System.out.println(persons[1]);
    }
    public static void swap(Person[] persons) {
        Person temp = persons[0];
        persons[0] = persons[1];
        persons[1] = temp;
    }

结果一样是正确的,但是这个方法需要辅助数组,如果题目规定了函数头,并且没有提供辅助数组,这种方法就不可行。

总结了交换对象的两种方式,我们来看这个面试题
题目并没有规定函数头,所以我们可以自己写一个辅助数组,这种方法很简单,不做介绍。
主要来看一下如何应用第一种方法完成交换,也就是去改变Integer内部的数据成员
通过对Integer源码的了解,Integer用来代替基本类型变量int的数据属性是
在这里插入图片描述
但是由于封装性,没有提供set方法,那么就需要用到反射
按照我们的思路,得到Class对象,获得私有属性,set方法修改实例的私有属性

    public static void swap(Integer a,Integer b) {
        Class<Integer> integerClass = Integer.class;
        Integer temp = a;
        try {
            Field value = integerClass.getDeclaredField("value");
            value.setAccessible(true);
            value.set(a,b);
            value.set(b,temp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

我们看看结果是什么
在这里插入图片描述
这是为什么呢?jdk5以后提供了包装类的自动装箱,底层调用了valueof方法,
在这里插入图片描述
Integer底层维护了一个在-128到127之间的数组,如果i的范围在该范围内,会通过i进行一系列数组下标的运算,然后找到数组中对应的对象,如果不在该范围内,就直接去new。
问题出在哪?当完成第一个set的时候,观察数组的情况
在这里插入图片描述
原本1的位置变成了2,所以这个时候a就变成了2,temp因为也是字面量赋值,跟着变成了2
在这里插入图片描述
这就是问题所在,temp是2,把2又给了b,所以结果是2,2。我们需要new一个temp对象出来,不从缓存数组中拿值,这样temp就不会受到缓存数组的影响。修改代码
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42220532/article/details/89294218