1. clone方法简介
clone方法返回与当前对象的一个副本对象。可以通过操作副本对象而不影响当前对象。
使用clone方法需要实现Cloneable接口。并重写Object方法中的clone方法。
需要注意的是在clone在Object中是project修饰符。因为所有类都是Object的子类,所以如果不实现clone方法,在类中可以直接使用父类的clone方法,但是其对象在别的类中不能调用clone方法。所以必须重写clone方法。
如果不实现Cloneable接口,只重写clone方法,调用则会抛出异常。
Stu类未实现Cloneable接口所以抛出异常
2. 浅克隆与深克隆
2.1 浅克隆
看下面一个例子
1 importjava.util.Arrays;2 importjava.util.HashSet;3 importjava.util.Scanner;4 importjava.util.Set;5
6 public classTest {7
8 public static void main(String[] args) throwsCloneNotSupportedException {9 Stu stu = newStu();10 stu.name = "Tom";11 Stu stu1;12 stu1 =stu;13 System.out.println(stu==stu1);14 stu1.name = "LiMing";15 System.out.println("stu:"+stu.name+" stu1:"+stu1.name);16 stu1 =(Stu) stu.clone();17 System.out.println(stu==stu1);18 stu1.name = "Tom";19 System.out.println("stu:"+stu.name+" stu1:"+stu1.name);20 }21
22 }23 class Stu implementsCloneable{24 String name;25 @Override26 protected Object clone() throwsCloneNotSupportedException {27 //TODO Auto-generated method stub
28 return super.clone();29 }30
31 }
Stu类实现了Cloneable接口,并且重写了clone方法。
首先设stu1变量值等于stu,我们都知道stu1直接指向stu的地址。所以测试stu1==stu时,结果为true,说明两个地址相同。同时改变stu1的name值为“LiMing”,stu的name值也改为“LiMimg”。
设stu1变量值等于stu.clone(),这时stu1的指向stu对象副本的地址,测试stu1==stu,结果为false,说明两个地址不同。改变stu1的name值为"Tom",stu因为指向的对象与stu1的指向的对象不同,因此stu的name值并未改变。
测试结果
2.2 深克隆
考虑下面这种情况
学生类包含笔类,这时在使用clone方法。
1 public classTest {2
3 public static void main(String[] args) throwsCloneNotSupportedException {4 Stu stu = newStu();5 stu.name = "Tom";6 stu.pen.name = "A";7 Stu stu1;8 stu1 =(Stu) stu.clone();9 System.out.println(stu1.pen==stu.pen);10 stu1.pen.name = "B";11 System.out.println("stu1:"+stu1.pen.name+"|stu:"+stu.pen.name);12 }13
14 }15 class Stu implementsCloneable{16 String name;17 Pen pen = newPen();18 @Override19 protected Object clone() throwsCloneNotSupportedException {20 //TODO Auto-generated method stub
21 return super.clone();22 }23
24 }25 classPen{26 String name;27 }
发现此时stu1.pen的地址与stu.pen的地址相同。所以改变stu1.pen.name的值,stu.pen.name的值也发生了相应的改变。这是因为对于引用类型,clone方法只克隆引用类型的地址。这时就用到了深克隆。
测试结果
深克隆:
1 public classTest {2
3 public static void main(String[] args) throwsCloneNotSupportedException {4 Stu stu = newStu();5 stu.name = "Tom";6 stu.pen.name = "A";7 Stu stu1;8 stu1 =(Stu) stu.clone();9 System.out.println(stu1.pen==stu.pen);10 stu1.pen.name = "B";11 System.out.println("stu1:"+stu1.pen.name+"|stu:"+stu.pen.name);12 }13
14 }15 class Stu implementsCloneable{16 String name;17 Pen pen = newPen();18 @Override19 protected Object clone() throwsCloneNotSupportedException {20 //TODO Auto-generated method stub
21 Stu temp = (Stu) super.clone();22 temp.pen =(Pen) pen.clone();23 returntemp;24 }25
26 }27 class Pen implementsCloneable{28 String name;29 @Override30 protected Object clone() throwsCloneNotSupportedException {31 //TODO Auto-generated method stub
32 return super.clone();33 }34 }
此时Pen类也重写了Cloneable方法。同时在重写Stu类的clone方法时先克隆Pen,在返回temp,这样temp中的pen指向克隆后的地址。
运行结果stu1.pen的地址与stu.pen的地址不在相同,改变stu1.pen.name的值而stu.pen.name的值不在改变。stu1.pen对象是一个全新的副本。
测试结果: