关于Java可克隆

day*_*mer 91 java cloneable

我正在寻找一些解释Java的教程Cloneable,但没有得到任何好的链接,而且Stack Overflow正变得越来越明显.

我想知道以下内容:

  1. Cloneable意味着我们可以通过实现Cloneable接口来获得克隆或对象的副本.这样做的优点和缺点是什么?
  2. 如果对象是复合对象,递归克隆如何发生?

Boz*_*zho 152

你应该知道的第一件事Cloneable是 - 不要使用它.

Cloneable正确的方法实现克隆是非常困难的,并且努力是不值得的.

而不是使用其他选项,如apache-commons SerializationUtils(深度克隆)或BeanUtils(浅层克隆),或者只使用复制构造函数.

请参阅此处了解Josh Bloch关于克隆的观点Cloneable,这解释了该方法的许多缺点.(Joshua Bloch是Sun的一名员工,领导了许多Java功能的开发.)

  • 请注意,Block表示不使用Cloneable.他没有说不使用克隆(或者至少我不希望).有许多方法可以简单地实现克隆,这些方法比使用反射的SerializationUtils或BeanUtils等类更有效.请参阅下面的帖子以获取示例. (3认同)

Luk*_*man 39

不幸的是,Cloneable本身只是一个标记接口,它是:它没有定义clone()方法.

做的是改变受保护的Object.clone()方法的行为,该方法将为不实现Cloneable的类抛出CloneNotSupportedException,并为那些执行成员的类执行浅层复制.

即使这是您正在寻找的行为,您仍然需要实现自己的clone()方法才能将其公开.

在实现你自己的clone()时,我们的想法是从super.clone()创建的对象开始,它保证是正确的类,然后在浅副本不是什么的情况下再做任何其他字段.你要.从clone()调用构造函数会有问题,因为如果子类想要添加自己的附加可克隆逻辑,这会破坏继承; 如果是调用super.clone(),在这种情况下会得到错误类的对象.

这种方法绕过了构造函数中可能定义的任何逻辑,这可能会产生问题.

另一个问题是,任何忘记覆盖clone()的子类都会自动继承默认的浅拷贝,这可能不是你想要的可变状态(现在将在源和副本之间共享).

大多数开发人员出于这些原因不使用Cloneable,而只是实现了一个复制构造函数.

有关Cloneable的更多信息和潜在缺陷,我强烈推荐Joshua Bloch撰写的Effective Java一书


Vla*_*nov 11

  1. 克隆调用一种超语言的构造对象的方式 - 没有构造函数.
  2. 克隆要求您以某种方式处理CloneNotSupportedException - 或者打扰客户端代码来处理它.
  3. 好处很小 - 您不必手动编写复制构造函数.

所以,明智地使用Cloneable.与您为完成所有事情所需的努力相比,它没有给您足够的好处.

  • @Bane:假设您有一个对象列表,这些对象都是从类型A派生的,可能有10种不同的类型.你不知道每个对象的类型是什么.在这种情况下使用instanceof是一个非常糟糕的主意.如果添加其他类型,则每次执行此操作时都必须添加另一个instanceof测试.如果派生类在另一个包中甚至无法访问,该怎么办?克隆是一种常见的模式.是的,java实现很糟糕,但有很多方法可以正常工作.复制构造函数不是等效操作. (4认同)

Cha*_*les 7

克隆是一种基本的编程范例.Java可能在很多方面实现得很差这一事实并没有减少对克隆的需求.并且,很容易实现克隆,无论你希望它能够工作,浅薄,深入,混合,等等.您甚至可以使用名称clone作为函数,如果愿意,也不要实现Cloneable.

假设我有类A,B和C,其中B和C是从A派生的.如果我有类型A的对象列表,如下所示:

ArrayList<A> list1;
Run Code Online (Sandbox Code Playgroud)

现在,该列表可以包含A,B或C类型的对象.您不知道对象是什么类型.所以,你不能像这样复制列表:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    list2.add(new A(a));
}
Run Code Online (Sandbox Code Playgroud)

如果对象实际上是B或C类型,则无法获得正确的副本.而且,如果A是抽象的呢?现在,有人建议:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    if(a instanceof A) {
        list2.add(new A(a));
    } else if(a instanceof B) {
        list2.add(new B(a));
    } else if(a instanceof C) {
        list2.add(new C(a));
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个非常非常糟糕的主意.如果添加新的派生类型怎么办?如果B或C在另一个包中并且您在本课程中无法访问它们怎么办?

你想做的是:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    list2.add(a.clone());
}
Run Code Online (Sandbox Code Playgroud)

很多人都说明了为什么克隆的基本Java实现存在问题.但是,它很容易克服这种方式:

在A班:

public A clone() {
    return new A(this);
}
Run Code Online (Sandbox Code Playgroud)

在B班:

@Override
public B clone() {
    return new B(this);
}
Run Code Online (Sandbox Code Playgroud)

在C级:

@Override
public C clone() {
    return new C(this):
}
Run Code Online (Sandbox Code Playgroud)

我没有实现Cloneable,只是使用相同的函数名称.如果您不喜欢,请将其他名称命名.


ILM*_*tan 5

A)克隆复制构造函数没有很多优点.可能最大的一个是能够创建完全相同动态类型的新对象(假设声明的类型是可克隆的并且具有公共克隆方法).

B)默认克隆创建一个浅拷贝,除非克隆实现改变了,否则它将保持浅拷贝.这可能很困难,尤其是如果您的班级有最终字段

Bozho是对的,克隆可能很难做对.复制构造函数/工厂将满足大多数需求.