有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Cloneable在Java中如何工作?我如何使用它?

我想知道以下几点:

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

共 (6) 个答案

  1. # 1 楼答案

    克隆是一种基本的编程模式。Java在许多方面可能实现得很差,但这一事实并没有减少克隆的必要性。而且,很容易实现克隆,无论你希望它如何工作,无论是浅的、深的、混合的,无论什么。你甚至可以使用克隆这个名字来实现这个功能,如果你喜欢的话,也可以不实现克隆

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

    ArrayList<A> list1;
    

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

    ArrayList<A> list2 = new ArrayList<A>();
    for(A a : list1) {
        list2.add(new A(a));
    }
    

    如果对象实际上是类型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));
        }
    }
    

    这是一个非常非常糟糕的主意。如果添加一个新的派生类型呢?如果B或C在另一个包中,而您在此类中没有访问权限,该怎么办

    你想做的是:

    ArrayList<A> list2 = new ArrayList<A>();
    for(A a : list1) {
        list2.add(a.clone());
    }
    

    很多人已经指出了为什么克隆的基本Java实现存在问题。但是,通过这种方式很容易克服:

    在A级:

    public A clone() {
        return new A(this);
    }
    

    B班:

    @Override
    public B clone() {
        return new B(this);
    }
    

    C班:

    @Override
    public C clone() {
        return new C(this):
    }
    

    我没有实现Cloneable,只是使用相同的函数名。如果你不喜欢,那就换个名字吧

  2. # 2 楼答案

    A)克隆与复制构造函数相比没有太多优势。最大的可能是创建完全相同动态类型的新对象的能力(假设声明的类型是可克隆的,并且有一个公共克隆方法)

    B)默认克隆会创建一个浅层副本,除非您的克隆实现改变了这一点,否则它将保持浅层副本。这可能会很困难,尤其是如果你的班级有最终的领域

    波佐是对的,克隆人很难做到正确。复制建造商/工厂将满足大多数需求

  3. # 3 楼答案

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

    它所做的就是改变受保护对象的行为。clone()方法,该方法将对未实现Cloneable的类抛出CloneNotSupportedException,并对实现Cloneable的类执行成员级浅复制

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

    在实现自己的clone()时,我们的想法是从super创建的对象开始。clone(),它保证是正确的类,然后执行任何额外的字段填充,以防浅拷贝不是您想要的。从clone()调用构造函数会有问题,因为如果子类想要添加自己的额外可克隆逻辑,这会破坏继承;如果它叫超级。clone()在这种情况下,它将获取错误类的对象

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

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

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

    有关Cloneable的更多信息和潜在缺陷,我强烈推荐Joshua Bloch的《高效Java》

  4. # 4 楼答案

    1. 克隆调用了一种非语言的方式来构造对象——不需要构造函数
    2. 克隆需要您以某种方式处理CloneNotSupportedException,或者麻烦客户机代码来处理它
    3. 好处很小——您不必手动编写复制构造函数

    所以,明智地使用Cloneable。与你为做好每件事所需的努力相比,它并没有给你带来足够的好处

  5. # 5 楼答案

    可克隆的缺点是什么

    如果你正在复制的对象有构图,克隆是非常危险的。在这种情况下,您需要考虑以下可能的副作用,因为克隆会创建浅拷贝:

    假设您有一个对象来处理与db相关的操作。例如,该对象将Connection对象作为属性之一

    因此,当有人创建originalObject的克隆时,正在创建的对象,比如说,cloneObject。 这里originalObjectcloneObjectConnection对象具有相同的引用

    假设originalObject关闭Connection对象,那么现在cloneObject将不起作用,因为connection对象在他们之间是共享的,并且它实际上是由^{关闭的

    如果您想克隆一个以IOStream为属性的对象,可能会出现类似的问题

    如果对象是复合对象,递归克隆是如何发生的

    Cloneable执行浅拷贝。意思是原始对象和克隆对象的数据将指向相同的引用/内存。 与深度复制相反,原始对象内存中的数据被复制到克隆对象内存中

  6. # 6 楼答案

    关于Cloneable你应该知道的第一件事是——不要使用它

    Cloneable正确的方法实现克隆非常困难,而且这种努力不值得

    而不是使用其他一些选项,比如apachecommons ^{}(深度克隆)或^{}(浅层克隆),或者只使用复制构造函数

    See here了解Josh Bloch关于Cloneable克隆的观点,这解释了该方法的许多缺点。(Joshua Bloch是Sun的员工,并领导了许多Java特性的开发。)