【问题标题】:What is wrong with this clone()?这个克隆()有什么问题?
【发布时间】:2010-11-06 08:15:42
【问题描述】:

我已经为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;
    }
}

【问题讨论】:

  • +1。因为据我了解,情况实际上有点棘手,而不仅仅是一个简单的 clone() 案例。

标签: java clone


【解决方案1】:

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

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

【讨论】:

  • 是的。想知道您如何表达对 Person 的孩子可克隆的需求 - 或者一般而言:按需复制自己?拥有一个 Cloneable 超类就足够了吗?
  • 所有子类都继承父类的接口。所以人是可克隆的。
【解决方案2】:

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

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

【讨论】:

  • 我没有得到你的答案。你的答案的第 3 行和第 4 行是什么意思??
  • 这人说的对,Object没有clone方法,只有ICloneable的东西有。我会说你需要将 obj 转换为 ICloneable 然后调用 clone 但是你正在使用 new Object 构建 obj,所以实际上不会有 clone()。你认为你的代码到底应该做什么?
  • 我认为 IClonable 是 C#?但是克隆对象而不是它本身仍然是个好点。
  • 我的代码应该参考Object类中的clone()方法来复制所有原始数据类型,而不是自己一个一个地复制。
【解决方案3】:

我认为目前的绿色答案不好,你为什么会问?

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

【讨论】:

    【解决方案4】:

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

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

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

    【讨论】:

    • +1 提供可读的文章,而不仅仅是参考书目。
    【解决方案5】:

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

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

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

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

    Edit2:改写我的答案

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

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

    Edit5:试试这个:

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

    Edit6然后将您的方法命名为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ábor Hargitai for super.clone()

    【讨论】:

    • 是的,我已经实现了 Cloneable 接口。
    • 我已经把它放在 try catch 块中,但它仍然有这个错误。
    • 我不能使用 super.clone() 。这里 super 是 Person 并且 Person 有一个抽象的 clone() !!!如何调用 super.clone()????
    • 按照你推荐的方式,会报错:不能直接调用Person类型的抽象方法clone()
    • 好的,我现在看到了。您遇到了声明存在于 Person 类上方的类层次结构中的抽象方法的“不可能”情况。如果可以,请使用与 clone() 不同的名称。 edit
    【解决方案6】:

    你应该简单地写

    return super.clone(); 
    

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

    【讨论】:

    • 我不能,因为超类是例如具有抽象 clone() 方法的 Person。
    • 这不是问题,因为实际的克隆是由Object类执行的——你只需要实现Cloneable,除非你在Person类上创建了一个抽象的clone()方法。
    • 为什么会有抽象的克隆方法?您可以在 Person 中将 clone() 设为非抽象并执行相同操作。
    • 或者只是从 Preson 中完全删除 clone()。
    • 我想以这样的方式制作它,因为我知道我可以编写这段代码。
    【解决方案7】:

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

    更多信息请看这里:http://java.sun.com/javase/6/docs/api/java/lang/Object.html#clone()

    【讨论】:

    • @dxmio:是的,我实现了Cloneable接口,但也有这个错误,不知道为什么??:((
    【解决方案8】:

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

    帖子摘录:

    另一个缺点是经常 无法访问 clone() 方法 抽象类型。大多数接口和 Java中的抽象类不 指定一个公共 clone() 方法。作为一个 结果,通常是使用 clone() 方法是如果你知道 对象的实际类别;这是 违背抽象原则 使用最通用的类​​型 可能的。例如,如果一个人有一个 Java中的列表引用,不能 在该引用上调用 clone() 因为 List 没有指定 public 克隆()方法。实际实现 像 ArrayList 和 LinkedList 这样的列表 通常都有 clone() 方法 自己,但是很不方便 不好的抽象随身携带 对象的实际类类型。

    【讨论】:

      【解决方案9】:

      基本上,为了拥有一个可正确克隆的对象,在该类中实现一个公共 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 标记。

      【讨论】:

      • 好,但是我一个一个克隆原始类型,我不想要这个。我想一次克隆那些原始数据类型而不使用“newObject.field1 = this.field1;”
      • 这就是克隆机制的工作原理:-)。如果您想要其他东西,您需要自己实现它(使用代码生成 ala asm 和/或反射)。
      • 在克隆中新建一个对象被认为是不好的做法。您应该使用 super.clone() 来获取新的克隆实例。
      猜你喜欢
      • 1970-01-01
      • 2013-04-28
      • 2010-12-18
      • 2011-03-08
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多