先做个标记
http://www.iteye.com/topic/182772
http://www.blogjava.net/jerry-zhaoj/archive/2009/10/14/298141.html
关于super.clone的理解
http://hi.baidu.com/%BB%AA%CF%C4%D1%A7%C9%FA%C1%AA%C3%CB/blog/item/7d70a43842622832b8998f86.html
http://tieba.baidu.com/p/938267457
http://topic.csdn.net/u/20080326/09/a48470f3-f2c2-43c4-af56-fa8ffc12b629.html
http://www.iteye.com/topic/1113790
如何查看JDK源码
http://hi.baidu.com/koflance/blog/item/07809915d6f70e5cf3de32dd.html
http://www.iteye.com/topic/1113790
java对象的克隆步骤:
1.类需要实现Cloneable接口
2 覆盖clone方法,调用super.clone即可。如果是复合类型,如数组,集合类,还需要继续clon下去。
类A1只含有原生类型的属性,A2、A3类含有复合类型的属性,实现起来要clone到底,不然只会拷贝一个引用,从而实现浅拷贝(影子拷贝),实际上只有一个堆对象。两个引用实际上指向了一个对象。
A1的实现
public class A1 implements Cloneable {
public int age;
public Object clone() {
A1 o = null;
try {
o = (A1) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
Object o = new Object();
}
A2的实现:
public class A2 implements Cloneable {
public String[] name;
public Object clone() {
A2 o = null;
try {
o = (A2) super.clone();
o.name = (String[]) name.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
public A2() {
name = new String[2];
}
}
A3是最复杂的
package com.tmall;
import java.util.Vector;
public class A3 implements Cloneable {
public String name[];
public Vector<B> claB;
public A3() {
name = new String[2];
claB = new Vector<B>();
}
public Object clone() {
A3 o = null;
try {
o = (A3) super.clone();
o.name = (String[]) name.clone();// 深度clone
o.claB = new Vector<B>();// 将clone进行到底
for (int i = 0; i < claB.size(); i++) {
B temp = (B) claB.get(i).clone();// 当然Class B也要实现相应clone方法
o.claB.add(temp);
}
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
}
其中B类的定义:
public class B {
public int age;
public Object clone() {
B o = null;
try {
o = (B) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
}
两个测试方法:
public static void testA1(){
A1 a11 = new A1();
A1 a12 = (A1)a11.clone();
a11.age = 10;
a12.age = 20;
System.out.println(a11.age);
System.out.println(a12.age);
}
public static void testA2(){
A2 a1=new A2();
a1.name[0]="a";
a1.name[1]="1";
A2 a2=(A2)a1.clone();
a2.name[0]="b";
a2.name[1]="1";
System.out.println("a1.name="+a1.name);
System.out.println("a1.name="+a1.name[0]+a1.name[1]);
System.out.println("a2.name="+a2.name);
System.out.println("a2.name="+a2.name[0]+a2.name[1]);
}
运行结果,可以知道成功实现了深拷贝。
进一步研究:
1 . 翻阅cloneable接口的源码,发现是一个空接口,啥都没有。
package java.lang;
/**
* A class implements the <code>Cloneable</code> interface to
* indicate to the {@link java.lang.Object#clone()} method that it
* is legal for that method to make a
* field-for-field copy of instances of that class.
* <p>
* Invoking Object's clone method on an instance that does not implement the
* <code>Cloneable</code> interface results in the exception
* <code>CloneNotSupportedException</code> being thrown.
* <p>
* By convention, classes that implement this interface should override
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.
* <p>
* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface. Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.
*
* @author unascribed
* @version 1.17, 11/17/05
* @see java.lang.CloneNotSupportedException
* @see java.lang.Object#clone()
* @since JDK1.0
*/
public interface Cloneable {
}
在这篇帖子中对这个问题就行了讨论
2.
clone方法的错误使用。
在不止一篇帖子里面,错误地使用了clone这个方法。
作者是这样调用clone方法的:
A a1=new A();
A a2=new A(); **
a2=(A)a1.clone();
其实做一次实验就可以知道,**行是多余的。
A1 a11 = new A1();
A1 a12 = (A1) a11.clone();
A1 a13 = new A1();
System.out.println("a11的地址是" + a11);
System.out.println("a12的地址是" + a12);
System.out.println("a13的地址是" + a13);
a13 = (A1) a11.clone();
System.out.println("a13的地址是" + a13);
我的电脑运行结果是
a11的地址是com.tmall.A1@c17164
a12的地址是com.tmall.A1@1fb8ee3
a13的地址是com.tmall.A1@61de33
a13的地址是com.tmall.A1@14318bb
说明a12克隆成功;
A1 a12 = (A1) a11.clone();
是没有问题的。
反而实现new 一个对象如a13,在调用clone()方法会多此一举:
A1 a13 = new A1();
a13 = (A1) a11.clone();
可以看到运行结果里面,a13的地址换了两次。第一次是new出来的新对象,第二次是克隆出来的新对象,两次地址都不等于那个被clone的对象a11。
我翻阅了《effectice java》的条款11,“谨慎地覆盖clone”。作者Joshua Bloch(可是google的首席java工程师)第三段写道:
“如果实现cloneable接口是对某个类起到作用,类和它的所有超类都必须遵守一个想当复杂的、不可实施的,并且基本上没有文档说明的协议。由此得到一种语言之外的机制:
无需要调用构造器就可以创建对象”,红色部分也说明了**行new 一个类是多余的。
3. Object类里面的clone定义。
翻看JDK源码,Object类里面的clone方法定义如下
protected native Object clone() throws CloneNotSupportedException;
使用C/C++来实现的。
翻了翻相关资料,java clone 克隆 super.clone
是“bitwise(逐位)的复制, 将该对象的内存空间完全复制到新的空间中去”这样实现的。
另外这个方法是protected的,不同的包是不能用的。这就可以解释为什么我们不能显示地调用Object.clone()这个方法。因为Object这个类是放在java,lang这个包里面的,而clone()方法是protected的,是不可见的。如果这样写
Object o1 = new Object();
Object o2 = o1.clone();
编译器肯定会报错的:
之前我不理解A类中为什么用super.clone()这样子的写法,现在明白了。
4. 如果一个类实现了Cloneable接口,Object的clone方法就会返回该对象的逐域拷贝,否则就会抛出CloneNotSupportedException异常。
来自《effective java》条款11的第二段话。所以要实现对象的clone,必须实现cloneable接口。