交换两个 Integer

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_21586317/article/details/82719761

题目:

public static void main(String[] args) {
    Integer a = 1;
    Integer b = 2;
    System.out.println("before swap: a=" + a + ", b=" + b);
    // 交换 a 和 b 的值
    swap(a, b);
    System.out.println("after swap: a=" + a + ", b=" + b);
}

思考:如何实现 swap(Integer a, Integer b) 函数?

开始

一、思考

1.Integer 是引用类型,那么直接将 Integer 作为 swap(Integer a, Integer b) 参数?

private static void swap(Integer a, Integer b) {
    Integer temp = a;
    a = b;
    b = temp;
}

运行前:a = 1,b = 2
运行后:a = 1,b = 2

为什么???

对象 修饰 含义
类 Integer final class Integer Integer 不可变
成员变量 value private final value value 私有不可变

Integer 虽然是引用类型,但是无法通过引用来修改 Integer 的 value 值

查看 Integer 类的定义:

// 类被 final 修饰,即不能被继承
public final class Integer extends Number implements Comparable<Integer> {

    ...

    /**
     * The value of the {@code Integer}.
     *
     * @serial
     */
    // 成员变量被 private/final 修饰,即不可见/不可变
    // 没有提供 setter 等方法修改成员变量
    private final int value;

    /**
     * Constructs a newly allocated {@code Integer} object that
     * represents the specified {@code int} value.
     * 构造一个新分配的 Integer 对象,该对象表示指定的 value 值
     *
     * @param   value   the value to be represented by the
     *                  {@code Integer} object.
     * 由整数对象表示的值
     */
    public Integer(int value) {
        this.value = value;
    }

    ...

}

Integer 对象一旦创建就不可变,所以当 Integer 作为参数传递时,虽然传递的是引用,但是在 swap() 函数中对该引用的操作并不能影响到 main() 函数中的 Integer 对象,swap(Integer a, Integer b) 中对引用的操作其实是先创建了新的 Integer 对象,然后修改的是新的 Integer 对象的 value 值,和 main() 函数中的 Integer 对象无关

2.Integer 的 value 私有不可变,使用反射?

private static void swap(Integer a, Integer b) {
    try {
        int temp = a;
        Field field = Integer.class.getDeclaredField("value");
        // 设置私有属性访问权限为 true
        field.setAccessible(true);
        field.set(a, b);
        field.set(b, temp);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

运行前:a = 1,b = 2
运行后:a = 2,b = 2

为什么???
Integer 类中有一个缓存数组 IntegerCache,数组范围是 [-128, 127],在此范围内的值,不通过 new 方式获取对象,直接从缓存数组中拿

/**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.
 * 返回表示指定 int value 值的 Integer 实例
 *
 * If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #Integer(int)}, as this method is likely
 * to yield significantly better space and time performance by
 * caching frequently requested values.
 * 如果不需要一个新的整数实例,则该方法一般应优先于构造函数整数,因为这种方法通过缓存频繁请求的值可能产生明显更好的空间和时间性能
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 * 此方法将始终在范围 -128 至 127 的范围内缓存值,并且可以缓存在此范围之外的其他值(缓存数组 IntegerCache 的最大值 high 是可以修改的,修改 sun.misc.VM.getSavedProperty 属性值)
 */
public static Integer valueOf(int i) {
    // 在缓存数组 IntegerCache 范围 -128 至 127 内的值
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        // 直接从缓存数组中获取对应值
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

因为 a = 1,b = 2,都在缓存数组 IntegerCache 范围 [-128, 127] 内,所以

// temp = a = 1,对应 IntegerCache 下标 130
int temp = a;
Field field = Integer.class.getDeclaredField("value");
field.setAccessible(true);
// 修改 IntegerCache 下标 130 的值为 b,此时 a = 2,temp = 2
field.set(a, b);
// 修改 IntegerCache 下标 131 的值为 temp,b = 2
field.set(b, temp);

那该怎么办咧?
答:不去拿缓存数组 IntegerCache 中的值

// 修改 b 的值时,手动 new Integer(temp)
field.set(b, new Integer(temp));

field.setInt(b, temp);

二、总结

1.Integer 是不可变类,对 Integer 引用进行操作后,得到的值是新创建的 Integer 对象
2.通过反射可以修改不可变对象的值

猜你喜欢

转载自blog.csdn.net/qq_21586317/article/details/82719761