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

最好不要公开对象(实体)的内部引用。 所以如果一个Object有一个java.util.Datetypes的字段,那么例如这个字段的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可由不受信任方进行分类。