7

我已经为Employee类的父类是抽象的并且父类中的clone()方法是抽象的情况编写了这个克隆方法。我想用这段代码复制Employee对象的原始数据类型,而不是复制每个原始数据单独输入,但是这段代码与我调用 clone() 方法的行有问题。(此代码在 Employee 类中)

public Object clone() {
    Object obj = new Object();
    Object object = obj.clone();  //Emphasis here
    return object;

}

错误是:来自 Object 类型的方法 clone() 不可见。

但是我的 Employee 类在类层次结构中,它可以访问 Object 类中受保护的 clone() 方法。

这是我的简单 Employee 类:

public class Employee extends Person implements Cloneable {
private int ID;

public Employee() {
    ID = 0;
}

public void setID(int ID) {
    this.ID = ID;
}

public int getID() {
    return ID;
}

public Object clone1() throws CloneNotSupportedException {
    try {
        Object obj = new Object();

        Object object = obj.clone();
        return object;
    } catch (CloneNotSupportedException ex) {
        return null;
    }
}
4

9 回答 9

11

使类可克隆的标准模式是:

  1. 实施Cloneable
  2. 覆盖该clone()方法并使其公开
  3. clone()调用中super.clone()然后复制任何可变对象的状态

您不应该使用new. 正确的方法是调用super.clone()一个新实例。Object'sclone()是特殊的,它将创建对象的新副本并复制其原始字段和引用。

例如:

public class Person implements Cloneable {
    protected String name;
    // Note that overridden clone is public
    public Object clone() {
        Person clone = (Person)super.clone();
        // No need to copy name as the reference will be
        // copied by Object's clone and String is immutable
        return clone;
    }
}

public class Employee extends Person {
    protected int id;
    protected java.awt.Point location;
    public Object clone() {
        Employee  clone = (Employee )super.clone();
        // No need to copy id as Object's clone has already copied it
        // Need to clone location as Point is mutable and could change
        clone.location = location.clone();
        return clone;
    }
}
于 2009-06-27T17:17:05.700 回答
6

Java 的克隆机制有些尴尬。为了能够克隆自己,该类必须做两件事。首先它必须实现 Clonable。其次,它必须覆盖 clone() 并将其公开。

在您的示例中,您覆盖了 clone() 但您不是在 Employee 类上调用 clone() ,而是在仅保护 clone() 的 Object.class() 上调用。

于 2009-06-27T08:09:05.333 回答
5

我认为当前的绿色答案很糟糕,您为什么会问?

  • 它添加了很多代码
  • 它要求您列出要复制的所有字段并执行此操作
  • 使用 clone() 时,这对列表不起作用(这就是 HashMap 的 clone() 所说的:返回此 HashMap 实例的浅表副本:键和值本身没有被克隆。)所以你最终手动完成(这使得我哭了)

哦,顺便说一句,序列化也很糟糕,你可能不得不到处添加 Serializable (这也让我哭了)。

那么解决方案是什么:

Java 深度克隆库 克隆库是一个小型的开源(apache 许可证)Java 库,它可以深度克隆对象。对象不必实现 Cloneable 接口。实际上,这个库可以克隆任何 java 对象。如果您不希望修改缓存的对象或想要创建对象的深层副本,则可以在缓存实现中使用它。

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

在http://code.google.com/p/cloning/上查看

于 2009-08-07T05:55:17.497 回答
4

Effective Java 的 Bloch 先生对clone.

http://www.artima.com/intv/bloch13.html

通常,当前的想法是避免使用克隆,因为它容易出错并且被严重误解,实现复制构造函数是一种替代策略,或者使用使用序列化深度复制对象的蛮力方法。

于 2009-06-27T08:19:14.923 回答
2

您是否在对象上实现了 Cloneable 接口?

但是,很少有我会使用克隆来复制对象的情况。一个这样的安全示例是 array.clone()。我宁愿使用复制构造函数或手动复制/分配值。

Effective Java (2nd edition) 中有关于背景问题的 Item#11 。Cloneable 接口是一种特殊的接口,因为它修改了Object类的克隆行为。基本上,它是 Java 中的一个功能启用接口。

编辑:根据您的示例,在一般情况下,您可能需要将 clone() 调用包装在 CloneNotSupportedException 的 try-catch 中。

Edit2:改写我的答案

Edit3:您是否在上下文中覆盖了 clone() public?在您给您的示例中,您尝试克隆一个对象,该对象位于 java.lang 包中 - 几乎不是您的代码所在的包。

Edit4:我认为答案已经在其他帖子中,只是想反思一下潜在的问题。

编辑5:试试这个:

public Object clone1() throws CloneNotSupportedException {        
    return super.clone();        
}

Edit6然后命名您的方法public abstract Object copy(),例如在实现中,使用 super.clone() - 以避免混淆。

Edit7我做了一些 eclipsing 并提出了以下解决方案:

public class Cloner {
    public static abstract class Person {
       protected abstract Object clone1() throws CloneNotSupportedException;
       public Object copy() throws CloneNotSupportedException {
           return clone1();
       }
    }
    public static class Employee extends Person implements Cloneable {
        @Override
        protected Object clone1() throws CloneNotSupportedException {
            return super.clone();
        }
    
    }
    public static void main(String[] args) throws Exception {
        new Employee().copy();
    }
}

但基本上它与将抽象方法重命名为 clone() 之外的其他东西是相同的概念。

Edit8:修复了我的示例,现在它可以正常工作了。

(但实际功劳归于 Gábor Hargitaisuper.clone()

于 2009-06-27T07:59:05.213 回答
2

你应该简单地写

return super.clone(); 

在您的克隆方法中并实现 Clonable 接口。

于 2009-06-27T08:11:02.070 回答
0

您是否实现了适当的接口?它可能就这么简单,尽管您可能已经考虑到了这一点。

有关更多信息,请参见此处:http: //java.sun.com/javase/6/docs/api/java/lang/Object.html#clone()

于 2009-06-27T07:58:51.470 回答
0

我对 Java 不是很熟悉,但这可能会有所帮助: http://en.wikipedia.org/wiki/Clone_(Java_method)

帖子摘录:

另一个缺点是通常无法访问抽象类型上的 clone() 方法。Java 中的大多数接口和抽象类都没有指定公共 clone() 方法。因此,通常使用 clone() 方法的唯一方法是您知道对象的实际类;这与使用尽可能通用类型的抽象原则背道而驰。例如,如果在 Java 中有一个 List 引用,则不能对该引用调用 clone(),因为 List 没有指定公共 clone() 方法。像 ArrayList 和 LinkedList 这样的 List 的实际实现通常都有自己的 clone() 方法,但是携带对象的实际类类型是不方便和糟糕的抽象。

于 2009-06-27T08:08:27.503 回答
-2

基本上,为了拥有一个可正确克隆的对象,就足以在该类中实现一个公共 clone() 方法。

Cloneable 接口是一个marker接口,用于向 VM 发出信号,表明可以安全地实现默认受保护的 clone() 方法作为逐个字段的副本。

为了正确实现类的克隆方法,您应该声明一个公共方法克隆,如 this()。

public Object clone() {
   return super.clone();
}

一个好的工作实现k将创建一个新对象并根据业务逻辑需要正确分配字段:

public Object clone() {
   CurrentClass newObject = new CurrentClass();

   newObject.field1 = this.field1; // for simple types: int, long, etc
   newObject.referenceField = this.referenceField.clone(); // for agregate objects or references.
   return newObject;
}

结论:声明一个公共克隆方法。如果您希望将默认实现作为字段复制调用 super 并将类标记为 Cloneable 如果您只想自定义克隆,则可以忽略 Cloneable 标记。

于 2009-06-27T08:49:32.820 回答