题
我正在寻找一些有关Java的教程 Cloneable
, ,但没有任何良好的链接,无论如何,堆栈溢出变得越来越明显。
我想知道以下内容:
Cloneable
意味着我们可以通过实现该物体的克隆或对象的副本Cloneable
界面。这样做的优点和缺点是什么?- 如果对象是复合对象,则如何发生递归克隆?
解决方案
您应该知道的第一件事 Cloneable
是 - 不要使用它。
很难实现克隆 Cloneable
是的,努力是不值得的。
而不是使用其他选项,例如Apache-Commons SerializationUtils
(深欺骗)或 BeanUtils
(浅词),或简单地使用复制构建器。
看这里 乔什·布洛克(Josh Bloch)关于克隆的观点 Cloneable
, ,这解释了该方法的许多缺点。 ((约书亚·布洛克(Joshua Bloch) 是一名太阳员工,并领导了许多Java功能的发展。)
其他提示
不幸的是,可克隆本身只是一个标记界面,也就是说:它不能定义clone()方法。
做什么,是更改受保护对象的行为。clone()方法,该方法将为无法实现可克隆的类的cloneNotsupportedException抛出一个clonotsupportedexception,并对会成员进行成员的浅副本。
即使这是您要寻找的行为,您仍然需要实现自己的clone()方法才能公开它。
当实现自己的clone()时,想法是从super.clone()的对象开始,该对象保证为正确的类你要。从clone()调用构造函数将是有问题的,因为如果子类希望添加自己的附加可克隆逻辑,这将破坏继承;如果要调用super.clone(),在这种情况下,将会有一个错误类的对象。
这种方法绕过了您的构造函数中可能定义的任何逻辑,这可能是有问题的。
另一个问题是,任何忘记覆盖clone()的子类都会自动继承默认浅副本,这可能不是您想要的,如果您在可变状态下(现在将在源和复制之间共享)。
大多数开发人员出于这些原因不使用可包隆,而是简单地实现复制构造函数。
有关更多信息和潜在的可讨化陷阱,我强烈推荐Joshua Bloch的《有效Java》一书
- 克隆调用了一种语言的构造对象的方式 - 没有构造函数。
- 克隆要求您以克洛诺底皮PpportedException的方式治疗或打扰客户代码以治疗它。
- 好处很小 - 您只是不必手动编写复制构造函数。
因此,明智地使用可克隆。与您需要申请做正确的一切所需的努力相比,它并不能为您带来足够的好处。
克隆是基本的编程范式。 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):
}
我不使用相同的函数名称实现可克隆。如果您不喜欢那样,请将其命名。
a)克隆比复制构造函数没有很多优点。最大的一个可能是能够创建完全相同的动态类型的新对象(假设声明的类型是可克隆的并且具有公共克隆方法)。
b)默认克隆会创建浅副本,除非您的克隆实施更改,否则它将保持浅副本。这可能很困难,尤其是如果您的班级有最终领域
Bozho是对的,克隆可能很难正确。复制构造函数/工厂将满足大多数需求。
可克隆的缺点是什么?
如果您要复制的对象有构图,克隆非常危险。在这种情况下,您需要考虑以下可能的副作用,因为克隆会创建浅副本:
假设您有一个可以处理与数据库相关的操作的对象。说,那个对象有 Connection
对象是其中之一。
因此,当某人创建克隆时 originalObject
, ,正在创建的对象,可以说, cloneObject
。在这里 originalObject
和 cloneObject
持有相同的参考 Connection
目的。
说 originalObject
关闭 Connection
对象,所以现在 cloneObject
不会起作用,因为 connection
它们之间共享对象,并且它是由 originalObject
.
如果假设要克隆具有iostream作为属性的对象,则可能会发生类似的问题。
如果对象是复合对象,则如何发生递归克隆?
可克隆的性能浅副本。意思是原始对象和克隆对象的数据将指向相同的参考/内存。相反,在深拷贝的情况下,将原始对象内存的数据复制到克隆对象的内存。