Java防御副本

发布于 2024-08-21 01:28:30 字数 255 浏览 3 评论 0原文

我见过这样编码的防御副本

void someMethod(Date d) {
    myDate = new Date( d.getTime() );
}

但这对我来说没有意义,Java 中是否有一种方法可以在该对象的内存中创建相同的副本?

我已经读过 clone() 不会在所有情况下工作,但我不明白为什么。

I've seen defensive copies coded like this

void someMethod(Date d) {
    myDate = new Date( d.getTime() );
}

But that doesn't make sense to me, isn't there a way in Java to create an identical copy in memory of that object?

I've read the clone() will not work in all instances, but I don't understand why.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

冷心人i 2024-08-28 01:28:30

我可以尝试回答这个问题,但我只是抄袭乔什·布洛赫(Josh Bloch),所以这里有一个资源链接:

复制构造函数与克隆

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

Josh Bloch:如果你读过我书中有关克隆的内容,特别是如果你读到字里行间的内容,你就会知道我认为克隆已经被彻底破坏了。有一些设计缺陷,其中最大的缺陷是 Cloneable 接口没有 clone 方法。这意味着它根本不起作用:使某些东西Cloneable并没有说明你可以用它做什么。相反,它说明了它在内部可以做什么。它表示,如果重复调用 super.clone 最终会调用 Objectclone 方法,则该方法将返回原创。

但它没有说明您可以使用实现 Cloneable 接口的对象做什么,这意味着您无法执行多态 clone 操作。如果我有一个可克隆的数组,您可能会认为我可以运行该数组并克隆每个元素以制作该数组的深层副本,但我不能。您无法将某些内容转换为 Cloneable 并调用 clone 方法,因为 Cloneable 没有公共 clone 方法Object 也没有。如果您尝试转换为Cloneable并调用clone方法,编译器会说您正在尝试调用对象上受保护的clone方法。

事实是,除了复制功能之外,通过实现 Cloneable 和提供公共 clone 方法,您不会向客户端提供任何功能。这并不比您提供具有不同名称的复制操作并且不实现 Cloneable 所得到的结果更好。这基本上就是您使用复制构造函数所做的事情。复制构造函数方法有几个优点,我在书中讨论了这些优点。一个很大的优点是副本可以具有与原件不同的表现形式。例如,您可以将 LinkedList 复制到 ArrayList 中。

Objectclone 方法非常棘手。它基于现场副本,并且是“超语言的”。它创建一个对象而不调用构造函数。无法保证它保留构造函数建立的不变量。多年来,Sun 内部和外部都出现了很多错误,其根源在于,如果您只是在链上重复调用 super.clone 直到克隆了一个对象,那么您就会得到一个浅层的错误。对象的副本。克隆通常与被克隆的对象共享状态。如果该状态是可变的,那么您就没有两个独立的对象。如果修改其中一个,另一个也会发生变化。突然间,你会得到随机行为。

我很少再使用Cloneable了。我经常在具体类上提供公共clone 方法,因为人们期望它。我没有实现 Cloneable 的抽象类,也没有扩展它的接口,因为我不会将实现 Cloneable 的负担放在所有扩展的类上(或实现)抽象类(或接口)。这是一个真正的负担,而且几乎没有什么好处。

Doug Lea 走得更远。他告诉我,除了复制数组之外,他不再使用克隆。您应该使用clone来复制数组,因为这通常是最快的方法。但 Doug 的类型根本不再实现 Cloneable 。他已经放弃了。我认为这并非没有道理。

遗憾的是,Cloneable 被破坏了,但它确实发生了。最初的 Java API 在紧迫的期限内很快完成,以满足市场关闭的需求。最初的 Java 团队做了令人难以置信的工作,但并不是所有的 API 都是完美的。 Cloneable 是一个弱点,我认为人们应该意识到它的局限性。

I could try to answer this, but I'd just be plagiarizing Josh Bloch, so here's a link to the resource instead:

Copy Constructor versus Cloning

Bill Venners: In your book you recommend using a copy constructor instead of implementing Cloneable and writing clone. Could you elaborate on that?

Josh Bloch: If you've read the item about cloning in my book, especially if you read between the lines, you will know that I think clone is deeply broken. There are a few design flaws, the biggest of which is that the Cloneable interface does not have a clone method. And that means it simply doesn't work: making something Cloneable doesn't say anything about what you can do with it. Instead, it says something about what it can do internally. It says that if by calling super.clone repeatedly it ends up calling Object's clone method, this method will return a field copy of the original.

But it doesn't say anything about what you can do with an object that implements the Cloneable interface, which means that you can't do a polymorphic clone operation. If I have an array of Cloneable, you would think that I could run down that array and clone every element to make a deep copy of the array, but I can't. You cannot cast something to Cloneable and call the clone method, because Cloneable doesn't have a public clone method and neither does Object. If you try to cast to Cloneable and call the clone method, the compiler will say you are trying to call the protected clone method on object.

The truth of the matter is that you don't provide any capability to your clients by implementing Cloneable and providing a public clone method other than the ability to copy. This is no better than what you get if you provide a copy operation with a different name and you don't implement Cloneable. That's basically what you're doing with a copy constructor. The copy constructor approach has several advantages, which I discuss in the book. One big advantage is that the copy can be made to have a different representation from the original. For example, you can copy a LinkedList into an ArrayList.

Object's clone method is very tricky. It's based on field copies, and it's "extra-linguistic." It creates an object without calling a constructor. There are no guarantees that it preserves the invariants established by the constructors. There have been lots of bugs over the years, both in and outside Sun, stemming from the fact that if you just call super.clone repeatedly up the chain until you have cloned an object, you have a shallow copy of the object. The clone generally shares state with the object being cloned. If that state is mutable, you don't have two independent objects. If you modify one, the other changes as well. And all of a sudden, you get random behavior.

There are very few things for which I use Cloneable anymore. I often provide a public clone method on concrete classes because people expect it. I don't have abstract classes implement Cloneable, nor do I have interfaces extend it, because I won't place the burden of implementing Cloneable on all the classes that extend (or implement) the abstract class (or interface). It's a real burden, with few benefits.

Doug Lea goes even further. He told me that he doesn't use clone anymore except to copy arrays. You should use clone to copy arrays, because that's generally the fastest way to do it. But Doug's types simply don't implement Cloneable anymore. He's given up on it. And I think that's not unreasonable.

It's a shame that Cloneable is broken, but it happens. The original Java APIs were done very quickly under a tight deadline to meet a closing market window. The original Java team did an incredible job, but not all of the APIs are perfect. Cloneable is a weak spot, and I think people should be aware of its limitations.

铃予 2024-08-28 01:28:30

clone() 仅在某些类中有意义地实现,因为克隆涉及 JVM 或编译器无法做出的许多决策(例如,浅复制与深复制)。有些人还认为 Java 中的整个概念被破坏了,因此很少被使用。

然而,最终的结果是许多类无法克隆。在这些情况下,用户通过基于另一个对象生成和初始化一个对象来克隆它们。

但是,Date 类实现了 Cloneable,因此与仅使用“复制构造函数”相比,在这种特定情况下并没有真正的好处。

最好使用克隆的一种情况是当您使用类层次结构时。想象一下,您将一个数学表达式表示为表达式子类型的树,其中引用最外层表达式。

假设在本例中它是 Plus。您可以在对表达式的引用上调用克隆,但您真正得到的是 Plus 的新实例。对于构造函数,您实际上无法做到这一点,因为您的表达式可能是接口或抽象类。

clone() is only implemented meaningfully in some classes, because there are a lot of decisions involved in cloning that the JVM or the compiler doesn't make for you (e.g., shallow vs. deep copying). Some would also argue that the whole concept in Java is broken and therefore rarely used.

The end result, however, is that many classes cannot be cloned. In these cases, a user clones them by generating and initializing one object based on the other.

However, the Date class implements Cloneable, so there isn't really a benefit in this specific case compared to just using the "copy constructor".

One situation where cloning is preferable is when you are working with class hierarchies. Imagine you are representing a mathematical expression as a tree of Expression subtypes, where you reference the outermost expression.

Let's say that in this case it is a Plus. You would call clone on your reference to an Expression, but what you would really get is a new instance of Plus. With constructors you can't really do that because your Expression could be an interface or an abstract class.

安静 2024-08-28 01:28:30

没有简单的方法可以制作始终有效的相同副本。

克隆本应做到这一点,但并非所有类都实现了它,因此或多或少被破坏了。

另一种方法是序列化/反序列化,但并非所有类都支持序列化。

There is no simple way to make an identical copy that always works.

Clone was supposed to do that, but it is not implemented by all classes and thus more or less broken.

Another approach would be to serialize/deserialize, but serialization is also not supported by all classes.

治碍 2024-08-28 01:28:30

从移动代码安全的角度来看,clone 通常可以被覆盖。

@Override public Date clone() {
    return this; // Ha!
}

即使您不关心安全性,您也可以想象一个程序员“聪明”,导致您的代码中出现错误报告。

特别是 clone 将返回完全相同的运行时类型的期望也会导致实现问题。

From a mobile code security point of view, clone can typically be overridden.

@Override public Date clone() {
    return this; // Ha!
}

Even if you don't care about security, you could imagine a programmer being "clever" causing a bug report in your code.

The expectation that particularly clone will return exactly the same runtime type also causes implementation problems.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文