关于Java可复制
我正在寻找一些关于Java Cloneable
教程,但没有得到任何好的链接,Stack Overflow越来越明显。
我想知道以下几点:
- 可复制意味着我们可以通过实现
Cloneable
接口来拥有一个克隆或一个对象副本。 做这件事有什么好处和坏处? - 如果对象是复合对象,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一书
- 克隆调用一种构造对象的语言外的方式 – 没有构造函数。
- 克隆需要你用CloneNotSupportedException以某种方式对待 – 或者为了处理它而烦扰客户端代码。
- 好处很小 – 你只需要手动编写一个复制构造函数。
所以,明智地使用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
。 这里originalObject
和cloneObject
对Connection
对象保持相同的引用。
假设originalObject
closures了Connection
对象,所以cloneObject
将不起作用,因为connection
对象是在它们之间共享的,并且被originalObject实际closures了。
如果假设你想克隆一个具有IOStream属性的对象,可能会出现类似的问题。
如果对象是复合对象,recursion克隆是如何发生的?
可复制执行浅拷贝。 意思是原始对象和克隆对象的数据将指向相同的引用/内存。 相反,在深拷贝的情况下,原始对象的内存中的数据被复制到克隆对象的内存中。