这个clone()有什么问题?

我编写了这个克隆方法,当Employee类的父类是抽象的,而父类中的clone()方法是abstract.I想用这个代码复制Employee的对象的原始数据类型,而不是复制每个原始数据单独键入,但此代码与我调用clone()方法的行有问题。 (此代码在Employee类中)

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

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

但是我的Employee类在类层次结构中,可以访问Object类中的protected 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; } } 

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

但是,我很少会使用clone来复制对象。 一个这样安全的例子是array.clone()。 我宁愿使用copy-constructor习惯用法或手动复制/赋值。

有效Java (第2版)中有关于背景问题的第11项。 可克隆接口是一种特殊的接口,因为它修改了Object类关于克隆的行为。 基本上它是一个在Java中启用接口的function。

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

Edit2:重述了我的回答

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

编辑4:我认为答案已经在其他post中,只是想反思潜在的问题。

编辑5:试试这个:

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

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

Edit7我做了一些食谱 ,并推出了以下解决方案:

 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áborHargitaisuper.clone()

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

  1. 实现Cloneable
  2. 重写clone()方法并将其公开
  3. clone()调用super.clone()然后复制任何可变对象的状态

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

例如:

 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; } } 

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

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

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

  • 它增加了很多代码
  • 它要求您列出要复制的所有字段并执行此操作
  • 当使用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/查看

Effective Java的Bloch先生对clone的使用有一些有趣的话。

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

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

你应该简单地写

 return super.clone(); 

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

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

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

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

post的摘录:

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

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

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

为了正确实现类的clone方法,你应该声明一个像this()这样的公共方法克隆。

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

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

 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标记。