关于Java可复制

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

我想知道以下几点:

  1. 可复制意味着我们可以通过实现Cloneable接口来拥有一个克隆或一个对象副本。 做这件事有什么好处和坏处?
  2. 如果对象是复合对象,recursion克隆是如何发生的?

谢谢。

你应该知道的关于Cloneable的第一件事是 – 不要使用它。

克隆权是很难实现的,努力也不值得。

而不是使用其他一些选项,比如apache-commons SerializationUtils (deep-clone)或者BeanUtils (shallow-clone),或者简单地使用copy-constructor。

在这里可以看到Josh Bloch关于克隆Cloneable的观点,这解释了这种方法的许多缺点。 ( Joshua Bloch是Sun的一名员工,并领导了众多Java特性的开发。)

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

什么是更改受保护的Object.clone()方法的行为,该方法将为未实现Cloneable的类抛出CloneNotSupportedException,并为类所在的类执行成员级浅表副本。

即使这是你正在寻找的行为,你仍然需要实现你自己的clone()方法来公开它。

在实现你自己的clone()的时候,我们的想法是从super.clone()创build的对象开始,这个对象保证是正确的类,然后再做任何额外的字段,以防止浅拷贝不是你要。 从clone()调用构造函数会有问题,因为如果子类想要添加自己额外的可复制逻辑,这将会中断inheritance。 如果要调用super.clone(),在这种情况下会得到错误类的对象。

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

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

大多数开发人员不会因为这些原因而使用Cloneable,而只是简单地实现一个复制构造函数。

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

  1. 克隆调用一种构造对象的语言外的方式 – 没有构造函数。
  2. 克隆需要你用CloneNotSupportedException以某种方式对待 – 或者为了处理它而烦扰客户端代码。
  3. 好处很小 – 你只需要手动编写一个复制构造函数。

所以,明智地使用Cloneable。 与你需要申请一切正确的努力相比,它没有给你足够的好处。

克隆是一个基本的编程范例。 Java可能以很多方式实施它的事实根本不会减less克隆的需要。 而且,实施可以工作的克隆很容易,但是你希望它能够工作,浅层,深层,混合等等。 你甚至可以使用名称克隆的function,而不是实现克隆,如果你喜欢。

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

 ArrayList<A> list1; 

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

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

如果对象实际上是typesB或C,则不会得到正确的副本。 而且,如果A是抽象的呢? 现在有人提出这样的build议:

 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)); } } 

这是一个非常非常糟糕的主意。 如果你添加一个新的派生types呢? 如果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,只是使用相同的函数名称。 如果你不喜欢,就把它命名为别的。

A)克隆拷贝构造函数没有太多的优点。 可能最大的一个是创build完全相同的dynamictypes的新对象(假设声明的types是可克隆的并具有公共克隆方法)的能力。

B)默认克隆创build一个浅拷贝,它将保持一个浅拷贝,除非你的克隆实现改变了这个拷贝。 这可能是困难的,特别是如果你的class级有最后的领域

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

Cloneable的缺点是什么?

克隆是非常危险的,如果你正在复制的对象有composition.You需要考虑在这种情况下可能的副作用,因为克隆创build浅拷贝:

假设你有一个对象来处理数据库相关的操作。 比方说,那个对象有Connection对象作为属性之一。

所以当有人创buildoriginalObject克隆时,创build的对象,比方说cloneObject 。 这里originalObjectcloneObjectConnection对象保持相同的引用。

假设originalObjectclosures了Connection对象,所以cloneObject将不起作用,因为connection对象是在它们之间共享的,并且被originalObject实际closures了。

如果假设你想克隆一个具有IOStream属性的对象,可能会出现类似的问题。

如果对象是复合对象,recursion克隆是如何发生的?

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