因为Java方法中对象参数的传递是句柄(引用)传递,所以方法中对句柄的改动会影响到原对象。
很少调用一个方法来处理它的参数,在实际编程中也要避免改变参数的情况。但如果我们需要在一个方法的调用期间修改一个参数,且不打算修改外部实体,就应该在自己方法的内部制作一个那个参数的副本来保护那个参数。
制作对象副本需要使用clone()方法。这个方法在基础类Object 中定义成“protected”模式。所以在希望克隆的任何衍生类中,必须将其覆盖为“public”模式。例如,标准库类Vector 覆盖了 clone(),所以能为 Vector调用clone()。
class Int {
private int i;
public Int(int ii) { i = ii; }
public void increment() { i++; }
public String toString() {
return Integer.toString(i);
}
}
public class Cloning {
public static void main(String[] args) {
Vector v = new Vector(); //创建Vector
for(int i = 0; i < 10; i++ )
v.addElement(new Int(i)); //添加对象
System.out.println("v: " + v); //第一次输出
Vector v2 = (Vector)v.clone(); //创建Vector的复制
for(Enumeration e = v2.elements();
e.hasMoreElements(); ) //对复制的Vector中的对象进行+1操作
((Int)e.nextElement()).increment();
System.out.println("v: " + v); //第二次输出
}
}
上面代码涉及到“深拷贝“和”浅拷贝“的问题。这个例子中由于别名问题,原Vector和拷贝的Vector都包含了相同的对象,即两个Vector是完全独立的,但其中容纳的东西相同,这就是”浅拷贝“。”浅拷贝“只拷贝本对象,该对象内部指向的其他对象,以及”其他对象“又指向的另外的对象都不拷贝。
若要“深拷贝”,必须在覆盖的clone()里采取附加的操作。 通常在调用 super.clone()后,为对象内每个句柄都明确调用一个 clone();否则那些句柄会别名变成原始对象的句柄。当然,必须保证对象内的每个句柄自身也都实现了“深拷贝”。
使类具有克隆能力:
因为Object类将clone()定义为”protected", 所以衍生类不做点什么的话,是无法具有克隆能力的(很简单,对象无法调用本类的protected方法)。
1、覆盖clone()方法为public.
想要类可以克隆,记住两点:
注意:因为Java不可能在衍生之后反而缩小方法的访问范围,所以一旦对象变得可以克隆,从它衍生的任何东西都是可以克隆的。(下面会有一种特殊机制关闭克隆能力)
2、实现Cloneable接口
Cloneable接口是空的,我们不需要为它实现任何方法。它的存在有两个原因:
所以在一般情况下,我们必须将“implement Cloneable”作为对克隆能力提供支持的一部分。
经过上面两步,就可以创建自己的可克隆的类:
class MyObject implements Cloneable {
int i;
MyObject(int ii) { i = ii; }
public Object clone() {
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("MyObject can't clone");
}
return o;
}
public String toString() { return Integer.toString(i); }
}
消除克隆能力: