java.util.Date克隆或复制到不公开内部引用
最好不要公开对象(实体)的内部引用。 所以如果一个Object有一个java.util.Date
types的字段,那么例如这个字段的getter不应该返回原始date,而是返回它的一个副本。
但是对于java.util.Date,有两种常见的方法来创build该副本:
- 克隆:(
(Date) originalDate.clone()
- 通过构造函数复制
new Date(originalDate.getTime())
我的问题是,哪种方式更好,为什么?
如果它绝对只是一个Date
,那么两者都不会有任何区别。
如果实际的对象可能是Date
一个子类 (比如java.sql.Date
),那么我希望clone()
会保留额外的信息(包括它的类),而调用构造函数不会。
另外,如果你使用Joda Time,你不会有这个问题,因为有很多不可变的types可供使用。 这也是一个更好的API 🙂
阅读有效的Java 。 创build副本的首选方法是使用复制构造函数方法。
Bill Venners:在你的书中,你推荐使用复制构造函数,而不是实现Cloneable和编写克隆。 你能详细解释一下吗?
Josh Bloch:如果你在我的书中读到了关于克隆的东西,尤其是如果你在两行之间阅读的话,你会知道我认为克隆已经被打破了。 有一些devise缺陷,其中最大的问题是Cloneable接口没有克隆方法。 这意味着它根本行不通:制作一个Cloneable并不能说明你能用它做什么。 相反,它说了一些关于内部可以做什么的事情。 它说,如果通过重复调用super.clone结束调用Object的克隆方法,这个方法将返回原始的字段副本。
如果你在防守编码,你会想复制构造函数。 请参阅Effective Java中的这段话 :
还要注意,我们没有使用Date的克隆方法来制作防御副本。 因为Date是非终结的,所以克隆方法不保证返回一个类为java.util.Date的对象; 它可以返回一个专门为恶意恶作剧devise的不受信任的子类的实例。 例如,这样的子类可以在其创build时logging对私有静态列表中的每个实例的引用,并允许攻击者访问该列表。 这将使攻击者对所有实例自由统治。 为了防止这种攻击,请不要使用克隆方法来制作一个参数的防御副本,其types可由不受信任方进行分类。