最佳实践是不公开对象(实体)的内部引用。因此,如果一个对象具有一个类型的字段,java.util.Date
那么例如该字段的获取器应返回的不是原始日期,而是它的副本。
但是对于java.util.Date,有两种创建该副本的常用方法:
- 克隆:
(Date) originalDate.clone()
- 通过构造函数复制
new Date(originalDate.getTime())
我的问题是,哪种方法更好,为什么?
Answers:
阅读有效的Java。创建副本的首选方法是使用副本构造器方法。
Bill Venners:在书中,您建议使用复制构造函数,而不要实现Cloneable和编写克隆。您能详细说明一下吗?
乔什·布洛赫(Josh Bloch):如果您读过我的书中有关克隆的文章,尤其是您在两行之间阅读时,您会知道我认为克隆已被严重打破。有一些设计缺陷,其中最大的缺陷是Cloneable接口没有克隆方法。这意味着它根本行不通:制作可克隆内容并不能说明您可以使用它做什么。相反,它说明了其内部功能。它说,如果通过重复调用super.clone最终导致调用Object的clone方法,则此方法将返回原始字段的副本。
如果要进行防御性编码,则需要复制构造函数。请参阅有效Java的这段内容:
还要注意,我们没有使用Date的clone方法制作防御副本。由于Date是非最终的,因此不能保证clone方法返回一个类为java.util.Date的对象。它可能会返回专门为恶意恶作剧而设计的不受信任子类的实例。这样的子类可以在创建实例时在私有静态列表中记录对每个实例的引用,并允许攻击者访问此列表。这将使攻击者可以在所有情况下自由支配。为防止此类攻击,请不要使用clone方法为类型可以由不受信任的子类继承的参数制作防御性副本。