我有一个关于Java中的clone()
方法的快速问题,在继承方面用作super.clone()
- 我在父类中调用clone()
方法一直从按钮。
clone()
方法应该返回此对象的副本,但是如果我在继承heirachy中有三个类并且调用super.clone()
三次,为什么不继承中的最高类heirachy,在类Object下,得到该类的副本返回?
假设我们有三个类:A,B和C,其中A - > B - > C(继承= - >)
然后在C类中调用super.clone()
,调用B中的clone()
调用super.clone()
,调用A中的clone()
,此时调用super.clone()
'对象。 clone()被调用'。为什么它不是从this
返回的关于A类的Object.clone()
对象的副本?这听起来很合乎逻辑。
答案 0 :(得分:12)
听起来这里至少有两个问题在起作用:
听起来你对clone()通常如何实现感到困惑。
听起来你认为克隆是一个好主意(与使用复制构造函数,工厂或其等价物相比)。
以下是克隆方法的an example of an implementation:
@Override
public Object clone() throws CloneNotSupportedException {
//get initial bit-by-bit copy, which handles all immutable fields
Fruit result = (Fruit)super.clone();
//mutable fields need to be made independent of this object, for reasons
//similar to those for defensive copies - to prevent unwanted access to
//this object's internal state
result.fBestBeforeDate = new Date( this.fBestBeforeDate.getTime() );
return result;
}
请注意,super.clone()
的结果会立即转换为Fruit
。这允许继承方法然后修改Fruit特定的成员数据(在这种情况下为fBestBeforeDate
)。
因此,对子clone()
方法的调用,虽然它将调用父母的克隆,但也会对新制作的副本添加自己的特定修改。在这种情况下,结果将是Fruit
,而不是Object
。
现在,更重要的是,克隆是一个坏主意。复制构造函数和工厂提供了更直观,易于维护的替代方案。尝试阅读我附加到示例的Java Practices链接上的标题:总结了一些问题。 Josh Bloch也有a much longer discussion:绝对应该避免克隆。以下是关于为什么他认为克隆是一个问题的优秀摘要段落:
对象的克隆方法非常棘手。它基于现场副本,和 这是“超语言”。它创建一个对象而不调用a 构造函数。无法保证它保留不变量 由建设者建立。有很多错误 多年来,无论是在孙内外,都源于如果你这样的事实 只需要反复调用super.clone直到你克隆了一个 对象,你有一个对象的浅表副本。克隆一般 与正在克隆的对象共享状态。如果那个状态是可变的, 你没有两个独立的对象。如果你修改一个,另一个 变化也是如此。突然之间,你会得到随机行为。
答案 1 :(得分:4)
虽然接受了一个答案,但我认为它不能完全回答问题的第一部分(为什么子类中的向下转换始终有效)。 虽然我无法解释它,但我想我可以清理一些与我的相同的海报混乱。 我们有以下课程
class A implements Cloneable
{
@Override
protected A clone() throws CloneNotSupportedException // could be public
{
Object clone = super.clone();
System.out.println("Class A: " + clone.getClass()); // will print 'C'
return (A) clone;
}
}
class B extends A
{
@Override
protected B clone() throws CloneNotSupportedException
{
A clone = super.clone();
System.out.println("Class B: " + clone.getClass()); // will print 'C'
return (B) clone;
}
}
class C extends B
{
@Override
protected C clone() throws CloneNotSupportedException
{
B clone = super.clone();
System.out.println("Class C: " + clone.getClass()); // will print 'C'
return (C) clone;
}
}
static main(char[] argv)
{
C c = new C();
C cloned_c = c.clone();
}
结果是
Class A: C
Class B: C
Class C: C
打印在命令行上。
所以,事实上,clone()
的{{1}}方法可以看起来 down 调用堆栈,并在链的开头看到的哪种类型的对象调用Object
,然后,如果调用冒泡,以便实际调用clone()
,则会创建该类型的对象。所以这已经发生在类Object#clone()
中,这很奇怪,但它解释了为什么向下转换不会导致C
。我已经检查过OpenJDK,看起来这是由一些用本机代码实现的Java黑魔法。
答案 2 :(得分:3)
它是一种特殊的原生方法。这样做是为了使克隆更容易。否则你将不得不复制祖先类的整个代码。
答案 3 :(得分:2)
如果B中的clone()返回A中的任何clone()返回,C中的clone()返回B中返回的clone(),则C中的clone()将返回A中的clone()返回的任何内容。 / p>
答案 4 :(得分:0)
该类具有有关该类的信息以及与另一个类关联的信息。因此,从概念上讲,该类的对象也将具有关联类的信息。这个对象是没有相关对象/父类的不完整对象。需要复制此类中的所有直接字段和间接字段,使其成为当前对象的新克隆是值得的。我们不能仅访问引用的仅表示子部分的那部分。