java 浅克隆与深克隆

 Java的对象都是引用,当将一个对象赋值给另外一个对象的时候, 也就是说指针(当然,java没有指针的概念)同指向同一块内存地址。


      这个时如果对一个对象进行修改,也必然会修改另外一个对象的值,这明显不是我们想要的.
 
     解决这个问题,可以引入克隆技术,我们可以克隆一个对象出来,使得对克隆出来的对象修改不会改变原始对象的值。
  
  克隆分为:浅克隆和深克隆。
  
  浅克隆是指:浅克隆只是克隆当前的对象,不克隆该对象所应用的对象
  
  深克隆是指:深克隆不但克隆当前的对象,而且还克隆该对象所引用的对象

  
  Object 类本身不实现接口 Cloneable,所以在类为 Object 的对象上调用 clone 方法将会导致在运行时抛出异常。
  
  Object 类的 clone 方法执行特定的克隆操作。首先,如果此对象的类不能实现接口 Cloneable,则会抛出
  CloneNotSupportedException。 注意:所有的数组都被视为实现接口 Cloneable。
 否则,此方法会创建此对象的类的一个新实例,并像通过分配那样, 严格使用此对象相应字段的内容初始化该对象的所有字段; 这些字段的内容没有被自我克隆。
  

  所以,此方法执行的是该对象的“浅表复制”,而不“深层复制”操作。

我们知道,Java是纯面向对象的程序设计语言。
Java里,所有的类的顶级父类都是java.lang.Object类,
也就是说,如果一个类没有显示 申明继承关系,它的父类默认就是java.lang.Object。 
                                                                                    
   有一个很简单的方法可以证明这一点,我们写一个Test类,如下: 
public class Test { 
    public void someMethod() { 
        super.clone(); 
    } 

里面调用了super.clone(),编译时并不报错。其实clone()方法为java.lang.Object类提供的一个 protected型方法。 


对象克隆 


本文通过介绍java.lang.Object#clone()方法来说明Java语言的对象克隆特性。 
java.lang.Object#clone()方法由java.lang.Object加以实现,主要对对象本身加以克隆。

首先我们看看下面的例子: 

[java]  view plain copy print ?
  1. public class TestClone {   
  2.     public static void main(String[] args) {   
  3.         MyClone myClone1 = new MyClone("clone1");   
  4.           
  5.         MyClone myClone2 = (MyClone)myClone1.clone();   
  6.           
  7.         if (myClone2 != null) {   
  8.             System.out.println(myClone2.getName());   
  9.             System.out.println("myClone2 equals myClone1: " + myClone2.equals(myClone1));   
  10.         } else {   
  11.             System.out.println("Clone Not Supported");   
  12.         }   
  13.     }   
  14. }   
  15. class MyClone {   
  16.     private String name;   
  17.     public MyClone(String name) {   
  18.         this.name = name;   
  19.     }   
  20.       
  21.     public String getName() {   
  22.         return name;   
  23.     }   
  24.     public void setName(String name) {   
  25.         this.name = name;   
  26.     }   
  27.       
  28.     public Object clone() {   
  29.         try {   
  30.             return super.clone();   
  31.         } catch (CloneNotSupportedException e) {   
  32.             return null;   
  33.         }   
  34.     }   

编译执行TestClone,打印出: 


C:\clone>javac *.java 
C:\clone>java TestClone 
Clone Not Supported 
C:\clone> 


说明MyClone#clone()方法调用super.clone()时抛出了CloneNotSupportedException异常,不 支持克隆。 


为什么父类java.lang.Object里提供了clone()方法,却不能调用呢? 


原来,Java语言虽然提供了这个方法,但考虑到安全问题, 

一方面将clone()访问级别设置为protected型,以限制外部类访问; 
另一方面,强制需要提供clone功能的子类实现java.lang.Cloneable接口,
在运行期,JVM会检查调用clone()方法的 类,如果该类未实现java.lang.Cloneable接口,则抛出CloneNotSupportedException异常。 

java.lang.Cloneable接口是一个空的接口,没有申明任何属性与方法。该接口只是告诉JVM,该接口的实现类需要开放“克隆”功 能。 

我们再将MyClone类稍作改变,让其实现Cloneable接口: 
class MyClone implements Cloneable { 
    ...//其余不做改变 


编译执行TestClone,打印出: 
C:\clone>javac *.java 
C:\clone>java TestClone 
clone1 
myClone2 equals myClone1 :    false 
C:\clone> 

根据结果,我们可以发现: 
1,myClone1.clone()克隆了跟myClone1具有相同属性值的对象 
2,但克隆出的对象myClone2跟myClone1不是同一个对象(具有不同的内存空间) 

小结: 

如果要让一个类A提供克隆功能,该类必须实现java.lang.Cloneable接口,并重载 java.lang.Object#clone()方法。

[java]  view plain copy print ?
  1. public class A extends Cloneable {   
  2.     public Object clone() {   
  3.         try {   
  4.             return super.clone();   
  5.         } catch (CloneNotSupportedException e) {   
  6.             //throw (new InternalError(e.getMessage()));   
  7.             return null;   
  8.         }   
  9.     }   
  10. }   
对象的深层次克隆 
  上例说明了怎么样克隆一个具有简单属性(String,int,boolean等)的对象。 
但如果一个对象的属性类型是List,Map,或者用户自定义的其他类时,克隆行为是通过怎样的方式进行的? 
很多时候,我们希望即使修改了克隆后的对象的属性值,也不会影响到原对象,这种克隆我们称之为对象的深层次克隆。


怎么样实现对象的深层次克隆呢? 




验证对象的克隆方式 
为了验证对象的克隆方式,我们对上面的例子加以改进,如下

 

[java]  view plain copy print ?
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3.   
  4. public class TestClone {  
  5.     public static void main(String[] args) {  
  6.         // 为克隆对象设置值  
  7.         MyClone myClone1 = new MyClone("clone1");  
  8.           
  9.         myClone1.setBoolValue(true);  
  10.         myClone1.setIntValue(100);  
  11.   
  12.         // 设置List值  
  13.         List<Element> listValue = new ArrayList<Element>();  
  14.         listValue.add(new Element("ListElement1"));  
  15.         listValue.add(new Element("ListElement2"));  
  16.         listValue.add(new Element("ListElement3"));  
  17.           
  18.         myClone1.setListValue(listValue);  
  19.   
  20.         // 设置Element值  
  21.         Element element1 = new Element("element1");  
  22.         myClone1.setElement(element1);  
  23.   
  24.         // 克隆  
  25.         MyClone myClone2 = (MyClone) myClone1.clone();  
  26.   
  27.         if (myClone2 != null) {  
  28.   
  29.             // 简单属性  
  30.             System.out.println("myClone2.name=" + myClone2.getName()  
  31.                     + " myClone2.boolValue=" + myClone2.isBoolValue()  
  32.                     + " myClone2.intValue=" + myClone2.getIntValue());  
  33.   
  34.             // 复合属性(List<Element>与Element)  
  35.             List clonedList = myClone2.getListValue();  
  36.             Element element2 = myClone2.getElement();  
  37.   
  38.             System.out  
  39.                     .println("myClone2.listValue.size():" + clonedList.size());  
  40.               
  41.             System.out.println("myClone2.element.equals(myClone1.element):"  
  42.                     + element2.equals(element1));  
  43.             // 打印结果 : element1.  
  44.             System.out.println("myClone2.element.name:" + element2.getName());  
  45.   
  46.             /* 
  47.              *  
  48.              * 下面我们测试一下myClone2.element是否等于myClone1.element 
  49.              * 以及myClone2.listValue是否等于myClone1.listValue 
  50.              *  
  51.              * 为此,我们修改myClone2.element与myClone2.listValue, 
  52.              * 如果myClone1的相应值也跟着被修改了,则它们引用 的是同一个内存空间的变量,我们认为它们相等 . 
  53.              */  
  54.   
  55.             clonedList.add("ListElement4");  
  56.             // 打印结果 : 4.  
  57.             System.out.println("myClone1.listValue.size():" + listValue.size());  
  58.   
  59.             element2.setName("Element2");  
  60.             // 打印结果为:  Element2.  
  61.             System.out.println("myClone1.element.name:" + element1.getName());  
  62.   
  63.         } else {  
  64.             System.out.println("Clone Not Supported");  
  65.         }  
  66.   
  67.     }  
  68.   
  69. }  
  70.   
  71. class MyClone implements Cloneable {  
  72.     private int intValue;  
  73.     private boolean boolValue;  
  74.     private String name;  
  75.     private List<Element> listValue;  
  76.     private Element element;  
  77.   
  78.     public MyClone(String name) {  
  79.         this.name = name;  
  80.     }  
  81.   
  82.     public int getIntValue() {  
  83.         return intValue;  
  84.     }  
  85.   
  86.     public void setIntValue(int intValue) {  
  87.         this.intValue = intValue;  
  88.     }  
  89.   
  90.     public boolean isBoolValue() {  
  91.         return boolValue;  
  92.     }  
  93.   
  94.     public void setBoolValue(boolean boolValue) {  
  95.         this.boolValue = boolValue;  
  96.     }  
  97.   
  98.     public String getName() {  
  99.         return name;  
  100.     }  
  101.   
  102.     public void setName(String name) {  
  103.         this.name = name;  
  104.     }  
  105.   
  106.     public List<Element> getListValue() {  
  107.         return listValue;  
  108.     }  
  109.   
  110.     public void setListValue(List<Element> listValue) {  
  111.         this.listValue = listValue;  
  112.     }  
  113.   
  114.     public Element getElement() {  
  115.         return element;  
  116.     }  
  117.   
  118.     public void setElement(Element element) {  
  119.         this.element = element;  
  120.     }  
  121.     /***** 
  122.      *  
  123.      * 手动在重载的clone()方法里,对属性也分别采用克隆操作。 
  124.      *  
  125.      * 当然条件是,属性类也得支持克隆操作  
  126.      *   
  127.      */  
  128.       
  129.     public Object clone() {  
  130.         try {  
  131.             return super.clone();  
  132.          } catch (CloneNotSupportedException e) {  
  133.             //throw (new InternalError(e.getMessage()));  
  134.             return null;  
  135.          }  
  136.      }  
  137. }  
  138.   
  139. class Element implements Cloneable {  
  140.     private String name;  
  141.   
  142.     public Element(String name) {  
  143.         this.name = name;  
  144.     }  
  145.   
  146.     public String getName() {  
  147.         return name;  
  148.     }  
  149.   
  150.     public void setName(String name) {  
  151.         this.name = name;  
  152.     }  
  153.       
  154.     /***** 
  155.        
  156.       手动在重载的clone()方法里,对属性也分别采用克隆操作。 
  157.             当然条件是,属性类也得支持克隆操作  
  158.         
  159.      */  
  160.     public Element clone() {  
  161.         try {  
  162.             return (Element) super.clone();  
  163.         } catch (CloneNotSupportedException e) {  
  164.             return null;  
  165.         }  
  166.     }  
  167.   
  168. }  

运行结果:

myClone2.name=clone1 myClone2.boolValue=true myClone2.intValue=100
myClone2.listValue.size():3
myClone2.element.equals(myClone1.element):true
myClone2.element.name:element1
myClone1.listValue.size():4
myClone1.element.name:Element2


我们发现,对于对象里的List,Element等复合属性,super.clone()只是简单地赋值,没有采取克隆手段。


也就是说,修改被克 隆后的对象值,会影响到原对象。 


怎么进行深层次的克隆呢? 
答案是,我们只能手动在重载的clone()方法里,对属性也分别采用克隆操作。当然条件是,属性类也得支持克隆操作 

[java]  view plain copy print ?
  1. class MyClone implements Cloneable {   
  2.      ...   
  3.     <span style="color:#cc0000;"><strong>public Object clone() {   
  4.         try {   
  5.              MyClone  myClone = (MyClone)super.clone();   
  6.             //分别对属性加以克隆操作   
  7.              myClone.element = this.element.clone();   
  8.               
  9.              myClone.listValue = new ArrayList();   
  10.             for (Element ele:this.listValue) {   
  11.                  myClone.listValue.add(ele.clone());   
  12.              }   
  13.                           
  14.             return myClone;   
  15.          } catch (CloneNotSupportedException e) {   
  16.             return null;   
  17.          }   
  18.      } </strong></span>  
  19.      ...   
  20. }   
  21.   
  22. //让Element类也支持克隆操作   
  23. class Element implements Cloneable   {   
  24.      ...   
  25.     public Element clone() {   
  26.         try {   
  27.             return (Element)super.clone();   
  28.          } catch (CloneNotSupportedException e) {   
  29.             return null;   
  30.          }   
  31.      }   
  32. }   

深层次的克隆操作往往存在效率问题,尤其是需要让List,Map等集合类也支持深层次的克隆操作时。 


总结: 
本文结合范例,比较深入地介绍了Java语言的克隆属性,以及克隆的实现方法等。
同时分析了深层次克隆的概念,实现,以及存在的问题等。 但是有没有更好的方法呢?当然,是有的,串行化来实现。


如果实现深克隆?

一个方法自然是重写clone方法,添加如order.items=(LineItems)items.clone()的语句,也就是人为地添加对引用对象的复制。这个方法的缺点是如果引用对象有很多,或者说引用套引用很多重,那么太麻烦了。业界常用的方法是使用串行化然后反串行化的方法来实现深克隆。由于串行化后,对象写到流中,所有引用的对象都包含进来了,所以反串行化后,对等于生成了一个完全克隆的对象。绝!

 

可借鉴:http://blog.csdn.net/accp_fangjian/article/details/2423252

这个方法的要求是对象(包括被引用对象)必须事先了Serializable接口,否则就要用transient关键字将其排除在复制过程中。

再举一个例子:

[java]  view plain copy print ?
  1. package com.itm.clone;  
  2.   
  3. class CloneStudent implements Cloneable {  
  4.     String name;  
  5.     int age;  
  6.   
  7.     CloneStudent(String name, int age) {  
  8.         this.name = name;  
  9.         this.age = age;  
  10.     }  
  11.   
  12.     public Object clone() {  
  13.         Object o = null;  
  14.         try {  
  15.             // Object 中的clone()识别出你要复制的是哪一个对象。  
  16.             o = (CloneStudent) super.clone();  
  17.         } catch (CloneNotSupportedException e) {  
  18.             System.out.println(e.toString());  
  19.         }  
  20.         return o;  
  21.     }  
  22.   
  23.     public static void main(String[] args) {  
  24.         CloneStudent s1 = new CloneStudent("zhangsan"18);  
  25.         CloneStudent s2 = (CloneStudent) s1.clone();  
  26.         s2.name = "lisi";  
  27.         s2.age = 20;  
  28.         // 修改学生2后,不影响学生1的值。  
  29.         System.out.println("name=" + s1.name + "," + "age=" + s1.age);  
  30.     }  
  31. }  

[java]  view plain copy print ?
  1. package com.itm.clone;  
  2.   
  3. // 若 Professor 不实现 Clonebale以及  重写 clone()方法,则是 浅克隆。。。  
  4. //  // 学生1的教授成为lisi,age为30。  
  5. class Professor implements Cloneable{  
  6.     String name;  
  7.     int age;  
  8.   
  9.     Professor(String name, int age) {  
  10.         this.name = name;  
  11.         this.age = age;  
  12.     }  
  13.       
  14.     public Object clone() {  
  15.         try {  
  16.             return super.clone();  
  17.          } catch (CloneNotSupportedException e) {  
  18.             //throw (new InternalError(e.getMessage()));  
  19.             return null;  
  20.          }  
  21.      }  
  22.       
  23. }  
  24.   
  25. class Student implements Cloneable {  
  26.     String name;// 常量对象。  
  27.     int age;  
  28.     Professor p;// 学生1和学生2的引用值都是一样的。  
  29.   
  30.     Student(String name, int age, Professor p) {  
  31.         this.name = name;  
  32.         this.age = age;  
  33.         this.p = p;  
  34.     }  
  35.   
  36.     public Object clone() {  
  37.         Student o = null;  
  38.         try {  
  39.             o = (Student) super.clone();  
  40.         } catch (CloneNotSupportedException e) {  
  41.             System.out.println(e.toString());  
  42.         }  
  43.         // 去掉下面一行:学生1的教授成为lisi,age为30。  
  44.         o.p = (Professor) p.clone();  
  45.         return o;  
  46.     }  
  47.   
  48.       
  49. }  
  50.   
  51. public class TwoTestClone{  
  52.     public static void main(String[] args) {  
  53.         Professor p = new Professor("wangwu"50);  
  54.         Student s1 = new Student("zhangsan"18, p);  
  55.         Student s2 = (Student) s1.clone();  
  56.         s2.p.name = "lisi";  
  57.         s2.p.age = 30;  
  58.         // 学生1的教授 name=wangwu,age=50  
  59.         System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age);  
  60.     }  
  61. }  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的克隆分为克隆克隆两种方式。克隆只是复制了对象的引用,而不是对象本身,因此对克隆对象的修改会影响到原对象。而克隆则是将对象及其引用对象一起复制,因此对克隆对象的修改不会影响到原对象。 以下是Java中实现克隆克隆的示例代码: 1. 克隆 ```java public class Person implements Cloneable { private String name; private Address address; public Person(String name, Address address) { this.name = name; this.address = address; } public String getName() { return name; } public Address getAddress() { return address; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } public class Address { private String city; public Address(String city) { this.city = city; } public String getCity() { return city; } } // 测试代码 Address address = new Address("Beijing"); Person person1 = new Person("Tom", address); Person person2 = (Person) person1.clone(); System.out.println(person1.getAddress().getCity()); // 输出:Beijing System.out.println(person2.getAddress().getCity()); // 输出:Beijing address.setCity("Shanghai"); System.out.println(person1.getAddress().getCity()); // 输出:Shanghai System.out.println(person2.getAddress().getCity()); // 输出:Shanghai ``` 2. 克隆 ```java public class Person implements Cloneable { private String name; private Address address; public Person(String name, Address address) { this.name = name; this.address = address; } public String getName() { return name; } public Address getAddress() { return address; } @Override public Object clone() throws CloneNotSupportedException { Person person = (Person) super.clone(); person.address = (Address) address.clone(); return person; } } public class Address implements Cloneable { private String city; public Address(String city) { this.city = city; } public String getCity() { return city; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } // 测试代码 Address address = new Address("Beijing"); Person person1 = new Person("Tom", address); Person person2 = (Person) person1.clone(); System.out.println(person1.getAddress().getCity()); // 输出:Beijing System.out.println(person2.getAddress().getCity()); // 输出:Beijing address.setCity("Shanghai"); System.out.println(person1.getAddress().getCity()); // 输出:Beijing System.out.println(person2.getAddress().getCity()); // 输出:Beijing ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值