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

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方法,并且在父类中改变子引用的指向

父子类

java 深拷贝浅拷贝 java什么是深拷贝和浅拷贝_浅拷贝

java 深拷贝浅拷贝 java什么是深拷贝和浅拷贝_深拷贝_02

@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)序列化方式

父子类

java 深拷贝浅拷贝 java什么是深拷贝和浅拷贝_浅拷贝

java 深拷贝浅拷贝 java什么是深拷贝和浅拷贝_深拷贝_02

@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);

父子类

java 深拷贝浅拷贝 java什么是深拷贝和浅拷贝_浅拷贝

java 深拷贝浅拷贝 java什么是深拷贝和浅拷贝_深拷贝_02

@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

实现方法

java 深拷贝浅拷贝 java什么是深拷贝和浅拷贝_浅拷贝

java 深拷贝浅拷贝 java什么是深拷贝和浅拷贝_深拷贝_02

//深拷贝
    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分