【问题标题】:object cloning with out implementing cloneable interface不实现可克隆接口的对象克隆
【发布时间】:2011-11-19 05:56:56
【问题描述】:

要克隆对象,我是否需要实现“可克隆”接口。因为这里我的类是一个 jar 文件(我的意思是 API)。所以我无法编辑课程。我听说所有的类都扩展了基对象类,并且这个对象类实现了可克隆接口。这是否意味着我们可以直接克隆对象而不实现接口。如果是这样,在我的日食中,我没有任何克隆对象的选项。 有没有其他方法可以在不实现可克隆接口的情况下克隆对象。请解释。

【问题讨论】:

  • 你为什么觉得你必须这样做?您最好告诉我们您正在尝试解决的整体问题,而不是您认为可能需要解决此问题的编程步骤。换句话说,你可能做错了事。
  • 答案中的每个 cmets,我认为 OP 想要一个复制构造函数。
  • 你好气垫船。在这里,我清楚地解释了我的问题。我有一个名为 XYZ 的类,我有一个方法返回这个 XYZ 类的对象。私人 XYZ getObject(){ 返回 obj;我通过调用此方法创建了一个对象。 XYZ obj1 = getObject();问题来了。实际上我想再创建一个对象而不再次调用该方法。我不能在这里编辑 XYZ 类。我想做 XYZ obj2 = (XYZ)obj1.clone();但我无法在我的 XYZ 类上实现“可克隆”接口。那么有没有其他方法可以为这个类再创建一个对象。
  • 我想我可以把这个问题改写为,我们知道在 Object Class 中已经定义了 clone() 方法,并且每个对象都是 Object 类的子对象,那么:1.为什么我们还需要实现可克隆界面? 2. 有没有不实现Cloneable接口就可以克隆的对象?

标签: java clone


【解决方案1】:

通常最好的做法是避免使用 clone(),因为它很难正确执行 (http://www.javapractices.com/topic/TopicAction.do?Id=71)。也许有问题的类有一个复制构造函数?

或者,如果它实现了 Serializable 或 Externalizable,您可以通过将其写入字节流并将其读回来进行深度复制

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Object deepCopy = ois.readObject();

(来自http://www.jguru.com/faq/view.jsp?EID=20435)。这既快速又简单,但并不漂亮……我通常认为这是最后的手段。

【讨论】:

  • 嗨,凯尔。感谢您的回复。但我对此并不完全了解。你能告诉我我该怎么做吗?
  • 复制构造函数看起来像:public XYZ(XYZ toCopy)。您可以调用 new XYZ(o),而不是调用 o.clone()。可序列化部分意味着如果 XYZ 可以写入流,那么您可以将其写入流(实际上只是内存中的字节数组),然后从该流中读回,从而有效地创建副本。
  • 复制构造函数的限制是您需要在代码创作时知道类类型。如果我传入XYZ 的子类,public XYZ(toCopy) 将不起作用。
【解决方案2】:

Java Object 类没有实现Cloneable 接口。但是它确实有clone() 方法。但是这个方法是protected,如果在一个没有实现Cloneable接口的对象上调用,会抛出CloneNotSupportedException。因此,如果您无法修改要克隆的类,那么您就不走运了,必须找到另一种方法来复制实例。

需要注意的是,Java 中的克隆系统漏洞百出,通常不再使用。看看这个interview 和 Josh Bloch 从 2002 年开始解释一些问题。

【讨论】:

  • 有没有其他方法可以在不实现可克隆接口的情况下克隆对象...谢谢您的回复
  • "但是需要注意的是,Java 中的克隆系统漏洞百出,一般不再使用。" -- 可以为语句添加参考吗?
  • 您可以使用构造函数创建一个新实例并复制所需的状态。这样做的可行性实际上取决于您要复制的对象类别。
  • 嗨东方。我完全被困在我无法修改我想要克隆的类的地方。你能建议我另一种方法来进行这种克隆吗?
【解决方案3】:

他们是一个克隆对象而不实现可克隆接口的api。

试试这个

https://github.com/kostaskougios/cloning

您还可以在此处找到有关克隆对象的更多详细信息

http://javatechniques.com/blog/faster-deep-copies-of-java-objects/

【讨论】:

  • 您的链接已损坏。
【解决方案4】:

使用反射 API 你可以实现它

【讨论】:

    【解决方案5】:

    尝试在不实现 Cloneable 的类上调用 clone 方法会引发 CloneNotSupported 异常,并且没有 Object 类不实现 Cloneable。

    这里是 Object 类的 clone 方法的 javadoc

    CloneNotSupportedException  if the object's class does not
     *               support the <code>Cloneable</code> interface. Subclasses
     *               that override the <code>clone</code> method can also
     *               throw this exception to indicate that an instance cannot
     *               be cloned.
    

    Object#clone 方法也是受保护的,因此您需要在您的类中实现 clone 方法并将其公开,以便创建类对象的类可以访问它,然后可以调用 clone。一个很好的例子是在 ArrayList 中实现克隆的方式

    ArrayList 实现如下可克隆 公共类 ArrayList 扩展 AbstractList 实现 List、RandomAccess、Cloneable、java.io.Serializable

    然后实现克隆方法:

    /**
     * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The
     * elements themselves are not copied.)
     *
     * @return a clone of this <tt>ArrayList</tt> instance
     */
    public Object clone() {
    try {
        ArrayList<E> v = (ArrayList<E>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError();
    }
    }
    

    【讨论】:

    • 嗨,gaurav,正如我已经提到的,我无法在我的班级上实现可克隆接口。但我想用现有对象的不同名称创建重复对象。有没有其他方法可以做到这一点。
    • 如果您询问是否可以克隆您无法编辑的类的对象。答案是不。从理论上讲,您可以使用反射来访问类的变量,从而能够获取和复制其状态,但如果您被允许访问类的私有成员,则取决于安全管理器。即使在这种情况下,也可能无法进行深度复制。
    • 在这种情况下您可能应该使用复制构造函数而不是clone()。谷歌的第一个链接是这个:javapractices.com/topic/TopicAction.do?Id=12
    【解决方案6】:

    Object 类中的 clone() 方法是受保护的,这意味着所有类都将使用受保护的访问修饰符继承它,因此,如果您尝试在该类之外访问它而不克隆它,您将看不到它,如果发生这种情况,它也会抛出 CloneNotSupportedException您尝试在不实现 Cloneable 接口的情况下调用它。

    如果您正在寻找一种创建克隆行为的方法,您需要在您的类中编写一个新方法,然后创建其中所有字段的副本,这基本上就像创建现有的新副本对象的状态。

    public class TestCloneable {
    private String name = null;
    
    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }
    
    /**
     * @return the name
     */
    public String getName() {
        return name;
    }
    
    
    public TestCloneable createCopy(){
        TestCloneable testCloneable = new TestCloneable();
        testCloneable.setName(this.getName());
        return testCloneable;
    }
    

    }

    【讨论】:

      【解决方案7】:

      实现可克隆接口以克隆对象不是强制性的。 您可以在要克隆其对象的类中编写自己的克隆方法。

      【讨论】:

        【解决方案8】:

        您可以使用 Unsafe 创建对象的实例,然后使用 java 反射将值复制到新实例中。但是就像 Unsafe 这个名字所说的那样,这不是一个很好的解决方案。

        public static Unsafe unsafe;
        static {
            Field f;
            try {
                f = Unsafe.class.getDeclaredField("theUnsafe");
                f.setAccessible(true);
                unsafe = (Unsafe) f.get(null);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        
        }
        
        public static <T> T clone(T object) throws InstantiationException {
            T instance = (T) unsafe.allocateInstance(object.getClass());
            copyInto(object,instance);
            return instance;
        }
        
        public static void copyInto(Object source,Object destination){
            Class<?> clazz = source.getClass();
            while (!clazz.equals(Object.class)) {
                for (Field field : clazz.getDeclaredFields()) {
                    field.setAccessible(true);
                    try {
                        field.set(destination, field.get(source));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
                clazz = clazz.getSuperclass();
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2023-03-15
          • 2016-06-27
          • 2012-05-06
          • 1970-01-01
          • 1970-01-01
          • 2010-12-14
          • 2011-11-07
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多