Arrays.copyOf() 是深克隆还是浅克隆(jdk1.8)

背景
我们在看JDK源码时经常看到数组复制方法,如 Arrays.copyOf()System.arraycopy(),今天我们研究下数组复制到底是浅克隆还是深克隆?


一、源码

public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

通过看源码可以知道copy数组确实是新生成的数组对象(new Object 或 Array.newInstance),但是数组内部元素是否是新对象呢?复制数组到底是深克隆还是浅克隆呢?


二、测试

int数组:

int[] ints = new int[]{1,2,3,4,5};
int[] intsCopy = Arrays.copyOf(ints, 10);

System.out.println(ints);
System.out.println(intsCopy);

System.out.println(Arrays.toString(intsCopy));

intsCopy[0] = 1111;
System.out.println(Arrays.toString(intsCopy));
System.out.println(Arrays.toString(ints));

打印结果:

[I@28d93b30
[I@1b6d3586
[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
[1111, 2, 3, 4, 5, 0, 0, 0, 0, 0]
[1, 2, 3, 4, 5]

可以发现,复制后的intsCopy数组的确是不同于原数组ints的数组对象(地址不一样),并且修改复制数组的int元素,对原数组并无影响!

String数组:

String[] strings = new String[]{"1", "2", "3"};
String[] stringsCopy = Arrays.copyOf(strings, 10);

System.out.println(strings);
System.out.println(stringsCopy);

System.out.println(Arrays.toString(stringsCopy));
stringsCopy[0] = "11111";
System.out.println(Arrays.toString(stringsCopy));
System.out.println(Arrays.toString(strings));

打印结果:

[Ljava.lang.String;@4554617c
[Ljava.lang.String;@74a14482
[1, 2, 3, null, null, null, null, null, null, null]
[11111, 2, 3, null, null, null, null, null, null, null]
[1, 2, 3]

复制的string数组元素改变依旧对原数组无影响!

对象数组:

public class Stu {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Stu{" +
                "name='" + name + '\'' +
                '}';
    }
}
Stu stu1 = new Stu();
stu1.setName("yy");

Stu stu2 = new Stu();
stu2.setName("jj");

Stu[] stus = {stu1, stu2};
Stu[] stusCopy = Arrays.copyOf(stus, 10);

System.out.println(stus);
System.out.println(stusCopy);

System.out.println(Arrays.toString(stusCopy));
stusCopy[0].setName("yyyyjjjj");
System.out.println(Arrays.toString(stusCopy));
System.out.println(Arrays.toString(stus));

打印结果:

[Lcn.yj.test.Stu;@14ae5a5
[Lcn.yj.test.Stu;@7f31245a
[Stu{name='yy'}, Stu{name='jj'}, null, null, null, null, null, null, null, null]
[Stu{name='yyyyjjjj'}, Stu{name='jj'}, null, null, null, null, null, null, null, null]
[Stu{name='yyyyjjjj'}, Stu{name='jj'}]

可以发现,当数组元素为对象时,改变复制数组stusCopy的元素属性确实会影响原数组,说明Arrays.copyOf只复制对象地址,并没有新创建数组元素

扫描二维码关注公众号,回复: 9950725 查看本文章

三、存在问题

看到上面测试,细心的读者可能会发现一个问题,那就是我们测试基本数据类型和String类型时都是完全替换复制数组的一个元素,如:

intsCopy[0] = 1111;
stringsCopy[0] = "11111";

而测试对象时,只是修改数组元素对象的一个属性

stusCopy[0].setName("yyyyjjjj");

这是不是有些不公,如果也对对象这样做呢?如下:

Stu stu3 = new Stu();
stu3.setName("yyyyjjjj");
stusCopy[0] = stu3;

这样做的话原数组还会改变吗?大家可以测试下,答案是不会改变

因为这样做等同于stusCopy[0]新换了一个对象的内存地址,不再是以前的地址了,那么该下标的元素和原数组对应的元素毫无关联了!

所以我们首先要明确一点,复制数组和原数组是确确实实两个不同的数组对象,但是该数组对象内部的元素可能存在关联!

完全替换(新增、删除)复制数组的一个元素,只是改变复制数组的一个属性,对原数组产生不了任何影响我们只有找到两者某个元素具有相同的内存地址,然后顺藤摸瓜通过该地址找到堆中真正的值,然后修改该值,才能对原数组产生影响!

这时再看上面三个测试:

基本数据类型:我们无法通过修改复制的基本数据类型来影响原数据,因为基本类型之间的赋值是创建新的拷贝,直接存储在JVM栈上的(局部变量),数据本身的值就是存储在栈空间里面。

String类型:String类是final类,无论是以字面量赋值(常量池),还是new方式赋值(堆),字符串都是一个不可变对象,所以无法通过某个字符串对象的引用地址改变真正的值;

对象引用类型:只有这时我们才能通过内存地址改变对象值,从而改变原数组具有相同内存地址的元素!


四、结论

Arrays.copyOf( 或 System.arraycopy)复制确实是浅克隆,只复制了对象的引用(内存地址),并没有为每个元素新创建对象!

发布了73 篇原创文章 · 获赞 373 · 访问量 42万+

猜你喜欢

转载自blog.csdn.net/Abysscarry/article/details/88653500