关于Java可克隆

时间:2010-11-02 20:35:50

标签: java cloneable

我正在寻找一些解释Java Cloneable的教程,但没有得到任何好的链接,而且Stack Overflow正变得越来越明显。

我想知道以下内容:

  1. Cloneable表示我们可以拥有克隆或对象的副本 实现Cloneable接口。有什么优点和 这样做的缺点?
  2. 如果对象是a,则递归克隆是如何发生的 复合对象?

6 个答案:

答案 0 :(得分:149)

关于Cloneable,您应该了解的第一件事是 - 不要使用它。

使用Cloneable正确实施克隆非常困难,并且努力是不值得的。

而不是使用其他一些选项,例如apache-commons SerializationUtils(深度克隆)或BeanUtils(浅层克隆),或者只使用复制构造函数。

See here了解Josh Bloch关于使用Cloneable进行克隆的观点,这解释了该方法的许多缺点。 (Joshua Bloch是Sun的一名员工,并领导了许多Java功能的开发。)

答案 1 :(得分:39)

不幸的是,Cloneable本身只是一个标记接口,它是:它没有定义clone()方法。

做什么,是改变受保护的Object.clone()方法的行为,它会为没有实现Cloneable的类抛出CloneNotSupportedException,并为那些执行类的成员执行浅层复制。

即使这是您正在寻找的行为,您仍然需要实现自己的clone()方法才能将其公开。

当实现你自己的clone()时,我们的想法是从super.clone()创建的对象开始,该对象保证是正确的类,然后在浅拷贝的情况下再做任何字段。不是你想要的。从clone()调用构造函数会有问题,因为如果子类想要添加自己的附加可克隆逻辑,这会破坏继承;如果要调用super.clone(),在这种情况下会得到错误类的对象。

这种方法绕过了构造函数中可能定义的任何逻辑,但可能存在问题。

另一个问题是,任何忘记覆盖clone()的子类都会自动继承默认的浅拷贝,这在可变状态(现在将在源和拷贝之间共享)的情况下可能不是你想要的。

大多数开发人员出于这些原因不使用Cloneable,而只是实现了一个复制构造函数。

有关Cloneable的更多信息和潜在缺陷,我强烈推荐Joshua Bloch撰写的Effective Java一书

答案 2 :(得分:11)

  1. 克隆调用一种超语言的构造对象的方式 - 没有构造函数。
  2. 克隆要求您以某种方式处理CloneNotSupportedException - 或者打扰客户端代码进行处理。
  3. 好处很小 - 您不必手动编写复制构造函数。
  4. 所以,明智地使用Cloneable。与您为完成所有事情所需的努力相比,它没有给您足够的好处。

答案 3 :(得分:7)

克隆是一种基本的编程范例。 Java可能在很多方面实现得很差这一事实并没有减少对克隆的需求。并且,很容易实现克隆,无论你希望它能够工作,浅薄,深入,混合,等等。你甚至可以为函数使用名称clone,如果你愿意,也不要实现Cloneable。

假设我有类A,B和C,其中B和C是从A派生的。如果我有类似A的对象列表,如下所示:

ArrayList<A> list1;

现在,该列表可以包含A,B或C类型的对象。您不知道对象是什么类型。所以,你不能像这样复制列表:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    list2.add(new A(a));
}

如果对象实际上是B或C类型,则无法获得正确的副本。而且,如果A是抽象的呢?现在,有人建议这样做:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    if(a instanceof A) {
        list2.add(new A(a));
    } else if(a instanceof B) {
        list2.add(new B(a));
    } else if(a instanceof C) {
        list2.add(new C(a));
    }
}

这是一个非常非常糟糕的主意。如果添加新的派生类型怎么办?如果B或C在另一个包中并且您在本课程中无法访问它们怎么办?

您想要做的是:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    list2.add(a.clone());
}

很多人都说明了为什么克隆的基本Java实现存在问题。但是,它很容易通过这种方式克服:

在A班:

public A clone() {
    return new A(this);
}

在B班:

@Override
public B clone() {
    return new B(this);
}

在C班:

@Override
public C clone() {
    return new C(this):
}

我没有使用相同的函数名实现Cloneable。如果您不喜欢这样,请将其命名为其他内容。

答案 4 :(得分:5)

A)克隆复制构造函数没有很多优点。可能最大的一个是能够创建完全相同动态类型的新对象(假设声明的类型是可克隆的并且具有公共克隆方法)。

B)默认克隆创建一个浅拷贝,除非克隆实现改变了,否则它将保持浅拷贝。这可能很困难,尤其是如果您的班级有最终字段

Bozho是对的,克隆可能很难做对。复制构造函数/工厂将满足大多数需求。

答案 5 :(得分:0)

Cloneable的缺点是什么?

如果要复制的对象具有组合,克隆是非常危险的。在这种情况下,您需要考虑下面可能的副作用,因为克隆会创建浅拷贝:

假设您有一个对象来处理与数据库相关的操作。比如说,该对象有Connection个对象作为属性之一。

因此,当有人创建originalObject的克隆时,正在创建的对象,即cloneObject。 这里originalObjectcloneObjectConnection对象持有相同的引用。

请说originalObject关闭Connection对象,因此现在cloneObject将无效,因为connection对象在它们之间共享,并且originalObject对象已被{ {1}}。

如果您想要克隆一个将IOStream作为属性的对象,可能会出现类似的问题。

如果对象是复合对象,如何进行递归克隆?

Cloneable执行浅拷贝。意思是原始对象和克隆对象的数据将指向相同的引用/内存。 相反,在深层复制的情况下,原始对象的内存中的数据被复制到克隆对象的内存中。