Java:浅拷贝(克隆)对我不起作用?

时间:2020-04-26 04:16:42

标签: java clone shallow-copy

根据定义:

浅表副本是我们只将字段值从一个对象复制到另一个对象的副本。它们是不同的对象,但是问题在于,当我们更改任何原始地址的属性时,这也会影响shallowCopy的地址。

我试图查看对象1和对象2的值是否相同,因此尝试跟随而不能这样做。

public class TestClone implements Cloneable {
    private String a;

    public String getA() {
        return a;
    }


    public void setA(String a) {
        this.a = a;
    }


    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

主程序:

package com.test;

public class MainTestClone {
    public static void main(String[] args) throws CloneNotSupportedException {
        TestClone clone = new TestClone();
        clone.setA("RABA");

        TestClone clone2 = (TestClone) clone.clone();
        System.out.println(clone.getA());
        System.out.println(clone2.getA());
        System.out.println("---------------------------");
        clone.setA("DABA");
        System.out.println(clone.getA());
        System.out.println(clone2.getA());
        System.out.println("---------------------------");
        clone2.setA("Clone2 RABA DABA");
        System.out.println(clone.getA());
        System.out.println(clone2.getA());

    }
}

输出:

RABA
RABA
---------------------------
DABA
RABA <--- It does not change to DABA
---------------------------
DABA <--- No Changes here
Clone2 RABA DABA

4 个答案:

答案 0 :(得分:2)

我认为真正的问题是您从上下文中脱离了以下定义,并误解了它:

“浅表复制是一种复制,其中我们仅将字段的值从一个对象复制到另一个对象。它们是不同的对象,但是问题是当我们更改任何原始地址的属性时,这也会影响shallowCopy的地址。”

这显然是指某些示例……您未引用。 (因此,我的评论是您已脱离上下文!)

为使此说明正确,“原始地址”属性必须是将对象与要克隆的对象分开的字段。我想这个例子是这样的:

public class User implements Cloneable {
    String name;
    Address address; 
    ...
    public User clone() {
        return (User)(super.clone());
    }
}

public class Address {
    String street;
    String city;
    String zipCode;
}

他们正在这样做:

Address address = new Address(...);
User user = new User("Fred", address);
User shallowCopy = user.clone();

然后

user.getAddress().setZipCode(...);

,并观察到这会更改与两者和原始User对象有关的邮政编码。

请注意,我们正在更改Address的属性,而不是User的属性。引用的文字很清楚地说明了这一点。


但是您的示例不同。这样,您将(分别)更改原始对象和克隆对象的字段。并且观察到分配一个不会影响另一个。

您在做什么等同于

shallowCopy.setAddress(new Address(...));

如果您这样做了,那么shallowCopy所引用的对象将不再是user所引用的对象的浅表副本,并且user.getAddress().setZipCode(...)将不再影响压缩shallowCopy反之亦然

简而言之,您看到与定义中所说的有所不同的原因是您正在做

克隆完全按照指定的方式工作。问题是您误解或错误使用了浅表副本的定义。

答案 1 :(得分:1)

浅拷贝是关于共享内部引用。如果直接更改参考,则克隆将保留在旧参考上,因为它是一个不同的对象。

这就是Java中浅拷贝或浅克隆的名称的原因。如果只有原始类型字段或不可变对象,则Java中的浅表副本和深表副本之间没有区别。这就是为什么您这里实际上有一个深层副本。

如果您将String替换为Date,然后修改日期内容(而不是引用内容,则类似obj1.date().setTime(1)的内容,克隆对象将反映更改,因为它指向到相同的Date参考。

我们可以看到这样的副本

在浅拷贝clone2.date = clone.date

在深层复制clone2.date=new Date(clone.getTime());

答案 2 :(得分:1)

您实施的只是“浅复制”。更改原始对象的属性不会影响复制对象的状态,因为它仅包含不可变字段。 如果其中包含任何易变的对象,则将同时影响它们

如果您尝试添加任何Class类型的另一个字段并尝试更改该类的field的值,那么更改将反映在clone以及clone2对象中。

答案 3 :(得分:0)

super.clone

根据Java文档

创建并返回此对象的副本。

因此,您对旧对象所做的任何更改都将独立于新克隆的对象。 因此clone和clone2是保存在不同内存空间中的单独的Java对象。

如果要复制对clone2的更改,请将两个引用都指向同一对象。 例如

TestClone clone2 = clone;