5

我正在阅读 Joshua Bloch 的 Effective Java。在那里,他谈到不使用Clonable界面。我有点菜鸟,所以我的问题是,在代码中需要克隆时的用例是什么?有人可以举一个棘手的例子,以便我掌握这个概念吗?

4

4 回答 4

6

A clone() interface provides a mechanism to create a "shallow" copy of an object. That is, by default, more memory would be allocated for the copy, and each part of the original would be copied into the copy. In contrast, a simple assignment of an object instance to a variable would result in an additional reference to the same object. While the cloned object is itself a true copy, its contained elements are by default references to the ones referred to from the original. If a true "deep" copy is needed, the clone() method would need to be specialized to create clones of its members as well.

One possible use case for a clone() interface would be for implementing a version history of an object to allow a rollback to an older version of it. This could be used in transactional systems, like databases.

Another use case would be for implementing copy-on-write, and this can be useful when the user of the object is only provided a read-only version of the object.

* Description of clone corrected, with thanks and kudos to newacct.

于 2014-05-14T03:43:14.463 回答
2

来自关于原型模式的维基百科,

...当您想在运行时clone()创建另一个对象时,您需要一个对象,该对象是您正在克隆的对象的真实副本真正的复制意味着新创建的对象的所有属性都应该与您正在克隆的对象相同。如果您可以使用new来实例化该类,您将获得一个具有所有属性作为其初始值的对象。例如,如果您正在设计一个用于执行银行账户交易的系统,那么您会想要制作一个包含您的账户信息的对象的副本,在其上执行交易,然后用修改后的对象替换原始对象。在这种情况下,您可能希望使用clone()而不是新的。

于 2014-05-14T03:40:53.260 回答
1

一个例子是参数的防御性复制(有效的 Java 第 39 条:在需要时制作防御性副本)

class X {
  private byte[] a;

  X(byte[] a) {
      this.a = a.clone();
  }
...
于 2014-05-14T04:05:53.700 回答
1

在 Java 中克隆对象有很好的理由。考虑以下代码:

MyObj first = new MyObj(someOwner, someTag, someInt);
MyObj second = first;

在这种情况下,我们只是复制该对象的引用(内存地址)。变量firstsecond都引用 MyObj 类的同一个实例。clone() 方法应该实现的是所谓的深拷贝。当然,这取决于实现:克隆方法的开发人员需要确保真正实现的是深拷贝。所以,代码:

MyObj third = (MyObj) first.clone();

在这种情况下,clone() 所做的是遍历所有first的实例成员并复制/克隆这些成员,并使用这些值创建一个全新的 MyObj 实例。然后它返回对新实例的引用,因此第三个是第一个的副本,而不仅仅是对同一实例的引用。

针对您在评论中提出的问题,clone 是否对成员变量进行新的克隆,或者只是复制引用,这取决于您的实现。考虑以下 MyObj 示例类的实现,假设还存在类 Person 和 NameTag。如果您克隆一个 MyObj 对象,您可能希望新克隆引用与原始对象相同的 Owner 实例,但制作 NameTag 的深层副本(当然,这只是使用虚构类的示例)。这将表示MyObj 和 NameTag 实例之间的一对一关系,以及 Owner 和 MyObj 实例之间的一对多关系。以下代码考虑了您的问题中提到的两种情况:

class MyObj implements Cloneable {

    private Person owner;
    private NameTag tag;
    private int size; 

    public MyObj(Person owner, NameTag tag, int size) {
         this.owner = owner;
         this.tag = tag;
         this.size = size;
    }

    public Object clone() {

        MyObj result;

        //call all super clone() methods before doing class specific work
        //this ensures that no matter where you are in the inheritance heirarchy,
        //a deep copy is made at each level.
        try {
            result = (MyObj) super.clone();
        }
        catch (CloneNotSupportedException e) {
            throw new RuntimeException
            ("Super class doesn't implement cloneable");
        }

        //work specific to the MyObj class
        result.owner = owner;     //We want the reference to the Person, not a clone
        result.tag = tag.clone()  //We want a deep copy of the NameTag
        result.size = size;       //ints are primitive, so this is a copy operation    

        return result;
}
于 2014-05-14T03:41:12.740 回答