• 深克隆


    如果我们有一个对象a,我们想得到它的一个克隆,那么我们该怎么做呢?最直观、最笨的方法是我们先new一个a的同类对象b,然后挨个拷贝a的属性给b中的相应属性。那么,这里马上就得引出两个概念:浅克隆与深克隆。

    如果用直白的、非严格定义的语言来解释这两个概念,那么可以这么说:

    所谓浅克隆是指复制一个对象的实例,但是这个对象中包含的其它的对象还是共用的。

    所谓深克隆是指复制一个对象的实例,而且这个对象中包含的其它的对象也要复制一份。

    如果我们要深克隆一个对象,而这个对象又比较复杂,它还包含n多其它对象的引用,那么要用我们开始说的那种克隆方法,简直是要人命! 即便是我们只要一个浅克隆的对象,如果这个对象有几十上百个基本属性,我们挨个去复制也是不可接受的!

    那么,我们先来看看java中浅克隆一个对象可以怎么做。

    Object 类提供有一个clone方法,它的方法签名如下:

     

    [java] view plain copy
     
    1. protected native Object clone() throws CloneNotSupportedException;  


    可以看到:(1)它是一个native方法。native方法的效率一般来说都远高于非native方法。

     

                        (2)它是一个protected方法。

                        (3)它返回一个Object。

    如果我们要在其它类中调用clone方法,那么我们就要重写这个方法,将它的方法属性改为public 。这个方法其实就提供了浅克隆的功能。

    验证的代码如下:

    Swallow.java:

     

    [java] view plain copy
     
    1. package com.myclone.test;  
    2.   
    3.   
    4. public class Swallow implements Cloneable{  
    5.   
    6.   
    7.     private String name;  
    8.     private Wing leftWing;  
    9.     private Wing rightWing;  
    10.       
    11.     public Swallow(String name, Wing leftWing, Wing rightWing){  
    12.         this.name = name;  
    13.         this.leftWing = leftWing;  
    14.         this.rightWing = rightWing;  
    15.     }  
    16.   
    17.     public String getName() {  
    18.         return name;  
    19.     }  
    20.   
    21.     public void setName(String name) {  
    22.         this.name = name;  
    23.     }  
    24.   
    25.     public Wing getLeftWing() {  
    26.         return leftWing;  
    27.     }  
    28.   
    29.     public void setLeftWing(Wing leftWing) {  
    30.         this.leftWing = leftWing;  
    31.     }  
    32.   
    33.     public Wing getRightWing() {  
    34.         return rightWing;  
    35.     }  
    36.   
    37.     public void setRightWing(Wing rightWing) {  
    38.         this.rightWing = rightWing;  
    39.     }  
    40.       
    41.     @Override  
    42.     protected Object clone() throws CloneNotSupportedException {  
    43.         // TODO 自动生成的方法存根  
    44.         return super.clone();  
    45.     }  
    46. }  


    Wing.java:

     

     

    [java] view plain copy
     
    1. package com.myclone.test;  
    2.   
    3. public class Wing{  
    4.   
    5.     private int width;  
    6.   
    7.     public Wing(int width){  
    8.         this.width = width;  
    9.     }  
    10.       
    11.     public int getWidth() {  
    12.         return width;  
    13.     }  
    14.   
    15.     public void setWidth(int width) {  
    16.         this.width = width;  
    17.     }  
    18. }  


    Main.java:

     

     

    [java] view plain copy
     
    1. package com.myclone.test;  
    2.   
    3. public class Main {  
    4.   
    5.     public static void main(String[] args) throws CloneNotSupportedException {  
    6.         // TODO 自动生成的方法存根  
    7.   
    8.         Swallow s1 = new Swallow("大雁a", new Wing(10), new Wing(10));  
    9.         Swallow s2 = (Swallow) s1.clone();  
    10.           
    11.         System.out.println("(s1==s2)="+ (s1==s2));  
    12.         System.out.println("s1.name="+ s1.getName()+", s2.name="+s2.getName());  
    13.         System.out.println("(s1.leftWing==s2.leftWing) = "+(s1.getLeftWing()==s2.getLeftWing()));  
    14.         System.out.println("(s1.rightWing==s2.rightWing) = "+(s1.getRightWing()==s2.getRightWing()));      
    15.     }  
    16. }  


    运行结果:

     

    (s1==s2)=false s1.name=大雁a, s2.name=大雁a (s1.leftWing==s2.leftWing) = true (s1.rightWing==s2.rightWing) = true

    从运行结果中我们可以看出,调用 s1.clone() 方法我们得到的是另一个对象,它浅克隆了s1,因为s1和s2中的对象属性 leftWing 指向的是同一个实例,rightWing指向的是同一个实例。注意:要调用clone方法,必须要实现 Cloneable接口,在我们这个例子中即Swallow实现了Cloneable接口。

     

    如果我们需要深克隆的对象呢?首先我们想到的是,可以再次重写Swallow中的clone方法,将leftWing 和 rightWing 克隆一份。那么Wing 也要重写它的clone方法,改写后的Wing类:

     

    [java] view plain copy
     
    1. package com.myclone.test;  
    2.   
    3. public class Wing implements Cloneable{  
    4.   
    5.     private int width;  
    6.   
    7.     public Wing(int width){  
    8.         this.width = width;  
    9.     }  
    10.       
    11.     public int getWidth() {  
    12.         return width;  
    13.     }  
    14.   
    15.     public void setWidth(int width) {  
    16.         this.width = width;  
    17.     }  
    18.       
    19.     @Override  
    20.     protected Object clone() throws CloneNotSupportedException {  
    21.         // TODO 自动生成的方法存根  
    22.         return super.clone();  
    23.     }  
    24. }  


    改写后的Swallow类:

     

     

    [java] view plain copy
     
    1. package com.myclone.test;  
    2.   
    3.   
    4. public class Swallow implements Cloneable{  
    5.   
    6.   
    7.     private String name;  
    8.     private Wing leftWing;  
    9.     private Wing rightWing;  
    10.       
    11.     public Swallow(String name, Wing leftWing, Wing rightWing){  
    12.         this.name = name;  
    13.         this.leftWing = leftWing;  
    14.         this.rightWing = rightWing;  
    15.     }  
    16.   
    17.     public String getName() {  
    18.         return name;  
    19.     }  
    20.   
    21.     public void setName(String name) {  
    22.         this.name = name;  
    23.     }  
    24.   
    25.     public Wing getLeftWing() {  
    26.         return leftWing;  
    27.     }  
    28.   
    29.     public void setLeftWing(Wing leftWing) {  
    30.         this.leftWing = leftWing;  
    31.     }  
    32.   
    33.     public Wing getRightWing() {  
    34.         return rightWing;  
    35.     }  
    36.   
    37.     public void setRightWing(Wing rightWing) {  
    38.         this.rightWing = rightWing;  
    39.     }  
    40.       
    41. //  @Override  
    42. //  protected Object clone() throws CloneNotSupportedException {  
    43. //      // TODO 自动生成的方法存根  
    44. //      return super.clone();  
    45. //  }  
    46.       
    47.     @Override  
    48.     protected Object clone() throws CloneNotSupportedException {  
    49.         // TODO 自动生成的方法存根  
    50.         Swallow swallow = (Swallow) super.clone();  
    51.         Wing lefWing = (Wing) swallow.getLeftWing().clone();  
    52.         Wing rightWing = (Wing) swallow.getRightWing().clone();  
    53.         swallow.setLeftWing(lefWing);  
    54.         swallow.setRightWing(rightWing);  
    55.         return swallow;  
    56.     }  
    57. }  


    再次运行Main,得到的结果是:

     

    (s1==s2)=false s1.name=大雁a, s2.name=大雁a (s1.leftWing==s2.leftWing) = false (s1.rightWing==s2.rightWing) = false
    可以看出s2已经是s1的深克隆了。这种方法有个缺点就是,如果Wing还含有对象属性的话,那么我们就必须依次去重写属性类的clone方法!

    另一种方法是通过序列化和反序列化来实现深克隆。这种方法实际上就是“先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流中,再从流中读出来,便可以重建对象。” ——java.lang.Object.clone()分析

    要注意的是所有相关的类都要实现Serializable接口,改写后的实例:

    Wing.java

     

    [java] view plain copy
     
    1. package com.myclone.test;  
    2.   
    3. import java.io.Serializable;  
    4.   
    5. public class Wing implements Cloneable, Serializable{  
    6.   
    7.     /** 
    8.      *  
    9.      */  
    10.     private static final long serialVersionUID = -6757262538695878009L;  
    11.     private int width;  
    12.   
    13.     public Wing(int width){  
    14.         this.width = width;  
    15.     }  
    16.       
    17.     public int getWidth() {  
    18.         return width;  
    19.     }  
    20.   
    21.     public void setWidth(int width) {  
    22.         this.width = width;  
    23.     }  
    24.       
    25.     @Override  
    26.     protected Object clone() throws CloneNotSupportedException {  
    27.         // TODO 自动生成的方法存根  
    28.         return super.clone();  
    29.     }  
    30. }  


    Swallow.java

     

     

    [java] view plain copy
     
    1. package com.myclone.test;  
    2.   
    3. import java.io.ByteArrayInputStream;  
    4. import java.io.ByteArrayOutputStream;  
    5. import java.io.IOException;  
    6. import java.io.ObjectInputStream;  
    7. import java.io.ObjectOutputStream;  
    8. import java.io.Serializable;  
    9.   
    10.   
    11. public class Swallow implements Cloneable, Serializable{  
    12.   
    13.   
    14.     /** 
    15.      *  
    16.      */  
    17.     private static final long serialVersionUID = -2152309743441152834L;  
    18.     private String name;  
    19.     private Wing leftWing;  
    20.     private Wing rightWing;  
    21.       
    22.     public Swallow(String name, Wing leftWing, Wing rightWing){  
    23.         this.name = name;  
    24.         this.leftWing = leftWing;  
    25.         this.rightWing = rightWing;  
    26.     }  
    27.   
    28.     public String getName() {  
    29.         return name;  
    30.     }  
    31.   
    32.     public void setName(String name) {  
    33.         this.name = name;  
    34.     }  
    35.   
    36.     public Wing getLeftWing() {  
    37.         return leftWing;  
    38.     }  
    39.   
    40.     public void setLeftWing(Wing leftWing) {  
    41.         this.leftWing = leftWing;  
    42.     }  
    43.   
    44.     public Wing getRightWing() {  
    45.         return rightWing;  
    46.     }  
    47.   
    48.     public void setRightWing(Wing rightWing) {  
    49.         this.rightWing = rightWing;  
    50.     }  
    51.       
    52. //  @Override  
    53. //  protected Object clone() throws CloneNotSupportedException {  
    54. //      // TODO 自动生成的方法存根  
    55. //      return super.clone();  
    56. //  }  
    57.       
    58.     @Override  
    59.     protected Object clone() throws CloneNotSupportedException {  
    60.         // TODO 自动生成的方法存根  
    61.         Swallow swallow = (Swallow) super.clone();  
    62.         Wing lefWing = (Wing) swallow.getLeftWing().clone();  
    63.         Wing rightWing = (Wing) swallow.getRightWing().clone();  
    64.         swallow.setLeftWing(lefWing);  
    65.         swallow.setRightWing(rightWing);  
    66.         return swallow;  
    67.     }  
    68.       
    69.     /** 
    70.      * 深克隆 
    71.      * @return 
    72.      * @throws IOException 
    73.      * @throws ClassNotFoundException 
    74.      */  
    75.     public Object deepClone() throws IOException, ClassNotFoundException{  
    76.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
    77.         ObjectOutputStream oos = new ObjectOutputStream(baos);  
    78.         oos.writeObject(this);  
    79.           
    80.         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());  
    81.         ObjectInputStream ois = new ObjectInputStream(bais);  
    82.         return ois.readObject();  
    83.     }  
    84. }  


    Main.java:

     

     

    [java] view plain copy
     
    1. package com.myclone.test;  
    2.   
    3. import java.io.IOException;  
    4.   
    5. public class Main {  
    6.   
    7.     public static void main(String[] args) throws CloneNotSupportedException, ClassNotFoundException, IOException {  
    8.         // TODO 自动生成的方法存根  
    9.   
    10.         Swallow s1 = new Swallow("大雁a", new Wing(10), new Wing(10));  
    11.         //Swallow s2 = (Swallow) s1.clone();  
    12.         Swallow s2 = (Swallow) s1.deepClone();  
    13.           
    14.         System.out.println("(s1==s2)="+ (s1==s2));  
    15.         System.out.println("s1.name="+ s1.getName()+", s2.name="+s2.getName());  
    16.         System.out.println("(s1.leftWing==s2.leftWing) = "+(s1.getLeftWing()==s2.getLeftWing()));  
    17.         System.out.println("(s1.rightWing==s2.rightWing) = "+(s1.getRightWing()==s2.getRightWing()));      
    18.     }  
    19. }  


    运行结果:

     

    (s1==s2)=false s1.name=大雁a, s2.name=大雁a (s1.leftWing==s2.leftWing) = false (s1.rightWing==s2.rightWing) = false

     

    由此可见,通过序列化和反序列化来实现深克隆是最优雅、最方便的。

    2015.09.02  补充:

    上午有个同事说深克隆还有一个方法,就是用json,这的确是一个好方法!那么,我们可以在Swallow 类中再加一个deepClone2方法,效果是一样的:

     

    [java] view plain copy
     
    1. public Swallow deepClone2(){  
    2.         Gson gson = new Gson();  
    3.         String json = gson.toJson(this);  
    4.         return gson.fromJson(json, this.getClass());  
    5.     }  

    似乎更简单一些~ 不过当时我就在想这样效率是否会低一些,测试了下10万次克隆,结果如下:

     

    deepClone 方法耗时=1564 deepClone2 方法耗时=4029

  • 相关阅读:
    [Linux/Apache Http]Apache Http(d)服务访问时报: 403 Forbidden You don't have permission to access /cdh/ on this server.
    [Linux]常用命令之【YUM】
    .Netcore HttpClient源码探究
    echarts使用多图的表达
    记录composer 安装 yii2项目
    IE浏览器js parseInt("08")返回值不是8
    将一个条件从else语句中拆分出来导致的bug
    我的第一次WebService接口开发教程
    Oracle以逗号分隔的字符串拆分为多行数据
    JS 小技巧
  • 原文地址:https://www.cnblogs.com/lxaic/p/5762034.html
Copyright © 2020-2023  润新知