java.util.Date克隆或复制以不公开内部引用


67

最佳实践是不公开对象(实体)的内部引用。因此,如果一个对象具有一个类型的字段,java.util.Date那么例如该字段的获取器应返回的不是原始日期,而是它的副本。

但是对于java.util.Date,有两种创建该副本的常用方法:

  • 克隆: (Date) originalDate.clone()
  • 通过构造函数复制 new Date(originalDate.getTime())

我的问题是,哪种方法更好,为什么?

Answers:


44

如果绝对是a Date,那么这两种方法都不会有任何区别。

如果实际的对象可能是一个子类Date(如java.sql.Date),那么我倒是希望clone()将保留额外信息(包括它是类),而调用构造函数不会。

顺便说一句,如果您使用Joda Time,则不会有此问题,因为有很多不可变的类型可以使用。这也是一个更好的API :)


8
从Java 8开始,可以使用java.time API代替Joda Time。
阿卡什(Akash)

37

阅读有效的Java。创建副本的首选方法是使用副本构造器方法。

Bill Venners:在书中,您建议使用复制构造函数,而不要实现Cloneable和编写克隆。您能详细说明一下吗?

乔什·布洛赫(Josh Bloch):如果您读过我的书中有关克隆的文章,尤其是您在两行之间阅读时,您会知道我认为克隆已被严重打破。有一些设计缺陷,其中最大的缺陷是Cloneable接口没有克隆方法。这意味着它根本行不通:制作可克隆内容并不能说明您可以使用它做什么。相反,它说明了其内部功能。它说,如果通过重复调用super.clone最终导致调用Object的clone方法,则此方法将返回原始字段的副本。


6
这是在您自己的类中设计克隆的首选方法,尽管它要求调用者知道使用了哪个具体子类,或者不关心使用了哪个具体子类。请参阅我的答案,以获取有关它如何产生影响的示例。
乔恩·斯基特

16

如果要进行防御性编码,则需要复制构造函数。请参阅有效Java的这段内容

还要注意,我们没有使用Date的clone方法制作防御副本。由于Date是非最终的,因此不能保证clone方法返回一个类为java.util.Date的对象。它可能会返回专门为恶意恶作剧而设计的不受信任子类的实例。这样的子类可以在创建实例时在私有静态列表中记录对每个实例的引用,并允许攻击者访问此列表。这将使攻击者可以在所有情况下自由支配。为防止此类攻击,请不要使用clone方法为类型可以由不受信任的子类继承的参数制作防御性副本。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.