浅拷贝深拷贝

2020-07-27  本文已影响0人  帮我的鸵鸟盖个章

浅拷贝:

  1. 对基本数据类型以及的String类型的成员变量,仅进行值传递,所以对其中一个对象的该成员对象进行修改,并不会影响到另一个对象拷贝得到的数据;
  2. 对数组,类的对象等成员变量,浅拷贝会将成员变量的内存地址复制一份给新的对象。所以实际上拷贝前后的两个对象,其数组、类的对象等成员变量都指向的同一个实例。所以对其中一个对象的该成员对象的修改会影响到另一个对象的该成员变量值。

以下情况均是对象的浅拷贝:

  1. 拷贝构造函数
  2. 顶级类重写clone()方法

深拷贝:

​ 对的理解:有一个类有一个成员变量A,A又有一个成员变量B,B又有一个成员变量C....直到一个确定的实例,这样就形成了一个深度,这是一层意思。另一层意思是,在这个有深度的各个成员变量中,都实现Cloneable接口并且重写clone()方法。即:每一层对象都实现浅拷贝即可实现深拷贝。

  1. 深拷贝是对整个对象的对象图进行拷贝(参考上面对深的理解);

以下情况均是对象的深拷贝:

  1. 对象图中所有对象均重写clone()方法
  2. 通过对象序列化实现深拷贝

下面以 重写clone()方法来做一些例子。

新建一个类Clazz

package com.mayn.spring;

/**
 * TODO
 *
 * @ClassName: Clazz
 * @author: yihong
 * @since: 2020/7/27 11:27
 */
public class Clazz {
    private String clazzName;
    private Integer clazzNo;

    public void setClazzName(String clazzName) {
        this.clazzName = clazzName;
    }

    public void setClazzNo(Integer clazzNo) {
        this.clazzNo = clazzNo;
    }

    public Clazz() {
    }

    public Clazz(String clazzName, Integer clazzNo) {
        this.clazzName = clazzName;
        this.clazzNo = clazzNo;
    }

    @Override
    public String toString(){
        return "[clazz:" + this.hashCode() + ",clazzName:" + this.clazzName + ",clazzNo:" + this.clazzNo +"]";
    }
}

新建一个类Studeng,里面包含成员变量Clazz

package com.mayn.spring;

/**
 * TODO
 *
 * @ClassName: Student
 * @author: yihong
 * @since: 2020/7/27 11:26
 */
public class Student implements Cloneable{
    private String name;
    private Integer age;
    private Clazz clazz;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Clazz getClazz() {
        return clazz;
    }

    public void setClazz(Clazz clazz) {
        this.clazz = clazz;
    }

    public Student() {
    }

    public Student(String name, Integer age, Clazz clazz) {
        this.name = name;
        this.age = age;
        this.clazz = clazz;
    }

    @Override
    public String toString(){
        return "[student:" + this.hashCode() + ",name:" + this.name + ",age:" + this.age + ",clazz:" + this.clazz + "]";
    }
}

使用=,将一个对象赋给新的对象
public class shallowCopy {
    public static void main(String[] args) {
        Clazz clazz = new Clazz("最强一班",18);
        Student studentA = new Student("学生A",11,clazz);
        System.out.println("studentA:" + studentA.toString());
        System.out.println("开始进行=");
        Student studentB = studentA;
        System.out.println("新对象改了个年纪--->99");
        studentB.setAge(99);
        System.out.println("新对象改了个名字--->张三");
        studentB.setName("张三");
        System.out.println("新对象改了班级的名字-->最最最最最12班");
        studentB.getClazz().setClazzName("最最最最最12班");
        System.out.println("新对象改了班级号--->9527");
        studentB.getClazz().setClazzNo(9527);
        System.out.println("=后,改变了B的我们:");
        System.out.println("studentA:" + studentA.toString());
        System.out.println("studentB:" + studentB.toString());
        studentA.setName("张三三");
        studentA.getClazz().setClazzNo(9527999);
        System.out.println("=后,改变了A的我们:");
        System.out.println("studentA:" + studentA.toString());
        System.out.println("studentB:" + studentB.toString());
    }
}

打印结果:

studentA:[student:463345942,name:学生A,age:11,clazz:[clazz:195600860,clazzName:最强一班,clazzNo:18]]
开始进行=。
新对象改了个年纪--->99
新对象改了个名字--->张三
新对象改了班级的名字-->最最最最最12班
新对象改了班级号--->9527
=后,改变了B的我们:
studentA:[student:463345942,name:张三,age:99,clazz:[clazz:195600860,clazzName:最最最最最12班,clazzNo:9527]]
studentB:[student:463345942,name:张三,age:99,clazz:[clazz:195600860,clazzName:最最最最最12班,clazzNo:9527]]
=后,改变了A的我们:
studentA:[student:463345942,name:张三三,age:99,clazz:[clazz:195600860,clazzName:最最最最最12班,clazzNo:9527999]]
studentB:[student:463345942,name:张三三,age:99,clazz:[clazz:195600860,clazzName:最最最最最12班,clazzNo:9527999]]

显然,Student studentB = studentA;并不会产生一个新的对象。不管是改变B还是改变A,两个对象(其实是同一个对象,内存地址一样)都会产生相同的变化。

使用浅拷贝

现在在Student类中重写clone()方法(需实现Cloneable类)

package com.mayn.spring;

/**
 * TODO
 *
 * @ClassName: Student
 * @author: yihong
 * @since: 2020/7/27 11:26
 */
public class Student implements Cloneable{
    private String name;
    private Integer age;
    private Clazz clazz;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Clazz getClazz() {
        return clazz;
    }

    public void setClazz(Clazz clazz) {
        this.clazz = clazz;
    }

    public Student() {
    }

    public Student(String name, Integer age, Clazz clazz) {
        this.name = name;
        this.age = age;
        this.clazz = clazz;
    }

    @Override
    public Object clone(){
        try {
            return super.clone();
        }catch (CloneNotSupportedException e){
            return  null;
        }
    }

    @Override
    public String toString(){
        return "[student:" + this.hashCode() + ",name:" + this.name + ",age:" + this.age + ",clazz:" + this.clazz + "]";
    }
}

对浅拷贝后的对象进行赋值:

public class shallowCopy {
    public static void main(String[] args) {
        Clazz clazz = new Clazz("最强一班",18);
        Student studentA = new Student("学生A",11,clazz);
        System.out.println("studentA:" + studentA.toString());
        System.out.println("开始进行浅拷贝。");
        Student studentB = (Student) studentA.clone();
        System.out.println("新对象改了个年纪--->99");
        studentB.setAge(99);
        System.out.println("新对象改了个名字--->张三");
        studentB.setName("张三");
        System.out.println("新对象改了班级的名字-->最最最最最12班");
        studentB.getClazz().setClazzName("最最最最最12班");
        System.out.println("新对象改了班级号--->9527");
        studentB.getClazz().setClazzNo(9527);
        System.out.println("浅拷贝后,改变了B的我们:");
        System.out.println("studentA:" + studentA.toString());
        System.out.println("studentB:" + studentB.toString());
    }
}

打印:

开始进行浅拷贝。
新对象改了个年纪--->99
新对象改了个名字--->张三
新对象改了班级的名字-->最最最最最12班
新对象改了班级号--->9527
浅拷贝后,改变了B的我们:
studentA:[student:463345942,name:学生A,age:11,clazz:[clazz:195600860,clazzName:最最最最最12班,clazzNo:9527]]
studentB:[student:1334729950,name:张三,age:99,clazz:[clazz:195600860,clazzName:最最最最最12班,clazzNo:9527]]

可以看到,对基本数据类型及String类型的成员变量,浅拷贝前后的对象互不影响,但是对Clazz类的成员变量(未重写clone()方法),改变拷贝生成的B,对象A的clazz属性也随之变化,两者是同步的.。从打印结果中知道,studentAstudentB的成员变量clazzhashCode是一样的,是同一个对象。拷贝前后的两个对象,其数组、类的对象等成员变量都指向的同一个实例。所以对其中一个对象的该成员对象的修改会影响到另一个对象的该成员变量值。

使用深拷贝

现在在Clazz类中重写clone()方法(需实现Cloneable类)

package com.mayn.spring;

/**
 * TODO
 *
 * @ClassName: Clazz
 * @author: yihong
 * @since: 2020/7/27 11:27
 */
public class Clazz implements Cloneable{
    private String clazzName;
    private Integer clazzNo;

    public void setClazzName(String clazzName) {
        this.clazzName = clazzName;
    }

    public void setClazzNo(Integer clazzNo) {
        this.clazzNo = clazzNo;
    }

    public Clazz() {
    }

    public Clazz(String clazzName, Integer clazzNo) {
        this.clazzName = clazzName;
        this.clazzNo = clazzNo;
    }

    @Override
    public Object clone(){
        try {
            return super.clone();
        }catch (CloneNotSupportedException e){
            return  null;
        }
    }

    @Override
    public String toString(){
        return "[clazz:" + this.hashCode() + ",clazzName:" + this.clazzName + ",clazzNo:" + this.clazzNo +"]";
    }
}

修改Student类中的clone()方法(需实现Cloneable类)

package com.mayn.spring;

/**
 * TODO
 *
 * @ClassName: Student
 * @author: yihong
 * @since: 2020/7/27 11:26
 */
public class Student implements Cloneable{
    private String name;
    private Integer age;
    private Clazz clazz;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Clazz getClazz() {
        return clazz;
    }

    public void setClazz(Clazz clazz) {
        this.clazz = clazz;
    }

    public Student() {
    }

    public Student(String name, Integer age, Clazz clazz) {
        this.name = name;
        this.age = age;
        this.clazz = clazz;
    }

    @Override
    public Object clone(){
        try {
            Student student = (Student) super.clone();
            student.clazz = (Clazz) clazz.clone();
            return student;
        }catch (CloneNotSupportedException e){
            return  null;
        }
    }

    @Override
    public String toString(){
        return "[student:" + this.hashCode() + ",name:" + this.name + ",age:" + this.age + ",clazz:" + this.clazz + "]";
    }
}

对深拷贝后的对象进行赋值:

public class deepCopy {
    public static void main(String[] args) {
        Clazz clazz = new Clazz("最强一班",18);
        Student studentA = new Student("学生A",11,clazz);
        System.out.println("studentA:" + studentA.toString());
        System.out.println("开始进行深拷贝。");
        Student studentB = (Student) studentA.clone();
        System.out.println("新对象改了个年纪--->99");
        studentB.setAge(99);
        System.out.println("新对象改了个名字--->张三");
        studentB.setName("张三");
        System.out.println("新对象改了班级的名字-->最最最最最12班");
        studentB.getClazz().setClazzName("最最最最最12班");
        System.out.println("新对象改了班级号--->9527");
        studentB.getClazz().setClazzNo(9527);
        System.out.println("深拷贝后,改变了B的我们:");
        System.out.println("studentA:" + studentA.toString());
        System.out.println("studentB:" + studentB.toString());
    }
}

打印结果:

studentA:[student:463345942,name:学生A,age:11,clazz:[clazz:195600860,clazzName:最强一班,clazzNo:18]]
开始进行深拷贝。
新对象改了个年纪--->99
新对象改了个名字--->张三
新对象改了班级的名字-->最最最最最12班
新对象改了班级号--->9527
深拷贝后,改变了B的我们:
studentA:[student:463345942,name:学生A,age:11,clazz:[clazz:195600860,clazzName:最强一班,clazzNo:18]]
studentB:[student:1334729950,name:张三,age:99,clazz:[clazz:1347137144,clazzName:最最最最最12班,clazzNo:9527]]

对基本数据类型以及String类的成员变量,浅拷贝和深拷贝的影响结果是一样的,都是各自独自,互不影响。

但是深拷贝后的对象studentB,其Clazz类型的成员变量,其变化并不会影响到原拷贝对象studentA,从clazzhashCode可以知道,studentBclazz属性的在内存上开辟了一个新的地址和空间,这就是深拷贝。

关于浅拷贝赫尔深拷贝,主要需要知道怎么实现拷贝;如何实现深拷贝;深拷贝对比浅拷贝改变了哪些东西,所以才能称之为深拷贝。理解了这些,基本上就理解了浅拷贝,深拷贝。

上一篇下一篇

猜你喜欢

热点阅读