zoukankan      html  css  js  c++  java
  • Java深层复制方式

    为什么需要深层复制

    Object 的 clone() 方法是浅层复制(但是 native 很高效)。
    另外,Java 提供了数组和集合的复制方法,分别是 Arrays.copy() 和 Collections.copy() 方法。
    前者实际上使用了 System.arraycopy() 方法,两者其实也是浅层复制,过程类似于下面的 for 循环:

    for(int i=0; i<len; i++){
      dest[i] = src[i];
    }

    所以当数组或集合中元素是对象时,只是做了引用的复制,指向的还是堆中同一个对象。

    一般有两种深层复制方案

    1)实现 Cloneable 接口

    包装 Object.clone() ,根据属性类型深度 clone。

    这种方法,使用了 Object.clone() ,优点是 native 方法性能好,缺点是实现太繁琐。

    /**
     * 0 实现 Cloneable 接口
     * 1 包装 super.clone(),提供 public 方法
     * 2 默认的是浅层复制
     * @author Super
     *
     */
    public class shallowCloneTest {
    
        public static void main(String[] args) {
            Resource0 r0 = new Resource0(0, "资源1号");
            Resource0 r1 = new Resource0(1, "内部资源");
            r0.setInnerResource(r1);
            
            //验证克隆
            Resource0 r2 = r0.shallowClone();
            System.out.println(r0);
            System.out.println(r2);
            System.out.println(r1==r2); //false
            
            //验证浅度克隆
            r2.getInnerResource().setId(7);
            System.out.println(r1); //受影响了
        }
    
    }
    
    /**
     * 深层复制方案一:包装 clone,引用变量继续 clone
     * @author Super
     *
     */
    public class DeepCloneTest1 {
    
        public static void main(String[] args) {
            Resource0 r0 = new Resource0(0, "资源1号");
            Resource0 r1 = new Resource0(1, "内部资源");
            r0.setInnerResource(r1);
            
            //验证克隆
            Resource0 r2 = r0.deepClone();
            System.out.println(r0);
            System.out.println(r2);
            System.out.println(r1==r2); //false
            
            //验证深度度克隆
            r2.getInnerResource().setId(7);
            System.out.println(r1); //不受影响
        }
    
    }
    
    
    public class Resource0 extends BaseVo implements Cloneable {
    
        private static final long serialVersionUID = 1L;
    
        private Integer id;
        private String name;
        private Resource0 innerResource;
    
        public Resource0(Integer id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Resource0 getInnerResource() {
            return innerResource;
        }
    
        public void setInnerResource(Resource0 innerResource) {
            this.innerResource = innerResource;
        }
        
        /**
         * 浅层复制
         * @return
         */
        public Resource0 shallowClone(){
            Resource0 r = null;
            try {
                r = (Resource0)super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return r;
        }
        
        /**
         * 深层复制
         * @return
         */
        public Resource0 deepClone(){
            Resource0 r = null;
            try {
                r = (Resource0)super.clone();
                r.innerResource = (Resource0) innerResource.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return r;
        }
    
        @Override
        public String toString() {
            return "Resource0 [id=" + id + ", name=" + name + ", innerResource="
                    + innerResource + "]";
        }
    }
    View Code

    2)对象序列化输入输出

    这种方法,强大且简单,先写到内存,再读出来。

    PS:单例对象最好也关注下是否有被序列化复制的风险。

    /**
     * 深层复制方案2:输入输出序列化
     * @author Super
     *
     */
    public class DeepCloneTest2 {
    
        public static void main(String[] args) {
            Resource0 r0 = new Resource0(0, "资源1号");
            Resource0 r1 = new Resource0(1, "内部资源");
            r0.setInnerResource(r1);
            
            //验证克隆
            Resource0 r2 = (Resource0) IOUtil.deepClone(r0);
            System.out.println(r0);
            System.out.println(r2);
            System.out.println(r1==r2); //false
            
            //验证深度度克隆
            r2.getInnerResource().setId(7);
            System.out.println(r1); //不受影响
        }
    
    }
    
    
    /**
         * 深层复制序列化 vo
         * @param src
         * @return dest
         * @throws IOException 
         * @throws ClassNotFoundException 
         */
        public static BaseVo deepClone(BaseVo src) {
            ByteArrayOutputStream bo = null;
            ObjectOutputStream out = null;
            ObjectInputStream in = null;
            BaseVo dest = null;
            try{
                try{
                    //对象写入内存
                    bo = new ByteArrayOutputStream();
                    out = new ObjectOutputStream(bo);
                    out.writeObject(src);
                    //从内存中读回来
                    in = new ObjectInputStream(new ByteArrayInputStream(bo.toByteArray()));
                    dest = (BaseVo) in.readObject();
                }finally{
                    //使用 finally 关闭资源
                    if(in!=null){
                        in.close();
                    }
                    if(out!=null){
                        out.close();
                    }
                    if(bo!=null){
                        bo.close();
                    }
                }
                //使用 catch 块统一捕捉资源
            } catch(IOException | ClassNotFoundException ex){
                ex.printStackTrace();
            }
            
            return dest;
        }
    View Code

    参考阅读:

    https://www.jianshu.com/p/7aaaf884cc44

  • 相关阅读:
    HDU
    POJ
    POJ
    POJ
    POJ
    POJ
    POJ
    SPFA算法——最短路径
    POJ1251 Jungle Roads Kruskal+scanf输入小技巧
    MongoDB--关于数据库及选择MongoDB的原因
  • 原文地址:https://www.cnblogs.com/noodlerkun/p/10440610.html
Copyright © 2011-2022 走看看