该字段由Object.clone()完成了什么?
题
在有效的Java中,作者指出:
如果类实现可克隆,则对象的克隆方法将返回对象的视野副本;否则,它会引发clonotsupportedException。
我想知道的是他对视野副本的含义。这是否意味着,如果类中有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()
的算法规范是(简单的语言),这是很棒的。
解决方案
是的,字段复制确实意味着当它创建新的(克隆)对象时,JVM会将每个字段的值从原始对象复制到克隆对象。不幸的是,这确实意味着您有浅副本。如果您想要深层副本,则可以覆盖克隆方法。
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的克隆。相反,我通常会创建自己的“重复”方法。
其他提示
这意味着浅副本 - 字段已复制,但是如果您有任何参考,则没有复制这些指示 - 您将有两个引用对同一对象的引用,一个在旧对象中,一个在新的对象中目的。但是,对于具有原始类型的字段,该字段是数据本身,因此无论如何它们都会被复制。
newObj.x = this.x; //redundant
newObj.y = this.y; //redundant
没错 - 这些是多余的,因为它们已经通过对象的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;
默认克隆执行值的浅副本。对于原始值,这足够,不需要额外的工作。
对于对象,浅副本意味着仅复制引用。因此,在这些情况下,通常需要深层副本。例外是当引用指向不变的对象时。不变的物体不会改变其明显的状态,因此可以安全地复制其参考。例如,这适用于字符串,整数,浮点,枚举(如果不能错误地变形)。