为什么一个对象不能被克隆?

5

我读了很多有关Object类中的clone()方法和Cloneable接口的线程,但是没有找到一个合理的答案来回答我的问题。

简而言之:

我发现Object类有一个名为clone()的方法,可以“神奇地”复制你的对象。但是,如果不实现Cloneable接口,则无法使用该方法,因为该接口允许Object使用clone()方法。那么为什么要这样做呢?为什么不应该从一开始就使每个对象都能够被克隆呢?


1
像“为什么要以这种方式实现foo?”这样的开放性问题,没有明确的答案,不适合在StackOverflow上提问。请参阅[FAQ]。 - Keppil
1
你做完你的作业了吗? - Yan Foto
3个回答

4

对于一些可变数据,实现Cloneable是有意义的。

但对于以下情况则没有意义:

  • 不可变数据
  • 需要浅拷贝或深拷贝的数据
  • 表示资源(例如线程、套接字、GUI组件)的对象
  • 单例和枚举类型
  • 可变状态的数据,只需复制数据以避免创建新对象。

某些编码风格建议尽量减少新的可变数据对象。 Cloneable并不适用于所有情况,如果您使所有对象都可克隆,则无法干净地关闭它。

注意:有许多项目在任何地方都避免使用Cloneable。


在不可变对象上调用 clone 可能只会返回相同的对象,这并没有什么问题。克隆的唯一根本问题(但是很大)出现在一个对象封装了某个对象或实体的可变状态并且还封装了该对象的标识时。当克隆单个对象时,克隆体可以封装原始状态的分离副本,也可以维护用于封装原始状态的对象的标识,但不能同时具备两者。 - supercat
如果有一个惯例,即不可变类型应该将clone实现为简单的return this,那么对于值集合和引用集合的分离类型是有意义的(值应该实现cloneable,而引用的克隆方法则不会被使用)。为了真正使其工作,应该有一个 cloneableObject具有受保护的“magic” memberwiseClone 方法,它将始终工作,而不是在对象中具有可能无法工作的clone方法;公共clone方法的支持将独立于从cloneableObject派生。 - supercat

1

如果每个对象(包括用户定义类的对象)都可以通过使用clone方法进行克隆,那么可能会导致意外改变被克隆的原始对象,因为clone提供了原始对象的浅拷贝。例如:

class MyClass
{
  private int val;
  public void setVal(int i)
  {
    this.val = i;
  }
  public int getVal()
  {
    return this.val;
  }
  public static void main(String st[]) throws Exception
  {
    ArrayList<MyClass> ar = new ArrayList<MyClass>();
    MyClass mc = new MyClass();
    mc.setVal(50);
    ar.add(mc);
    ArrayList<MyClass> copy =(ArrayList) ar.clone();//Since ArrayList is cloneable
    copy.get(0).setVal(10);
    System.out.print(ar.get(0).getVal());//it shows 10 instead of 50!!
  }
}

因此,我们可以看出克隆可能会导致被复制的原始对象的内部状态不一致。为了避免这种情况,定义类时给程序员选择是否要克隆该类的对象。

感谢您提供生动的例子 :) - user2338815

-2

克隆意味着创建对象的新副本,并且不像赋值运算符那样共享内存位置。

示例

         MyObject obj1 = new MyObject();
         obj2 = obj1;

在这种情况下,如果您对obj1进行任何更改,则会反映在obj2中,反之亦然,因为obj2保存了obj1的内存位置。

但是,如果您不希望在obj1中看到对obj2所做的更改,或者不希望在obj2中看到对obj1所做的任何更改,则可以执行克隆操作。在这种情况下,两个对象将具有不同的内存位置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接