clone(deep, shallow)

     In the actual programming process, we often encounter this situation: there is an object A, which already contains some valid values ​​at a certain time, and a new object B that is exactly the same as A may be required at this time, and then the Any changes to B will not affect the value in A, that is, A and B are two independent objects, but the initial value of B is determined by the A object. In the Java language, this requirement cannot be met with a simple assignment statement, and there are many ways to meet this requirement.
 
    How clone is implemented
 
       1. Shallow cloning
    
       Shallow clone For the object to be cloned, for the attributes of its basic data type, copy a copy to the newly generated object, and for the attributes of non-basic data types, only copy a reference to the newly generated object, that is, the newly generated object and properties of non-primitive data types in the original object all point to the same object.
 
       Shallow clone steps:
       1. Implement the java.lang.Cloneable interface
         Why should the class to be cloned implement the Cloneable interface? The Cloneable interface is an identity interface and does not contain any methods! This identifier is only for the clone() method in the Object class. If the clone class does not implement the Cloneable interface and calls the clone() method of the Object (that is, the super.Clone() method is called), then the clone() method of the Object is called. method will throw CloneNotSupportedException exception.
 
      2. Rewrite the java.lang.Object.clone() method
       The JDK API documentation explains that this method will return a copy of the Object object. There are two points to note: First, copying an object returns a new object, not a reference. Second, the difference between a copied object and a new object returned by the new operator is that the copy already contains some information about the original object, not the initial information of the object.
 
      观察一下Object类的clone()方法是一个native方法,native方法的效率一般来说都是远高于java中的非native方法。这也解释了为什么要用Object中clone()方法而不是先new一个类,然后把原始对象中的信息赋到新对象中,虽然这也实现了clone功能。 Object类中的clone()还是一个protected属性的方法,重写之后要把clone()方法的属性设置为public
 
Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。
 
Java代码实例:
public class Product implements Cloneable {   
    private String name;   
  
    public Object clone() {   
        try {   
            return super.clone();   
        } catch (CloneNotSupportedException e) {   
            return null;   
        }   
    }   
}  
 
 
   二、深度克隆
     
     在浅度克隆的基础上,对于要克隆的对象中的非基本数据类型的属性对应的类,也实现克隆,这样 对于非基本数据类型的属性,复制的不是一份引用,即新产生的对象和原始对象中的非基本数据类型的属性指向的不是同一个对象
 
       深度克隆步骤:
       要克隆的类和 类中所有非基本数据类型的属性对应的类
        1、都实现java.lang.Cloneable接口
        2、都重写java.lang.Object.clone()方法
 
Java代码实例:
public class Attribute implements Cloneable {   
    private String no;   
       
    public Object clone() {   
        try {   
            return super.clone();   
        } catch (CloneNotSupportedException e) {   
            return null;   
        }   
    }   
}   
  
public class Product implements Cloneable {   
    private String name;   
       
    private Attribute attribute;   
  
    public Object clone() {   
        try {   
            return super.clone();   
        } catch (CloneNotSupportedException e) {   
            return null;   
        }   
    }   
}   
 
 
      三、使用对象序列化和反序列化实现深度克隆
        所谓对象序列化就是将对象的状态转换成字节流,以后可以通过这些值再生成相同状态的对象。
 
对象的序列化还有另一个容易被大家忽略的功能就是对象复制(Clone),Java中通过Clone机制可以复制大部分的对象,但是众所周知,Clone有深度Clone和浅度Clone,如果你的对象非常非常复杂,并且想实现深层 Clone,如果使用序列化,不会超过10行代码就可以解决。
 
虽然Java的序列化非常简单、强大,但是要用好,还有很多地方需要注意。比如曾经序列化了一个对象,可由于某种原因,该类做了一点点改动,然后重新被编译,那么这时反序列化刚才的对象,将会出现异常。 你可以通过添加serialVersionUID属性来解决这个问题(在 Java序列化与反序列化学习(二):序列化接口说明 的标题三中有此说明 )。 如果你的类是个单例(Singleton)类,虽然我们多线程下的并发问题来控制单例,但是,是否允许用户通过序列化机制或者克隆来复制该类,如果不允许你需要谨慎对待该类的实现(让此类不用实现Serializable接口或Externalizable接口和 Cloneable接口 就行了)
  
Java代码实例:
public class Attribute {   
    private String no;   
}   
  
public class Product {   
    private String name;   
       
    private Attribute attribute;   
  
    public Product clone() {   
        ByteArrayOutputStream byteOut = null;   
        ObjectOutputStream objOut = null;   
        ByteArrayInputStream byteIn = null;   
        ObjectInputStream objIn = null;   
           
        try {   
// 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
            byteOut = new ByteArrayOutputStream();    
            objOut = new ObjectOutputStream(byteOut);    
            objOut.writeObject(this);   
  // 将流序列化成对象
            byteIn = new ByteArrayInputStream(byteOut.toByteArray());   
            objIn = new ObjectInputStream(byteIn);   
               
            return (ContretePrototype) objIn.readObject();   
        } catch (IOException e) {   
            throw new RuntimeException("Clone Object failed in IO.",e);      
        } catch (ClassNotFoundException e) {   
            throw new RuntimeException("Class not found.",e);      
        } finally{   
            try{   
                byteIn = null;   
                byteOut = null;   
                if(objOut != null) objOut.close();      
                if(objIn != null) objIn.close();      
            }catch(IOException e){      
            }      
        }   
    }   
或者:
[java]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. public static <T extends Serializable> T copy(T input) {  
  2.     ByteArrayOutputStream baos = null;  
  3.     ObjectOutputStream oos = null;  
  4.     ByteArrayInputStream bis = null;  
  5.     ObjectInputStream ois = null;  
  6.     try {  
  7.         baos = new ByteArrayOutputStream();  
  8.         oos = new ObjectOutputStream(baos);  
  9.         oos.writeObject(input);  
  10.         oos.flush();  
  11.   
  12.         byte[] bytes = baos.toByteArray();  
  13.         bis = new ByteArrayInputStream(bytes);  
  14.         ois = new ObjectInputStream(bis);  
  15.         Object result = ois.readObject();  
  16.         return (T) result;  
  17.     } catch (IOException e) {  
  18.         throw new IllegalArgumentException("Object can't be copied", e);  
  19.     } catch (ClassNotFoundException e) {  
  20.         throw new IllegalArgumentException("Unable to reconstruct serialized object due to invalid class definition", e);  
  21.     } finally {  
  22.         closeQuietly(oos);  
  23.         closeQuietly(baos);  
  24.         closeQuietly(bis);  
  25.         closeQuietly(ois);  
  26.     }  
  27. }  
 
也可以用json等其他序列化技术实现深度复制;

#########################注意######################
Bean复制的几种框架中(Apache BeanUtils、PropertyUtils,Spring BeanUtils,Cglib BeanCopier)都是相当于克隆中的浅克隆。
1)spring包和Apache中的 BeanUtils     采用反射实现
     Spring: void copyProperties(Object source, Object target,String[] ignoreProperties)
    Apache:void  copyProperties(Object dest, Object orig)
2)cglib包中的  Beancopier   采用动态字节码实现
    cglib: BeanCopier create(Class source, Class target,boolean useConverter)
   例如:
          BeanCopier copier =BeanCopier.create(stuSource.getClass(), stuTarget.getClass(), false);
          copier.copy(stuSource, stuTarget, null);
 
公司内部对用到的bean属性复制做了下性能分析:
cglib   BeanCopier   15ms
 
Spring  BeanUtil      4031ms
 
apache BeanUtils      18514ms.
#########################注意######################

clone和BeanUtils.copyProperties对比:

Cloning is done by you. If the instance which you trying clone contains reference of another instance you have to write cloning code to that one too. What if the instances contains chain of references to other instances? So if you do cloning by yourself, there are chances that you might miss a small detail.

BeanUtils.copyProperties on otherhand take care of everything by itself. It reduces your effort.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326344577&siteId=291194637