Cloneable在Java中如何工作?我如何使用它? 1 周,4 日 Questions & Answers 76558 我想知道以下几点: Cloneable意味着我们可以通过 实现Cloneable接口。它的优点和优点是什么 这样做的缺点是什么李> 如果对象是对象,递归克隆是如何发生的 复合对象李>
# 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 楼答案 A)克隆与复制构造函数相比没有太多优势。最大的可能是创建完全相同动态类型的新对象的能力(假设声明的类型是可克隆的,并且有一个公共克隆方法) B)默认克隆会创建一个浅层副本,除非您的克隆实现改变了这一点,否则它将保持浅层副本。这可能会很困难,尤其是如果你的班级有最终的领域 波佐是对的,克隆人很难做到正确。复制建造商/工厂将满足大多数需求
# 3 楼答案 不幸的是,Cloneable本身只是一个标记接口,也就是说:它没有定义clone()方法 它所做的就是改变受保护对象的行为。clone()方法,该方法将对未实现Cloneable的类抛出CloneNotSupportedException,并对实现Cloneable的类执行成员级浅复制 即使这是您正在寻找的行为,您仍然需要实现自己的clone()方法才能将其公开 在实现自己的clone()时,我们的想法是从super创建的对象开始。clone(),它保证是正确的类,然后执行任何额外的字段填充,以防浅拷贝不是您想要的。从clone()调用构造函数会有问题,因为如果子类想要添加自己的额外可克隆逻辑,这会破坏继承;如果它叫超级。clone()在这种情况下,它将获取错误类的对象 不过,这种方法绕过了构造函数中可能定义的任何逻辑,这可能会有问题 另一个问题是,任何忘记覆盖clone()的子类都会自动继承默认的浅层副本,这可能不是在可变状态下所需的(现在将在源和副本之间共享) 出于这些原因,大多数开发人员不使用Cloneable,而是简单地实现了一个复制构造函数 有关Cloneable的更多信息和潜在缺陷,我强烈推荐Joshua Bloch的《高效Java》
# 4 楼答案 克隆调用了一种非语言的方式来构造对象——不需要构造函数李> 克隆需要您以某种方式处理CloneNotSupportedException,或者麻烦客户机代码来处理它李> 好处很小——您不必手动编写复制构造函数李> 所以,明智地使用Cloneable。与你为做好每件事所需的努力相比,它并没有给你带来足够的好处
# 5 楼答案 可克隆的缺点是什么 如果你正在复制的对象有构图,克隆是非常危险的。在这种情况下,您需要考虑以下可能的副作用,因为克隆会创建浅拷贝: 假设您有一个对象来处理与db相关的操作。例如,该对象将Connection对象作为属性之一 因此,当有人创建originalObject的克隆时,正在创建的对象,比如说,cloneObject。 这里originalObject和cloneObject对Connection对象具有相同的引用 假设originalObject关闭Connection对象,那么现在cloneObject将不起作用,因为connection对象在他们之间是共享的,并且它实际上是由^{关闭的 如果您想克隆一个以IOStream为属性的对象,可能会出现类似的问题 如果对象是复合对象,递归克隆是如何发生的 Cloneable执行浅拷贝。意思是原始对象和克隆对象的数据将指向相同的引用/内存。 与深度复制相反,原始对象内存中的数据被复制到克隆对象内存中
# 6 楼答案 关于Cloneable你应该知道的第一件事是——不要使用它 用Cloneable正确的方法实现克隆非常困难,而且这种努力不值得 而不是使用其他一些选项,比如apachecommons ^{}(深度克隆)或^{}(浅层克隆),或者只使用复制构造函数 See here了解Josh Bloch关于Cloneable克隆的观点,这解释了该方法的许多缺点。(Joshua Bloch是Sun的员工,并领导了许多Java特性的开发。)
# 1 楼答案
克隆是一种基本的编程模式。Java在许多方面可能实现得很差,但这一事实并没有减少克隆的必要性。而且,很容易实现克隆,无论你希望它如何工作,无论是浅的、深的、混合的,无论什么。你甚至可以使用克隆这个名字来实现这个功能,如果你喜欢的话,也可以不实现克隆
假设我有A、B和C类,其中B和C是从A派生的。如果我有一个A类型的对象列表,如下所示:
现在,该列表可以包含A、B或C类型的对象。您不知道这些对象是什么类型的。所以,你不能像这样复制列表:
如果对象实际上是类型B或C,则无法获得正确的副本。如果A是抽象的呢?现在,有人建议:
这是一个非常非常糟糕的主意。如果添加一个新的派生类型呢?如果B或C在另一个包中,而您在此类中没有访问权限,该怎么办
你想做的是:
很多人已经指出了为什么克隆的基本Java实现存在问题。但是,通过这种方式很容易克服:
在A级:
B班:
C班:
我没有实现Cloneable,只是使用相同的函数名。如果你不喜欢,那就换个名字吧
# 2 楼答案
A)克隆与复制构造函数相比没有太多优势。最大的可能是创建完全相同动态类型的新对象的能力(假设声明的类型是可克隆的,并且有一个公共克隆方法)
B)默认克隆会创建一个浅层副本,除非您的克隆实现改变了这一点,否则它将保持浅层副本。这可能会很困难,尤其是如果你的班级有最终的领域
波佐是对的,克隆人很难做到正确。复制建造商/工厂将满足大多数需求
# 3 楼答案
不幸的是,Cloneable本身只是一个标记接口,也就是说:它没有定义clone()方法
它所做的就是改变受保护对象的行为。clone()方法,该方法将对未实现Cloneable的类抛出CloneNotSupportedException,并对实现Cloneable的类执行成员级浅复制
即使这是您正在寻找的行为,您仍然需要实现自己的clone()方法才能将其公开
在实现自己的clone()时,我们的想法是从super创建的对象开始。clone(),它保证是正确的类,然后执行任何额外的字段填充,以防浅拷贝不是您想要的。从clone()调用构造函数会有问题,因为如果子类想要添加自己的额外可克隆逻辑,这会破坏继承;如果它叫超级。clone()在这种情况下,它将获取错误类的对象
不过,这种方法绕过了构造函数中可能定义的任何逻辑,这可能会有问题
另一个问题是,任何忘记覆盖clone()的子类都会自动继承默认的浅层副本,这可能不是在可变状态下所需的(现在将在源和副本之间共享)
出于这些原因,大多数开发人员不使用Cloneable,而是简单地实现了一个复制构造函数
有关Cloneable的更多信息和潜在缺陷,我强烈推荐Joshua Bloch的《高效Java》
# 4 楼答案
所以,明智地使用Cloneable。与你为做好每件事所需的努力相比,它并没有给你带来足够的好处
# 5 楼答案
可克隆的缺点是什么
如果你正在复制的对象有构图,克隆是非常危险的。在这种情况下,您需要考虑以下可能的副作用,因为克隆会创建浅拷贝:
假设您有一个对象来处理与db相关的操作。例如,该对象将
Connection
对象作为属性之一因此,当有人创建
originalObject
的克隆时,正在创建的对象,比如说,cloneObject
。 这里originalObject
和cloneObject
对Connection
对象具有相同的引用假设关闭的
originalObject
关闭Connection
对象,那么现在cloneObject
将不起作用,因为connection
对象在他们之间是共享的,并且它实际上是由^{如果您想克隆一个以IOStream为属性的对象,可能会出现类似的问题
如果对象是复合对象,递归克隆是如何发生的
Cloneable执行浅拷贝。意思是原始对象和克隆对象的数据将指向相同的引用/内存。 与深度复制相反,原始对象内存中的数据被复制到克隆对象内存中
# 6 楼答案
关于
Cloneable
你应该知道的第一件事是——不要使用它用
Cloneable
正确的方法实现克隆非常困难,而且这种努力不值得而不是使用其他一些选项,比如apachecommons ^{} (深度克隆)或^{} (浅层克隆),或者只使用复制构造函数
See here了解Josh Bloch关于
Cloneable
克隆的观点,这解释了该方法的许多缺点。(Joshua Bloch是Sun的员工,并领导了许多Java特性的开发。)