java 的基类Object中定义了clone()(克隆)方法,但该方法并不是每个类都能调用,虽然该方法在Object中定义为protected作用域。
Object中方法:
protected native Object clone() throws CloneNotSupportedException;
想要使用clone()方法,必须Cloneable接口,且重写clone()方法。
例如:
class MyObject implements Cloneable {
int i;
MyObject(int ii) {i = ii;}
protected 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);
}
}
public class LocalCopy {
static MyObject g(MyObject v) {
v.i++;
return v;
}
static MyObject f(MyObject v) {
v = (MyObject)v.clone();
v.i++;
return v;
}
public static void main(String[] args) {
MyObject a = new MyObject(11);
MyObject b = g(a);
if (a == b)
System.out.println("a == b");
else
System.out.println("a != b");
System.out.println("a = " + a);
System.out.println("b = " + b);
MyObject c = new MyObject(47);
MyObject d = f(c);
if (c == d)
System.out.println("c == d");
else
System.out.println("c != d");
System.out.println("c = " + c);
System.out.println("d = " + d);
}
}
输出结果为:
a == b
a = 12
b = 12
c != d
c = 47
d = 48
在LocalCopy 中,两个方法g()和f()揭示出两种参数传递方法间的差异。其中,g()演示的是按引用传递,
它会修改外部对象,并返回对那个外部对象的一个引用。而f()是对自变量进行克隆,所以将其分离出来,
并让原来的对象保持独立。随后,它继续做它希望的事情。甚至能返回指向这个新对象的一个句柄,而且不
会对原来的对象产生任何副作用。注意下面这个多少有些古怪的语句:
v = (MyObject)v.clone();
它的作用正是创建一个本地副本。为避免被这样的一个语句搞混淆,记住这种相当奇怪的编码形式在Java 中
是完全允许的,因为有一个名字的所有东西实际都是一个句柄。所以句柄v 用于克隆一个它所指向的副本,
而且最终返回指向基础类型Object 的一个句柄(因为它在Object.clone()中是那样被定义的),随后必须
将其造型为正确的类型。
clone()该方法为浅克隆,浅层的复制
例如:
public class Snake implements Cloneable{
private Snake next;
private char c;
Snake(int i, char x) {
c = x;
if (--i > 0)
next = new Snake(i, (char)(x + 1));
}
void increment() {
c++;
if (next != null)
next.increment();
}
public String toString() {
String s = ":" + c;
if (next != null)
s +=next.toString();
return s;
}
public Object clone() {
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
public static void main(String args[]) {
Snake s = new Snake(5, 'a');
System.out.println("s = " + s);
Snake s2 = (Snake)s.clone();
System.out.println("s2 = " + s2);
s.increment();
System.out.println("after s.increment, s2 = " + s2);
}
}
一条Snake(蛇)由数段构成,每一段的类型都是Snake。所以,这是一个一段段链接起来的列表。所有段都
是以循环方式创建的,每做好一段,都会使第一个构建器参数的值递减,直至最终为零。而为给每段赋予一
个独一无二的标记,第二个参数(一个Char)的值在每次循环构建器调用时都会递增。
increment()方法的作用是循环递增每个标记,使我们能看到发生的变化;而toString 则循环打印出每个标
记。输出如下:
s = :a:b:c:d:e
s2 = :a:b:c:d:e
after s.increment, s2 = :a:c:d:e:f
这意味着只有第一段才是由Object.clone()复制的,所以此时进行的是一种“浅层复制”。若希望复制整条
蛇——即进行“深层复制”——必须在被覆盖的clone()里采取附加的操作。
通常可在从一个能克隆的类里调用super.clone(),以确保所有基础类行动(包括Object.clone())能够进
行。随着是为对象内每个句柄都明确调用一个clone();否则那些句柄会别名变成原始对象的句柄。构建器
的调用也大致相同——首先构造基础类,然后是下一个衍生的构建器⋯⋯以此类推,直到位于最深层的衍生
构建器。区别在于clone()并不是个构建器,所以没有办法实现自动克隆。为了克隆,必须由自己明确进
行。
网友评论