Java 防御副本

标签 java theory defensive-programming

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

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

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

我读过 clone() 并非在所有情况下都有效,但我不明白为什么。

最佳答案

我可以试着回答这个问题,但我只是在剽窃 Josh Bloch,所以这里是资源链接:

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.

关于Java 防御副本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2234446/

相关文章:

java - 标识符名称中的数字没有问题吗?

java - 更改 Java 应用程序中文件的位置

java - Apache Cassandra 的高级 Java 客户端选择

oop - Java/C#等中反射的用途是什么

assembly - 有什么可以防止汇编中的堆栈溢出吗?

C语法问题

Java exec() mysqldump 错误 : Couldn't find table ">"

java - [[我不能被转换为 [I Exception

algorithm - 爬山算法的时间复杂度是多少?

java - 从 Java Map<String, Object> propertyMap 添加条目的*副本*