克隆是Object类中的一个protected修饰的方法,故只能被子类调用或重写,克隆会在栈中产生一个新引用,在堆中产生一个新对象,但是如果对象中有引用,则引用地址是被克隆的,即对象中的引用指向同一个变量(常量)
看如下代码
class Address
{
String detail;
public Address(String detail)
{
this.detail = detail;
}
}
class User implements Cloneable
{
int age;
Address address;
public User(int age,String address)
{
this.age = age;
this.address = new Address(address);
}
public User clone()
throws CloneNotSupportedException
{
return (User)super.clone();
}
}
public class CloneTest {
public static void main(String[] args)
throws CloneNotSupportedException
{
String str = "北京海淀";
User u1 = new User(1,str);
User u2 = u1.clone();
System.out.println(u1 == u2);
System.out.println(u1.address == u2.address);
}
}
每个要调用clone方法的函数必须throws CloneNotSupportedException。
结果输出是
falsetrue
因为u1和u2在栈中是两个不同的引用变量,当然不相等。而u1创建时,str作为参数传入构造器,而构造器里面用str创建了一个Address类的对象,取名叫A好了,并把u1.address引用到A。u1克隆出u2,u2.address克隆了u1.address的引用(地址),而Address对象还是只有那个A,u2.address同样指向这个A,所以第二个结果当然是true。
这也说明,clone方法是一种“浅克隆”,只克隆对象本身的成员变量值,当这个值是一个引用时,并不会克隆引用的对象。如果需要深克隆,则可以用递归实现。
一个值得注意的地方是,clone是一个效率很高的方法,对于一个一百个int的int[]数据类型,clone方法比静态copy方法快接近两倍。(int[] 是一个跟int完全不同的引用类型,它也有方法)。
注意:有两个问题:
1、一个类要重写clone方法,必须实现Cloneable接口,本来以为不需要,因为任何一个类都继承自Object,通过super完全可以调用父类的clone方法,为什么一定要实现这个接口呢,反正不实现这个接口就会抛出上面个异常。猜想是接口里面有变量来完成某种判断?
2、书上说自定义类里面要实现自己的clone方法,个人尝试了一下其实User里面的clone方法当然可以取别的名字,一样可以成功运行。但是如果某个子类继承了User,要进行克隆的时候,就必须知道User里克隆的方法是什么,对于程序的扩展是很麻烦的。那么全部都定义为clone,这样无论什么类,只要用super.clone() 就可以一路调用父类的clone方法直到Object,方便多了。