深拷贝&浅拷贝

Java中变量有两种类型:基本类型和引用类型

基本类型的变量保存原始值,即它代表的值就是数值本身 引用类型的变量保存引用值,"引用值"指向内存空间的地址,代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置

注:String类型通过常量赋值时相当于基本数据类型,通过new关键字创建对象时便是引用数据类型

浅拷贝

1. 浅拷贝介绍

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

2. 浅拷贝特点

(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个。 (2) 对于引用类型,比如数组或者类对象,因为引用类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,它们指向了同一内存空间。改变其中一个,会对另外一个也产生影响。

3. 浅拷贝的实现

public class Subject {
    private String name;

    public Subject(String name) {
        this.name = name;
    }

    public String toString() {
        return String.format("[Subject: %d, name: %s]", this.hashCode(), this.name);
    }

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

  

public class Student implements Cloneable{
    //基础数据类型
    private int age;
    private String name;
    //引用类型
    private Subject subject;
    public Student(int age,String name,Subject subject){
        this.age=age;
        this.name=name;
        this.subject=subject;
    }

    /**
     * 浅拷贝,重写clone()
     * @return
     */
    public Object clone(){
        try {
            //直接调用父类的clone()方法
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public String toString(){
        return String.format("{Student:%d, subject:%s, name:%s, age:%d}",this.hashCode(),this.subject.toString(),this.name,this.age);
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Subject getSubject() {
        return subject;
    }
    public void setSubject(Subject subject) {
        this.subject = subject;
    }
}

  测试

public class Test {
    public static void main(String[] args) {
        Subject subjectA=new Subject("yuwen");
        Student studentA=new Student(10,"stuA",subjectA);

        Student studentB= (Student) studentA.clone();
        studentB.setAge(12);
        studentB.setName("stuB");

        Subject subjectB=studentB.getSubject();
        subjectB.setName("shuxue");

        System.out.println("studentA: "+studentA.toString());
        System.out.println("studentB: "+studentB.toString());
    }
}

  结果

studentA: {Student:1896277646, subject:[Subject: 2128227771, name: shuxue], name:stuA, age:10}
studentB: {Student:396180261, subject:[Subject: 2128227771, name: shuxue], name:stuB, age:12}

   studentAstudentB 的基础数据类型的修改互不影响,而引用类型 subject 修改后是会有影响的。

深拷贝

1.深拷贝介绍

深拷贝,在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。

2. 深拷贝特点

(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。 (2) 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。 (3) 对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝 (4) 深拷贝相比于浅拷贝速度较慢并且花销较大。

3.深拷贝的实现

public class Subject implements Cloneable{
    private String name;

    public Subject(String name) {
        this.name = name;
    }

    /**
     * 重写clone(),每个对象都调用父类的clone() 方法
     * @return
     */
    public Object clone(){
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
    
    public String toString() {
        return String.format("[Subject: %d, name: %s]", this.hashCode(), this.name);
    }

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

  

public class Student implements Cloneable{
    //基础数据类型
    private int age;
    private String name;
    //引用类型
    private Subject subject;
    public Student(int age, String name, Subject subject){
        this.age=age;
        this.name=name;
        this.subject=subject;
    }

    /**
     * 深拷贝,重写clone(),每个引用类型的变量都调用 各自重写的clone()
     * @return
     */
    public Object clone(){
        Student student=null;
        try {
            student= (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        student.subject= (Subject) subject.clone();
        return student;
    }

    public String toString(){
        return String.format("{Student:%d, subject:%s, name:%s, age:%d}",this.hashCode(),this.subject.toString(),this.name,this.age);
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Subject getSubject() {
        return subject;
    }
    public void setSubject(Subject subject) {
        this.subject = subject;
    }
}

  测试

public class Test {
    public static void main(String[] args) {
        Subject subjectA= new Subject("yuwen");
        Student studentA=new Student(10,"stuA",subjectA);

        Student studentB= (Student) studentA.clone();
        studentB.setAge(12);
        studentB.setName("stuB");

        Subject subjectB=studentB.getSubject();
        subjectB.setName("shuxue");

        System.out.println("studentA: "+studentA.toString());
        System.out.println("studentB: "+studentB.toString());
    }
}

  结果

studentA: {Student:1896277646, subject:[Subject: 2128227771, name: yuwen], name:stuA, age:10}
studentB: {Student:396180261, subject:[Subject: 625576447, name: shuxue], name:stuB, age:12}

  深拷贝后,不管是基础数据类型还是引用类型的成员变量,修改其值都不会相互造成影响。

总结

关于clone()

JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。

和 new 的区别

共同点:都是分配内存,对象都是指向不同的内存地址 不同点:new创建一个对象,clone复制一个对象。new是返回的新对象,而调用clone()方法时,拷贝对象已经包含了一些原来对象的信息,而不是对象的初始信息

Object类的clone()是一个native方法,native方法的效率一般来说都是远高于Java中的非native方法。这也解释了为 什么要用Objectclone()方法而不是先new一个类,然后把原始对象中的信息赋到新对象中,虽然这也实现了clone功能

为什么要实现Cloneable接口

Cloneable接口是不包含任何方法的,其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对 Object类中clone()方法的,如果实现clone()方法的类没有实现Cloneable接口,并调用了Objectclone()方法(也就是调用了 super.clone()方法),那么Objectclone()方法就会抛出CloneNotSupportedException异常

浅拷贝

对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝

深拷贝

对基本数据类型进行值传递,对引用数据类型,会对引用指向的对象进行拷贝,此为深拷贝。也就是在clone()方法对其内的引用类型的变量再进行一次 clone()

 

参考

https://www.jianshu.com/p/e8c6155d9694

https://www.jianshu.com/p/94dbef2de298

posted @ 2019-09-23 17:53  huiyii  阅读(149)  评论(0编辑  收藏  举报