最佳做法是不公开Object(Entity)的内部引用。因此,如果一个Object有一个java.util.Date
类型的字段,那么例如该字段的getter应该不是原始日期而是它的副本。
但是对于java.util.Date,有两种常用的方法来创建该副本:
(Date) originalDate.clone()
new Date(originalDate.getTime())
我的问题是,哪种方式更好,为什么?
答案 0 :(得分:43)
如果它绝对只是一个Date
,它不会有任何区别。
如果实际对象可能是Date
的子类(例如java.sql.Date
),那么我希望clone()
能保留额外的信息(包括它是哪个类)而调用构造函数则不会。
顺便说一句,如果您使用Joda Time,则不会出现此问题,因为有大量不可变类型可供使用。它也是一个更好的API:)
答案 1 :(得分:35)
阅读Effective Java。创建副本的首选方法是使用复制构造方法。
Bill Venners:在你的书中,你建议使用复制构造函数 而不是实现Cloneable和编写克隆。您可以...吗 详细说明了吗?
Josh Bloch:如果你在我的书中读过关于克隆的项目, 特别是如果你在两行之间阅读,你会知道我的想法 克隆深受打击。有一些设计缺陷,最大的 这是Cloneable接口没有克隆方法。 这意味着它根本不起作用:制作可克隆的东西 没有说明你可以用它做什么。相反,它说 关于内部可以做些什么的事情。它说,如果通过电话 super.clone反复最终调用Object的clone方法,这个 method将返回原始的字段副本。
答案 2 :(得分:15)
如果您正在进行防御性编码,那么您将需要复制构造函数。见this passage from Effective Java:
另请注意,我们没有使用Date的克隆方法来制作防御性副本。因为Date是非最终的,所以不能保证clone方法返回一个类为java.util.Date的对象;它可以返回一个专门为恶意恶作剧设计的不受信任的子类的实例。例如,这样的子类可以在创建私有静态列表时记录对每个实例的引用,并允许攻击者访问该列表。这将使攻击者在所有实例中自由统治。要防止此类攻击,请不要使用克隆方法制作类型可由不信任方进行子类化的参数的防御副本。