浅谈深拷贝和浅拷贝之区别

1,337 阅读5分钟

你是否还在纠结于什么是深拷贝?什么是浅拷贝?或者简单了当的记着基本数据类型拷贝下来的就是深拷贝,引用类型拷贝下来的就是浅拷贝,其实这样的理解是有问题的,是不够准确的。那么该如何理解呢?相信在接下来的内容中你会找到你想要的答案!

概念

首先我们得要了解下面这几个基本概念: 深拷贝、浅拷贝、Cloneable接口、clone方法、Object类 然后我们再通过Clone方法来更深的了解什么是深拷贝浅拷贝

深拷贝(deep copy)

深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响,拷贝完成之后,通过这个引用修改其指向的数据,不会影响到原来的数据。

浅拷贝(shallow copy)

浅拷贝就是拷贝指向对象的指针,意思就是说:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,浅拷贝只是一种简单的拷贝,让几个对象公用一个内存(即指向同一个对象)

换言之,浅拷贝就是指对象复制的时候只复制一层;深拷贝是指复制对象的所有层级。

Cloneable接口

Cloneable接口是一个空接口,也称作标志接口 其作用是:代表当前这个类是可以被克隆的。

clone方法

一个对象要被克隆,必须满足下面三个条件:

  1. 要实现Cloneable接口
  2. 并且要重写Object里的克隆方法clone()
  3. 同时它会抛出异常,你要声明这个异常,此时才能调用克隆方法来克隆你的对象

Object类

  • Object是Java默认提供的一个类
  • Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收
  • 在开发之中,Object类是参数的最高统一类型

如何实现?

实现深拷贝和浅拷贝

情况1:

这是情况1: 你所要拷贝的类中没有引用类型

分析: 通过clone()方法克隆了一个和person1相同的对象地址为0x2222,然后将这个地址给到person2,因此person2指向了这个新的对象了,然后改变person2所指向的这个对象中id这个变量的值,并不影响person1所指向对象中id的值。

结论: 因为它们之间是互不影响的,所以这是一个*深拷贝*。

上图的代码实现如下:

class Person implements Cloneable{
    public int id = 1;

    @Override
    public String toString() {
        return "Person{" +
                "id='" + id + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person tmp = (Person) super.clone();
        return tmp;
        //return super.clone();
    }

}
public class TestDemo {

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person();
        Person person2 = (Person)person1.clone();
        System.out.println(person1);
        System.out.println(person2);
        System.out.println("=========================");
        person2.id = 99;
        System.out.println(person1);
        System.out.println(person2);
    }
}

输出如下:

情况2:

这是情况2: 你所要拷贝的类中有引用类型,并不对这个引用类型进行拷贝

分析:通过clone()方法克隆了一个和person1相同的对象地址为0x2222,然后将这个地址给到person2,因此person2指向了这个新的对象了。又因为这个对象中有一个引用类型,并且它们的引用m所指向是同一对象,地址为0x3333这个对象。如果通过person1修改了变量money的值那么person2money的值也会发生同样的变化,反之通过person2修改了变量money的值也会影响person1money的值。

结论: 对于money这个变量来说,无论通过person1还是person2修改它,都会影响person1和person2中m的值,并不相对独立,所以这是一个浅拷贝。

上图浅拷贝的代码实现如下:

class Money implements Cloneable{
    public double money = 9.9;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Person implements Cloneable{
    public int id = 1;
    public Money m = new Money();

    @Override
    public String toString() {
        return "Person{" +
                "id='" + id + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person tmp = (Person) super.clone();
        return tmp;
        //return super.clone();
    }

}
public class TestDemo {

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person();
        Person person2 = (Person)person1.clone();
        System.out.println(person1.m.money);
        System.out.println(person2.m.money);
        System.out.println("=========================");
        person2.m.money = 99.99;
        System.out.println(person1.m.money);
        System.out.println(person2.m.money);
    }

输出如下:

情况3:

这是情况3: 你所要拷贝的类中有引用类型,并对这个引用类型进行拷贝

**分析:**首先通过clone()方法克隆了一个和person1相同的对象地址为0x2222,然后将这个地址给到person2,因此person2指向了这个新的对象了。又因为这个对象中有一个引用类型,并且它们的引用m所指向是同一对象,地址为0x3333这个对象,在这里我们可以再次使用clone()方法对m这个引用变量进行拷贝,因此得到了地址为0x4444这个对象,然后将person2中的m指向这个新的对象。这样一来person1m所指向的对象和person2m所指向的对象就不是同一个对象了。

结论: 因为person1person2m所指向的对象都不相同了,所以它们之间也就相不影响了,是相对独立的存在,即这是一个深拷贝。

上图深拷贝的代码实现如下:

class Money implements Cloneable{
    public double money = 9.9;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Person implements Cloneable{
    public int id = 1;
    public Money m = new Money();

    @Override
    public String toString() {
        return "Person{" +
                "id='" + id + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person tmp = (Person) super.clone();
        tmp.m = (Money) this.m.clone();
        return tmp;
        //return super.clone();
    }

}
public class TestDemo {

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person();
        Person person2 = (Person)person1.clone();
        System.out.println(person1.m.money);
        System.out.println(person2.m.money);
        System.out.println("=========================");
        person2.m.money = 99.99;
        System.out.println(person1.m.money);
        System.out.println(person2.m.money);
    }
}

输出如下:

总结

对于深拷贝还是浅拷贝 通过上述我们能够得出以下几点结论:

  • 深浅拷贝是取决于你的代码是如何实现的而不是说某个方法就是深拷贝或者浅拷贝。
  • 想要达到深拷贝,那么每个对象中,如果有引用这个对象的引用 所指的对象也要进行拷贝
  • 换句话说,如果你想要实现深拷贝,只要你所克隆的对象中有引用数据类型,你就继续拷贝这个引用数据类型,直到下一个对象中没有引用数据类型,即实现了深拷贝。反之,如果下一个对象中存在引用数据类型,你没有对它进行拷贝,即是浅拷贝。