Object.clone() 的逐个字段复制是啥?
Posted
技术标签:
【中文标题】Object.clone() 的逐个字段复制是啥?【英文标题】:What is this field-by-field copy done by Object.clone()?Object.clone() 的逐个字段复制是什么? 【发布时间】:2011-02-22 20:05:42 【问题描述】:在 Effective Java 中,作者指出:
如果一个类实现了 Cloneable, 对象的克隆方法返回一个 对象的逐个字段副本; 否则它会抛出 CloneNotSupportedException。
我想知道的是他对逐字段复制的含义。这是否意味着如果该类在内存中有 X 个字节,它只会复制那块内存?如果是,那么我可以假设原始类的所有值类型都将被复制到新对象中吗?
class Point implements Cloneable
private int x;
private int y;
@Override
public Point clone()
return (Point)super.clone();
如果Object.clone()
所做的是逐个字段复制Point
类,我会说我不需要显式复制字段x
和y
,即上面显示的代码足以克隆Point
类。也就是说,下面这段代码是多余的:
@Override
public Point clone()
Point newObj = (Point)super.clone();
newObj.x = this.x; //redundant
newObj.y = this.y; //redundant
我说的对吗?
我知道克隆对象的引用会自动指向原始对象的引用指向的位置,我只是不确定值类型具体会发生什么。如果有人能清楚地说明Object.clone()
的算法规范是什么(用简单的语言),那就太好了。
【问题讨论】:
【参考方案1】:是的,逐个字段复制确实意味着当它创建新的(克隆的)对象时,JVM 会将每个字段的值从原始对象复制到克隆对象中。不幸的是,这确实意味着你有一个浅拷贝。如果你想要一个深拷贝,你可以重写 clone 方法。
class Line implements Cloneable
private Point start;
private Point end;
public Line()
//Careful: This will not happen for the cloned object
SomeGlobalRegistry.register(this);
@Override
public Line clone()
//calling super.clone is going to create a shallow copy.
//If we want a deep copy, we must clone or instantiate
//the fields ourselves
Line line = (Line)super.clone();
//assuming Point is cloneable. Otherwise we will
//have to instantiate and populate it's fields manually
line.start = this.start.clone();
line.end = this.end.clone;
return line;
关于克隆的另一件重要的事情是,克隆对象的构造函数永远不会被调用(只复制字段)。因此,如果构造函数初始化了一个外部对象,或者将该对象注册到某个注册表中,那么克隆对象就不会发生这种情况。
我个人更喜欢不使用 Java 的克隆。相反,我通常创建自己的“复制”方法。
【讨论】:
为什么不用Java的克隆,自己写方法呢?有什么具体原因吗? 阅读有效的 Java。有很多。 @zengr 因为 clone() 不会创建深拷贝,所以我必须自己实现 clone 方法的主体,还要承担 Cloneable 和 CloneNotSupportedException 的负担。除此之外,还有一些其他问题,为什么我选择创建自己的方法。 《Effective Java》中对它们进行了很好的解释 原语为我们完成了吗?还是我们也需要显式复制它们?【参考方案2】:这意味着一个浅拷贝——字段被复制,但如果你有任何引用,这些指向的内容不会被复制——你将有两个对同一个对象的引用,一个在旧对象中,一个在旧对象中新的,克隆的对象。但是,对于具有原始类型的字段,该字段就是数据本身,因此无论如何都会被复制。
【讨论】:
正确,但如果clone
方法没有像 super.clone()
那样实现,它可能不会实现到合约中。
啊,是的,这当然是真的。我注意到在示例中他扩展了 Cloneable,这似乎不正确——您将实现它然后覆盖 Cloneable 方法(也许扩展 Cloneable 是它在 Java 1 中的工作方式?)。我想实现这个是说“嘿,我有这种特殊的可克隆性质,我正确克隆并在我的克隆方法中进行手动深度复制”。但是,除非您覆盖,否则正如您所说,来自对象的 clone() 会执行浅拷贝。【参考方案3】:
newObj.x = this.x; //redundant
newObj.y = this.y; //redundant
没错——这些都是多余的,因为它们已经被 Object 的 clone() 方法复制了。
将其视为数据副本是正确的。原始类型被复制,引用也被复制,因此它们指向同一个对象。例如,
class A implements Cloneable
Object someObject;
A a = new A();
a.someObject = new Object();
A cloneA = (A)a.clone();
assert a.someObject==cloneA.someObject;
【讨论】:
【参考方案4】:默认克隆执行值的浅拷贝。对于原始值,这就足够了,不需要额外的工作。
对于对象,浅拷贝意味着只拷贝引用。因此,在这些情况下,通常需要深拷贝。例外情况是引用指向不可变对象时。不可变对象不能改变其明显的状态,因此可以安全地复制它们的引用。例如,这适用于 String、Integer、Float、枚举(如果没有被错误地设置为可变)。
【讨论】:
以上是关于Object.clone() 的逐个字段复制是啥?的主要内容,如果未能解决你的问题,请参考以下文章
请问java中深度copy一个二维数组是啥意思?怎么用代码实现?