• Java的深拷贝与浅拷贝


    鄙人写文章喜欢简洁点,希望用尽可能短的语句描述一个知识点

    1、概述

    拷贝的一个经典的使用场景:当前对象要传给其他多个方法使用,如果该对象在某一个方法中被修改,那么这个修改会影响到其他方法。 如果要避免这种影响,就需要给每一个方法都传入一个当前对象的拷贝。

    深与浅拷贝的区别就在于对复杂对象的处理:对于基本类型,浅拷贝、深拷贝都是拷贝的值;对于引用类型浅拷贝的是对象的引用。而深拷贝则是直接新建一个对象实例。

    注意浅拷贝中的复杂引用以及简单引用:对于简单引用,拷贝后的对象指向新的地址不会影响到原对象。复杂引用,拷贝后的对象将内部的对象指向新的地址,影响到原对象对应的内部对象。这里一定理解下,抓住“地址”指向去理解就好了。

    2、代码实现

    (1)浅拷贝实现方式:类实现Cloneable接口,并且重写clone()方法。详细见下面的代码

    (2)深拷贝:主要是针对类内部的复杂引用变量。两种方式:1)复杂引用也实现Cloneable,并重写clone()方法,然后在父对象重写的clone方法中将该复杂引用指向克隆后的引用  2)直接将需要克隆的对象进行序列化,然后反序列化就可以得到一个深拷贝的对象。

    当然这些工作都有现成的轮子了,借助于Apache Commons工具类可以直接实现:

    • 浅克隆:BeanUtils.cloneBean(Object obj);
    • 深克隆:SerializationUtils.clone(T object);

    当然可以直接使用

    2.1浅拷贝

    父子类

     1 @Data
     2 @Builder
     3 class Father implements Cloneable {
     4     Long age;
     5     StringBuilder name;
     6     Son son;
     7 
     8     @Override
     9     public Object clone() {
    10         //浅拷贝
    11         try {
    12             return super.clone();
    13         } catch (CloneNotSupportedException e) {
    14             e.printStackTrace();
    15             return null;
    16         }
    17     }
    18 }
    19 
    20 @Data
    21 @Builder
    22 class Son {
    23     Long age;
    24     String name;
    25     int grade;
    26 }

    初始赋值

    1 public static Son son = Son.builder().age(new Long(30L)).name("大儿子").grade(100).build();
    2 public static Father father = Father.builder().age(new Long(50L)).name("爸爸").son(son).build();

    浅拷贝示例:

      private static void shallowClone() {
            System.out.println("父亲年龄:" + father.getAge());
            System.out.println("父亲姓名:" + father.getName());
            System.out.println("儿子年龄:" + father.getSon().getAge());
            System.out.println("儿子姓名:" + father.getSon().getName());
            System.out.println("儿子分数:" + father.getSon().getGrade());
            //开始克隆
    
            Father fatherInLaw = (Father) father.clone();
            fatherInLaw.getSon().setAge(10L);
            fatherInLaw.getSon().setGrade(0);
            fatherInLaw.getSon().setName("继子");
            fatherInLaw.setAge(new Long(80L));
            fatherInLaw.setName("继父");
    
            //修改后结果
            System.out.println("==========浅拷贝后===========");
    
            System.out.println("父亲年龄:" + father.getAge());
            System.out.println("父亲姓名:" + father.getName());
            System.out.println("儿子年龄:" + father.getSon().getAge());
            System.out.println("儿子姓名:" + father.getSon().getName());
            System.out.println("儿子分数:" + father.getSon().getGrade());
        }

    结果输出

    父亲年龄:50
    父亲姓名:爸爸
    儿子年龄:30
    儿子姓名:大儿子
    儿子分数:100
    ==========浅拷贝后===========
    父亲年龄:50
    父亲姓名:爸爸
    儿子年龄:10
    儿子姓名:继子
    儿子分数:0

    2.2.深拷贝

    (1)子引用重写clone方法,并且在父类中改变子引用的指向

    父子类

    @Data
    @Builder
    class Father implements Cloneable {
        Long age;
        StringBuilder name;
        Son son;
    
        @Override
        public Object clone() {
            Father father = null;
            //浅拷贝
            try {
                father = (Father) super.clone();
                father.son = (Son) son.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
                return null;
            }
            return father;
        }
    }
    
    @Data
    @Builder
    class Son implements Cloneable {
        Long age;
        String name;
        int grade;
    
        @Override
        public Object clone() {
            //拷贝
            try {
                return super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    View Code

    赋初值

    1  public static Son son = Son.builder().age(new Long(30L)).name("大儿子").grade(100).build();
    2     public static Father father = Father.builder().age(new Long(50)).name(new StringBuilder("爸爸")).son(son).build();

    深拷贝示例:

    //深拷贝
        private static void deepClone() {
            System.out.println("父亲年龄:" + father.getAge());
            System.out.println("父亲姓名:" + father.getName());
            System.out.println("儿子年龄:" + father.getSon().getAge());
            System.out.println("儿子姓名:" + father.getSon().getName());
            System.out.println("儿子分数:" + father.getSon().getGrade());
            //开始克隆
    
            Father fatherInLaw = (Father) father.clone();
            fatherInLaw.getSon().setAge(10L);
            fatherInLaw.getSon().setGrade(0);
            fatherInLaw.getSon().setName("继子");
            fatherInLaw.setAge(new Long(80L));
            fatherInLaw.setName(new StringBuilder("继父"));
    
            //修改前
            System.out.println("==========浅拷贝后===========");
    
            System.out.println("父亲年龄:" + father.getAge());
            System.out.println("父亲姓名:" + father.getName());
            System.out.println("儿子年龄:" + father.getSon().getAge());
            System.out.println("儿子姓名:" + father.getSon().getName());
            System.out.println("儿子分数:" + father.getSon().getGrade());
        }

    结果

    父亲年龄:50
    父亲姓名:爸爸
    儿子年龄:30
    儿子姓名:大儿子
    儿子分数:100
    ==========浅拷贝后===========
    父亲年龄:50
    父亲姓名:爸爸
    儿子年龄:30
    儿子姓名:大儿子
    儿子分数:100

    (2)序列化方式

    父子类

    @Data
    @Builder
    class Father implements Serializable {
        Long age;
        StringBuilder name;
        Son son;
    
        public Object deepClone() throws IOException, ClassNotFoundException {
            //序列化
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            //反序列化
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
    
            return ois.readObject();
        }
    }
    
    @Data
    @Builder
    class Son implements Serializable {
        Long age;
        String name;
        int grade;
    }
    View Code

    实现方法

     //深拷贝
        private static void deepCloneV2() throws IOException, ClassNotFoundException {
            System.out.println("父亲年龄:" + father.getAge());
            System.out.println("父亲姓名:" + father.getName());
            System.out.println("儿子年龄:" + father.getSon().getAge());
            System.out.println("儿子姓名:" + father.getSon().getName());
            System.out.println("儿子分数:" + father.getSon().getGrade());
            //开始克隆
    
            Father fatherInLaw = (Father) father.deepClone();
            fatherInLaw.getSon().setAge(10L);
            fatherInLaw.getSon().setGrade(0);
            fatherInLaw.getSon().setName("继子");
            fatherInLaw.setAge(new Long(80L));
            fatherInLaw.setName(new StringBuilder("继父"));
    
            //修改前
            System.out.println("==========浅拷贝后===========");
    
            System.out.println("父亲年龄:" + father.getAge());
            System.out.println("父亲姓名:" + father.getName());
            System.out.println("儿子年龄:" + father.getSon().getAge());
            System.out.println("儿子姓名:" + father.getSon().getName());
            System.out.println("儿子分数:" + father.getSon().getGrade());
        }

    结果:

    父亲年龄:50
    父亲姓名:爸爸
    儿子年龄:30
    儿子姓名:大儿子
    儿子分数:100
    ==========浅拷贝后===========
    父亲年龄:50
    父亲姓名:爸爸
    儿子年龄:30
    儿子姓名:大儿子
    儿子分数:100

    3、使用Apach  Commons

    3.1实现深拷贝,需要实现序列化接口,SerializationUtils.clone(T object);

    父子类

    @Data
    @Builder
    class Father implements Serializable{
        Long age;
        StringBuilder name;
        Son son;
    }
    
    @Data
    @Builder
    class Son implements Serializable{
        Long age;
        String name;
        int grade;
    }
    View Code

    实现方法

     //深拷贝
        private static void deepCloneCommons() throws IOException, ClassNotFoundException {
            System.out.println("父亲年龄:" + father.getAge());
            System.out.println("父亲姓名:" + father.getName());
            System.out.println("儿子年龄:" + father.getSon().getAge());
            System.out.println("儿子姓名:" + father.getSon().getName());
            System.out.println("儿子分数:" + father.getSon().getGrade());
            //开始克隆
    
            Father fatherInLaw = (Father) SerializationUtils.clone(father);
            fatherInLaw.getSon().setAge(10L);
            fatherInLaw.getSon().setGrade(0);
            fatherInLaw.getSon().setName("继子");
            fatherInLaw.setAge(new Long(80L));
            fatherInLaw.setName(new StringBuilder("继父"));
    
            //修改前
            System.out.println("==========浅拷贝后===========");
    
            System.out.println("父亲年龄:" + father.getAge());
            System.out.println("父亲姓名:" + father.getName());
            System.out.println("儿子年龄:" + father.getSon().getAge());
            System.out.println("儿子姓名:" + father.getSon().getName());
            System.out.println("儿子分数:" + father.getSon().getGrade());
        }
    View Code

    结果

    父亲年龄:50
    父亲姓名:爸爸
    儿子年龄:30
    儿子姓名:大儿子
    儿子分数:100
    ==========深拷贝后===========
    父亲年龄:50
    父亲姓名:爸爸
    儿子年龄:30
    儿子姓名:大儿子
    儿子分数:100

    3.2使用工具类实现浅拷贝

    BeanUtils.copyProperties(Object dest, Object orig)直接调用即可实现浅拷贝,BeanUtils.cloneBean(Object obj)最终也是调用的copyProperties方法。

    今日学习笔记到此结束。

    书于:2019/08/01/ 22:40分

  • 相关阅读:
    在VS中用CLAPACK解决广义特征值问题
    再议:__cdecl与__stdcall 调用约定在动态链接库调用中不同的表现
    类成员析构、虚析构函数、动态生成对象相关的 关于析构顺序的杂谈
    C++ 中dynamic_cast的使用方法
    函数传值 复制构造函数 深度拷贝
    hdoj_1867A + B for you again
    如何判断一个数是否为素数
    hdoj_2087剪花布条
    STL容器之优先队列
    hdoj_4006优先队列的使用
  • 原文地址:https://www.cnblogs.com/yuerugou54/p/11285842.html
Copyright © 2020-2023  润新知