Java clone() 浅克隆与深度克隆

Java clone() 浅克隆与深度克隆


以下文字转自:桔子园 http://www.blogjava.net/orangelizq/archive/2007/10/17/153573.html

 

 

     现在Clone已经不是一个新鲜词语了,伴随着“多莉”的产生这个词语确实很“火”过一阵子,在java中也有这么一个概念,它可以让我们很方便的“制造”出一个对象的副本来,下面来具体看看java中的Clone机制是如何工作的?
     1. Clone&Copy
     假设现在有一个Employee对象,Employee tobby =new Employee(“CMTobby”,5000),通
常我们会有这样的赋值Employee cindyelf=tobby,这个时候只是简单了copy了一下reference,cindyelf和tobby都指向内存中同一个object,这样cindyelf或者tobby的一个操作都可能影响到对方。打个比方,如果我们通过cindyelf.raiseSalary()方法改变了salary域的值,那么tobby通过getSalary()方法得到的就是修改之后的salary域的值,显然这不是我们愿意看到的。我们希望得到tobby的一个精确拷贝,同时两者互不影响,这时候我们就可以使用Clone来满足我们的需求。Employee cindy=tobby.clone(),这时会生成一个新的Employee对象,并且和tobby具有相同的属性值和方法。
      2. Shallow Clone&Deep Clone
Clone是如何完成的呢?Object在对某个对象实施Clone时对其是一无所知的,它仅仅是简单地执行域对域的copy,这就是Shallow Clone。这样,问题就来了咯,以Employee为例,它里面有一个域hireDay不是基本型别的变量,而是一个reference变量,经过Clone之后就会产生一个新的Date型别的reference,它和原始对象中对应的域指向同一个Date对象,这样克隆类就和原始类共享了一部分信息,而这样显然是不利的,过程下图所示:
Java clone() 浅克隆与深度克隆_第1张图片

 

这个时候我们就需要进行deep Clone了,对那些非基本型别的域进行特殊的处理,例如本例中的hireDay。我们可以重新定义Clone方法,对hireDay做特殊处理,如下代码所示:

[java]  view plain copy print ?
  1.    class Employee implements Cloneable  
  2.   
  3. {  
  4.         public Object clone() throws CloneNotSupportedException  
  5.         {  
  6.          Employee cloned = (Employee) super.clone();  
  7.       cloned.hireDay = (Date) hireDay.clone()  
  8.       return cloned;  
  9.         }  
  10. }  

 

3. Clone()方法的保护机制

在Object中Clone()是被申明为protected的,这样做是有一定的道理的,以Employee

类为例,通过申明为protected,就可以保证只有Employee类里面才能“克隆”Employee对象,原理可以参考我前面关于public、protected、private的学习笔记。

4. Clone()方法的使用

Clone()方法的使用比较简单,注意如下几点即可:

a. 什么时候使用shallow Clone,什么时候使用deep Clone,这个主要看具体对象的域是什么性质的,基本型别还是reference variable

b. 调用Clone()方法的对象所属的类(Class)必须implements Clonable接口,否则在调用Clone方法的时候会抛出CloneNotSupportedException。




1.java里的clone分为: 
A:浅复制(浅克隆): 浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。 
b:深复制(深克隆):深复制把要复制的对象所引用的对象都复制了一遍。 
Java中对象的克隆,为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。必须要遵循下面三点 
1.在派生类中覆盖基类的clone()方法,并声明为public【Object类中的clone()方法为protected的】。 
2.在派生类的clone()方法中,调用super.clone()。 
3.在派生类中实现Cloneable接口。 

Object类里的clone方法是浅复制(浅克隆) 

浅复制(浅克隆)的例子如下: 

Java代码   收藏代码
  1. package com.test;  
  2.   
  3. //浅复制(浅克隆): 浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。  
  4. //深复制(深克隆):深复制把要复制的对象所引用的对象都复制了一遍。  
  5. //  
  6. //Java中对象的克隆,为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。必须要遵循下面三点  
  7. //1.在派生类中覆盖基类的clone()方法,并声明为public【Object类中的clone()方法为protected的】。  
  8. //2.在派生类的clone()方法中,调用super.clone()。  
  9. //3.在派生类中实现Cloneable接口。  
  10.   
  11. //[color=red]Object类里的clone方法是浅复制(浅克隆)[/color]public class CloneTest {  
  12.   
  13.     public static void main(String[] args) throws Exception{  
  14.         //teacher对象将被clone出来的Student对象共享.  
  15.         Teacher teacher = new Teacher();  
  16.         teacher.setAge(40);  
  17.         teacher.setName("Teacher zhang");  
  18.           
  19.         Student student1 = new Student();  
  20.         student1.setAge(20);  
  21.         student1.setName("zhangsan");  
  22.         student1.setTeacher(teacher);  
  23.           
  24.         //复制出来一个对象student2  
  25.         Student student2 = (Student)student1.clone();  
  26.         System.out.println(student2.getAge());  
  27.         System.out.println(student2.getName());  
  28.           
  29.           
  30.         System.out.println("~~~~~~~~~~~~~~~~~~~~~~");  
  31.         System.out.println(student1.getTeacher().getAge());  
  32.         System.out.println(student1.getTeacher().getName());  
  33.           
  34.           
  35.         //修改student2的引用对象  
  36.         student2.getTeacher().setAge(50);  
  37.         student2.getTeacher().setName("Teacher Li");  
  38.           
  39.         System.out.println("~~~~~~~~~~~~~~~~~~~~~~");  
  40.         System.out.println(student1.getTeacher().getAge());  
  41.         System.out.println(student1.getTeacher().getName());  
  42.     }  
  43. }  
  44.   
  45. class Teacher {  
  46.     public int age;  
  47.     public String name;  
  48.       
  49.     public int getAge() {  
  50.         return age;  
  51.     }  
  52.     public void setAge(int age) {  
  53.         this.age = age;  
  54.     }  
  55.     public String getName() {  
  56.         return name;  
  57.     }  
  58.     public void setName(String name) {  
  59.         this.name = name;  
  60.     }  
  61.       
  62.       
  63. }  
  64.   
  65. class Student implements Cloneable{  
  66.       
  67.     public int age ;  
  68.     public String name;  
  69.     public Teacher teacher;  
  70.     public int getAge() {  
  71.         return age;  
  72.     }  
  73.     public void setAge(int age) {  
  74.         this.age = age;  
  75.     }  
  76.     public String getName() {  
  77.         return name;  
  78.     }  
  79.     public void setName(String name) {  
  80.         this.name = name;  
  81.     }  
  82.     public Teacher getTeacher() {  
  83.         return teacher;  
  84.     }  
  85.     public void setTeacher(Teacher teacher) {  
  86.         this.teacher = teacher;  
  87.     }  
  88.     @Override  
  89.     public Object clone() throws CloneNotSupportedException {  
  90.         return super.clone();  
  91.     }  
  92.       
  93.       
  94. }  
  95. 输出结果为:  
  96. 20  
  97. zhangsan  
  98. ~~~~~~~~~~~~~~~~~~~~~~  
  99. 40  
  100. Teacher zhang  
  101. ~~~~~~~~~~~~~~~~~~~~~~  
  102. 50  
  103. Teacher Li  


2.深复制(深Clone)例子: 

Java代码   收藏代码
  1. package com.test1;  
  2.   
  3. //深clone  
  4. public class DeepCloneTest {  
  5.   
  6.     public static void main(String[] args) throws Exception{  
  7.         //teacher对象将不被clone出来的Student对象共享.  
  8.         Teacher teacher = new Teacher();  
  9.         teacher.setAge(40);  
  10.         teacher.setName("Teacher zhang");  
  11.           
  12.         Student student1 = new Student();  
  13.         student1.setAge(20);  
  14.         student1.setName("zhangsan");  
  15.         student1.setTeacher(teacher);  
  16.           
  17.         //复制出来一个对象student2  
  18.         Student student2 = (Student)student1.clone();  
  19.         System.out.println(student2.getAge());  
  20.         System.out.println(student2.getName());  
  21.           
  22.           
  23.         System.out.println("~~~~~~~~~~~~~~~~~~~~~~");  
  24.         System.out.println(student1.getTeacher().getAge());  
  25.         System.out.println(student1.getTeacher().getName());  
  26.           
  27.           
  28.         //修改student2的引用对象  
  29.         student2.getTeacher().setAge(50);  
  30.         student2.getTeacher().setName("Teacher Li");  
  31.           
  32.         System.out.println("~~~~~~~~~~~~~~~~~~~~~~");  
  33.         System.out.println(student1.getTeacher().getAge());  
  34.         System.out.println(student1.getTeacher().getName());  
  35.     }  
  36. }  
  37.   
  38. class Teacher implements Cloneable{  
  39.     public int age;  
  40.     public String name;  
  41.       
  42.     public int getAge() {  
  43.         return age;  
  44.     }  
  45.     public void setAge(int age) {  
  46.         this.age = age;  
  47.     }  
  48.     public String getName() {  
  49.         return name;  
  50.     }  
  51.     public void setName(String name) {  
  52.         this.name = name;  
  53.     }  
  54.     @Override  
  55.     public Object clone() throws CloneNotSupportedException {  
  56.         return super.clone();  
  57.     }  
  58.       
  59. }  
  60.   
  61. class Student implements Cloneable{  
  62.       
  63.     public int age ;  
  64.     public String name;  
  65.     public Teacher teacher;  
  66.     public int getAge() {  
  67.         return age;  
  68.     }  
  69.     public void setAge(int age) {  
  70.         this.age = age;  
  71.     }  
  72.     public String getName() {  
  73.         return name;  
  74.     }  
  75.     public void setName(String name) {  
  76.         this.name = name;  
  77.     }  
  78.     public Teacher getTeacher() {  
  79.         return teacher;  
  80.     }  
  81.     public void setTeacher(Teacher teacher) {  
  82.         this.teacher = teacher;  
  83.     }  
  84.     @Override  
  85.     public Object clone() throws CloneNotSupportedException {  
  86.         Student student = (Student)super.clone();  
  87.         //将引用的对象teacher也clone下  
  88.         student.setTeacher((Teacher)(student.getTeacher().clone()));  
  89.         return student;  
  90.     }  
  91.       
  92.       
  93. }  
  94. 输出结果为:  
  95. 20  
  96. zhangsan  
  97. ~~~~~~~~~~~~~~~~~~~~~~  
  98. 40  
  99. Teacher zhang  
  100. ~~~~~~~~~~~~~~~~~~~~~~  
  101. 40  
  102. Teacher zhang  


3.利用序列化来做深复制,把对象写到流里的过程是序列化(Serilization)过程,而把对象从流中读出来的过程则叫做反序列化(Deserialization)过程。应当指出的是, 写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。 ,利用这个特性,可以做深拷贝 
Java代码   收藏代码
  1. package com.test3;  
  2.   
  3. import java.io.ByteArrayInputStream;  
  4. import java.io.ByteArrayOutputStream;  
  5. import java.io.ObjectInputStream;  
  6. import java.io.ObjectOutputStream;  
  7. import java.io.Serializable;  
  8. //利用序列化来做深复制  
  9. //深clone  
  10. public class DeepCloneTest {  
  11.   
  12.     public static void main(String[] args) throws Exception{  
  13.         //teacher对象将不被clone出来的Student对象共享.  
  14.         Teacher teacher = new Teacher();  
  15.         teacher.setAge(40);  
  16.         teacher.setName("Teacher zhang");  
  17.           
  18.         Student student1 = new Student();  
  19.         student1.setAge(20);  
  20.         student1.setName("zhangsan");  
  21.         student1.setTeacher(teacher);  
  22.           
  23.         //复制出来一个对象student2  
  24.         Student student2 = (Student)student1.deepCopy();  
  25.         System.out.println(student2.getAge());  
  26.         System.out.println(student2.getName());  
  27.           
  28.           
  29.         System.out.println("~~~~~~~~~~~~~~~~~~~~~~");  
  30.         System.out.println(student1.getTeacher().getAge());  
  31.         System.out.println(student1.getTeacher().getName());  
  32.           
  33.           
  34.         //修改student2的引用对象  
  35.         student2.getTeacher().setAge(50);  
  36.         student2.getTeacher().setName("Teacher Li");  
  37.           
  38.         System.out.println("~~~~~~~~~~~~~~~~~~~~~~");  
  39.         System.out.println(student1.getTeacher().getAge());  
  40.         System.out.println(student1.getTeacher().getName());  
  41.     }  
  42. }  
  43.   
  44. class Teacher implements Serializable{  
  45.       
  46.     private static final long serialVersionUID = -8834559347461591191L;  
  47.       
  48.     public int age;  
  49.     public String name;  
  50.       
  51.     public int getAge() {  
  52.         return age;  
  53.     }  
  54.     public void setAge(int age) {  
  55.         this.age = age;  
  56.     }  
  57.     public String getName() {  
  58.         return name;  
  59.     }  
  60.     public void setName(String name) {  
  61.         this.name = name;  
  62.     }  
  63.       
  64. }  
  65.   
  66. class Student implements Serializable{  
  67.       
  68.     //serialVersionUID 如果你的对象序列化后存到硬盘上面后,可是后来你却更改了类的field(增加或减少或改名),当你反序列化时,就会出现Exception的,这样就会造成不兼容性的问题。   
  69.     //但当serialVersionUID相同时,它就会将不一样的field以type的缺省值赋值(如int型的是0,String型的是null等),这个可以避开不兼容性的问题。所以最好给serialVersionUID赋值  
  70.     private static final long serialVersionUID = 7991552226614088458L;  
  71.       
  72.     public int age ;  
  73.     public String name;  
  74.     public Teacher teacher;  
  75.     public int getAge() {  
  76.         return age;  
  77.     }  
  78.     public void setAge(int age) {  
  79.         this.age = age;  
  80.     }  
  81.     public String getName() {  
  82.         return name;  
  83.     }  
  84.     public void setName(String name) {  
  85.         this.name = name;  
  86.     }  
  87.     public Teacher getTeacher() {  
  88.         return teacher;  
  89.     }  
  90.     public void setTeacher(Teacher teacher) {  
  91.         this.teacher = teacher;  
  92.     }  
  93.       
  94.     public Object deepCopy() throws Exception{  
  95.         //将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝  
  96.         ByteArrayOutputStream bos = new ByteArrayOutputStream();  
  97.   
  98.         ObjectOutputStream oos = new ObjectOutputStream(bos);  
  99.   
  100.         oos.writeObject(this);  
  101.   
  102.         //将流序列化成对象  
  103.         ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());  
  104.   
  105.         ObjectInputStream ois = new ObjectInputStream(bis);  
  106.   
  107.         return ois.readObject();  
  108.     }  
  109.       
  110.       
  111. }  
  112. 输出结果为:  
  113. 20  
  114. zhangsan  
  115. ~~~~~~~~~~~~~~~~~~~~~~  
  116. 40  
  117. Teacher zhang  
  118. ~~~~~~~~~~~~~~~~~~~~~~  
  119. 40  
  120. Teacher zhang  

你可能感兴趣的:(Java clone() 浅克隆与深度克隆)