我见过像这样的防御性副本
void someMethod(Date d) {
myDate = new Date( d.getTime() );
}
但这对我来说没有意义,Java中没有办法在该对象的内存中创建相同的副本吗?
我已经阅读了
clone()
不适用于所有实例的情况,但是我不明白为什么。 最佳答案
我可以尝试回答这个问题,但我只是窃乔什·布洛赫(Josh Bloch),所以这里有一个指向资源的链接:
Copy Constructor versus Cloning
Bill Venners:在书中,您建议使用复制构造函数而不是实现Cloneable
并编写clone
。您能详细说明一下吗?
乔什·布洛赫(Josh Bloch):如果您读过我的书中有关克隆的文章,尤其是您在两行之间阅读时,您会知道我认为克隆已被严重打破。有一些设计缺陷,其中最大的缺陷是Cloneable
接口没有clone
方法。这意味着它根本行不通:制作Cloneable
并没有说明您可以使用它做什么。相反,它说明了其内部功能。它说,如果通过重复调用super.clone
最终导致调用Object
的clone
方法,则此方法将返回原始字段的副本。
但这并没有说明您可以对实现Cloneable
接口的对象执行的操作,这意味着您无法执行多态的clone
操作。如果我有一个Cloneable
数组,您可能会认为我可以用尽该数组并克隆每个元素以制作该数组的深层副本,但我做不到。您不能将某些内容强制转换为Cloneable
并调用clone
方法,因为Cloneable
没有公共的clone
方法,Object
也没有。如果尝试强制转换为Cloneable
并调用clone
方法,则编译器会说您正在尝试对对象调用受保护的clone
方法。
事实的真相是,除了实现复制功能外,您没有通过实现Cloneable
并提供公共的clone
方法来向客户提供任何功能。如果您提供使用不同名称的复制操作并且未实现Cloneable
,那么这不会比您得到的更好。基本上,这就是您使用复制构造函数所做的事情。复制构造函数方法有几个优点,我将在本书中进行讨论。一个很大的优点是可以使副本具有与原始副本不同的表示形式。例如,您可以将LinkedList
复制到ArrayList
。
Object
的clone
方法非常棘手。它基于字段副本,并且是“额外语言”。它创建一个对象而不调用构造函数。不能保证它保留构造函数建立的不变式。多年来,在Sun内外都有许多错误,这是由于您在链中重复调用super.clone
直到克隆了一个对象,才得到该对象的浅表副本。克隆通常与要克隆的对象共享状态。如果该状态是可变的,则您没有两个独立的对象。如果您修改其中一个,则其他也将更改。突然之间,您会得到随机的行为。
我很少使用Cloneable
了。我经常在具体的类上提供公共的clone
方法,因为人们期望这样做。我没有抽象类实现Cloneable
,也没有接口对其进行扩展,因为我不会在扩展(或实现)抽象类(或接口)的所有类上放置实现Cloneable
的负担。 。这是一个真正的负担,几乎没有好处。
道格·李(Doug Lea)走得更远。他告诉我,除了复制数组以外,他不再使用clone
了。您应该使用clone
复制数组,因为通常这是最快的方法。但是Doug的类型根本不再实现Cloneable
了。他放弃了。我认为这并非不合理。
令人遗憾的是,Cloneable
损坏了,但是它确实发生了。最初的Java API在紧迫的期限内很快就完成了,以适应市场的逐渐关闭。最初的Java团队做得非常出色,但是并非所有的API都是完美的。 Cloneable
是一个弱点,我认为人们应该意识到它的局限性。