Java中的clone() 深拷贝 浅拷贝

Java中的clone() 深拷贝 浅拷贝

上图展示了浅拷贝:对于非基本数据类型,clone过后,结果两个指针指向了同一块儿内存空间,所以仅仅是浅拷贝,这样的话如果对一个对象进行操作,另一个内容也会变,这显然是不合理的,应该每个对象分别保存自己的数据。

所以我们要进行深拷贝!

浅拷贝和深拷贝例子:

[java] view plaincopy

  1. import java.util.Vector;

  2. public class Student implements Cloneable{

  3. private int id;

  4. private String name;

  5. private Vector courses;

  6. public Student(){

  7. try{

  8. Thread.sleep(1000);

  9. System.out.println("Student Construnctor called");

  10. }catch(InterruptedException e){

  11. e.printStackTrace();

  12. }

  13. }

  14. public int getId(){

  15. return id;

  16. }

  17. public void setId(int id){

  18. this.id=id;

  19. }

  20. public String getName(){

  21. return name;

  22. }

  23. public void setName(String name){

  24. this.name=name;

  25. }

  26. public Vector getCourses(){

  27. return courses;

  28. }

  29. public void setCourses(Vector courses){

  30. this.courses=courses;

  31. }

  32. public Student newInstance(){ //使用clone()创建对象,浅拷贝

  33. try{

  34. return (Student)this.clone();

  35. }catch(CloneNotSupportedException e){

  36. e.printStackTrace();

  37. }

  38. return null;

  39. }

  40. public Student deepClone(){ //使用clone()创建对象,深拷贝

  41. try{

  42. Student cloning = (Student) super.clone();

  43. // Student cloning = (Strdent) this.clone(); //和上一句话效果等价

  44. cloning.courses = new Vector(); //关键点:非基本数据类型的空间需要自己新开辟一块儿

  45. return cloning;

  46. }catch(CloneNotSupportedException e){

  47. e.printStackTrace();

  48. }

  49. return null;

  50. }

  51. }

[java] view plaincopy

  1. import java.util.Vector;

  2. public class Test {

  3. public static void main(String[] args) {

  4. // TODO Auto-generated method stub

  5. Student stu1 = null;

  6. shallowCopyDemo(stu1);

  7. System.out.println("----- ----- -----I'm cut-off rule----- ----- -----");

  8. deepCopyDemo(stu1);

  9. }

  10. public static void shallowCopyDemo(Student stu1) {

  11. stu1=new Student();

  12. Vector cs=new Vector();

  13. cs.add("Java");

  14. stu1.setId(1);

  15. stu1.setName("Tom");

  16. stu1.setCourses(cs);

  17. Student stu2=stu1.newInstance();

  18. stu2.setId(2);

  19. stu2.setName("Mary");

  20. stu2.getCourses().add("C#");

  21. System.out.println("stu1'name:"+stu1.getName());

  22. System.out.println("stu2'name:"+stu2.getName());

  23. System.out.println(stu1.getCourses()==stu2.getCourses());

  24. System.out.println(stu1.getName + "'s course: " + stu1.getCourses());

  25. System.out.println(stu2.getName + "'s course: " + stu2.getCourses());

  26. }

  27. public static void deepCopyDemo(Student stu1) {

  28. stu1=new Student();

  29. Vector cs=new Vector();

  30. cs.add("Java");

  31. stu1.setId(1);

  32. stu1.setName("Tom");

  33. stu1.setCourses(cs);

  34. Student stu2=stu1.deepClone();

  35. stu2.setId(2);

  36. stu2.setName("Mary");

  37. stu2.getCourses().add("C#");

  38. System.out.println("stu1'name:"+stu1.getName());

  39. System.out.println("stu2'name:"+stu2.getName());

  40. System.out.println(stu1.getCourses()==stu2.getCourses());

  41. System.out.println(stu1.getName + "'s course: " + stu1.getCourses());

  42. System.out.println(stu2.getName + "'s course: " + stu2.getCourses());

  43. }

  44. }

输出结果:

由结果可知,第一种调用浅拷贝导致对Mary添加课程C#的时候,Tom的课程中竟然 也有了C#,而且Mary的课程中也有Tom的Java,且stu1.getCourses()==stu2.getCourses()返回的是 “true”,说明二者的course属性指向的就是同一块儿内存;而在第二种情况中,我们为copy出来的Mary的course新开辟了一块儿空间 cloning.courses = new Vector(),所以Tom和Mary操控的是不同的Vector内存,两者自然就不一样了。

在上例中,深拷贝deepClone()和浅拷贝newInstance()函数都是 我们自己写的,所以deepClone()的Student cloning = (Student) super.clone()和Student cloning = (Strdent) this.clone()都是可行的。除此之外,我们也可以直接覆写本类的clone()函数这样的话就只能使用Student cloning = (Student) super.clone()了,覆写的代码如下:

[java] view plaincopy

  1. public Object clone(){ //覆写clone(),深拷贝

  2. try{

  3. Student cloning = (Student) super.clone();

  4. cloning.courses = new Vector(); //关键点:非基本数据类型的空间需要自己新开辟一块儿

  5. return cloning;

  6. }catch(CloneNotSupportedException e){

  7. e.printStackTrace();

  8. }

  9. return null;

  10. }

这里不能使用Student cloning = (Strdent) this.clone()的原因是我们正在覆写本类的clone()方法,如果再调用本类的函数,即:this.clone(),就相当于无线递归无限死循环了,最终肯定会崩溃的。所以这里我们只能调用父类的函数,即:super.clone()。

所以,要么自己给自己的深拷贝函数起一个名字,要么覆写本类的clone()方法,自己选一个就好,但两者的关键都在于——对于非基本数据类型,要重新new一块儿空间。