欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java浅拷贝与深拷贝

程序员文章站 2022-05-28 14:36:46
...

浅拷贝

浅拷贝是按位拷贝对象,在进行浅拷贝时会创建一个新对象。如果被拷贝对象的属性是基本类型,那么拷贝的就是基本类型的值;如果属性是对象(内存地址/引用类型),拷贝的就是对象 。属性是对象时,其中一个对象改变了这个地址,就会影响到另一个对象。
我们先写一个被拷贝对象。

/**
 * 对象(内存地址)
 * 作为一个对象的属性
 */
public class BaseInfo {
    private String name;
    private int age;

    public BaseInfo(String name, int age) {
        setName(name);
        setAge(age);
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
public class People implements Cloneable {
    private BaseInfo baseInfo;
    private String email;

    public People(BaseInfo baseInfo, String email) {
        setBaseInfo(baseInfo);
        setEmail(email);
    }

    public BaseInfo getBaseInfo() {
        return baseInfo;
    }

    public void setBaseInfo(BaseInfo baseInfo) {
        this.baseInfo = baseInfo;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

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

    public void resetPeopleInfo(String name,int age,String email){
        baseInfo.setName(name);
        baseInfo.setAge(age);
        setEmail(email);
    }

    public String getPeopleInfo() {
        return "name = " + getBaseInfo().getName() + ",age = " + getBaseInfo().getAge() + ",email = " + getEmail();
    }
}

为了实现被拷贝的能力,需要将被拷贝的对象实现Cloneable接口,并覆写clone()方法。
拷贝一下:

BaseInfo baseInfo = new BaseInfo("张三", 18);
People people_1 = new People(baseInfo, "aaa@qq.com");
Log.d(TAG, "people_1--->  " + people_1.toString());
Log.d(TAG, "people_1的baseInfo--->  " + people_1.getBaseInfo().toString());
Log.d(TAG, "people_1--->  " + people_1.getPeopleInfo());
try {
     People people_2 = (People) people_1.clone();
     Log.d(TAG,"-------------------------------------------");
     Log.d(TAG, "people_2--->  " + people_2.toString());
     Log.d(TAG, "people_2的baseInfo--->  " + people_2.getBaseInfo().toString());
     Log.d(TAG, "people_2--->  " + people_2.getPeopleInfo());
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }

再看下日志:

02-06 02:56:16.392 5943-5943/com.example.lenovo.testdemo D/CopyActivity: people_1--->  copy.People@94b6efe
02-06 02:56:16.392 5943-5943/com.example.lenovo.testdemo D/CopyActivity: people_1的baseInfo--->  copy.BaseInfo@763d65f
02-06 02:56:16.393 5943-5943/com.example.lenovo.testdemo D/CopyActivity: people_1--->  name = 张三,age = 18,email = zhangsan@163.com
02-06 02:56:16.393 5943-5943/com.example.lenovo.testdemo D/CopyActivity: -------------------------------------------
02-06 02:56:16.393 5943-5943/com.example.lenovo.testdemo D/CopyActivity: people_2--->  copy.People@d4d68ac
02-06 02:56:16.394 5943-5943/com.example.lenovo.testdemo D/CopyActivity: people_2的baseInfo--->  copy.BaseInfo@763d65f
02-06 02:56:16.394 5943-5943/com.example.lenovo.testdemo D/CopyActivity: people_2--->  name = 张三,age = 18,email = zhangsan@163.com

可以看到,两个People对象的地址是不一样的,即people_2是新生成的一个地址,然后people_1的属性赋值给了people_2。再仔细看下,就能发现两个People对象的BaseInfo属性却是一个地址。就说明,拷贝的时候对于对象类型的属性,拷贝的只是地址,没有为这样非基本类型数据开辟新的地址空间。
Java浅拷贝与深拷贝
当我们重置一下people_1对象的属性时,可以看一下people_2对象的属性的值的变化。

people_1.resetPeopleInfo("李四",37,"aaa@qq.com");
Log.d(TAG, "people_1--->  " + people_1.toString());
Log.d(TAG, "people_1的baseInfo--->  " + people_1.getBaseInfo().toString());
Log.d(TAG, "people_1--->  " + people_1.getPeopleInfo());
Log.d(TAG,"-------------------------------------------");
Log.d(TAG, "people_2--->  " + people_2.toString());
Log.d(TAG, "people_2的baseInfo--->  " + people_2.getBaseInfo().toString());
Log.d(TAG, "people_2--->  " + people_2.getPeopleInfo());

我们改变下people_1的属性:name由张三改成李四,年龄由18改成37,邮件由aaa@qq.com改成aaa@qq.com
看下数据的变化情况

02-06 02:56:16.396 5943-5943/com.example.lenovo.testdemo D/CopyActivity: people_1--->  copy.People@94b6efe
02-06 02:56:16.396 5943-5943/com.example.lenovo.testdemo D/CopyActivity: people_1的baseInfo--->  copy.BaseInfo@763d65f
02-06 02:56:16.396 5943-5943/com.example.lenovo.testdemo D/CopyActivity: people_1--->  name = 李四,age = 37,email = lisi@126.com
02-06 02:56:16.396 5943-5943/com.example.lenovo.testdemo D/CopyActivity: -------------------------------------------
02-06 02:56:16.396 5943-5943/com.example.lenovo.testdemo D/CopyActivity: people_2--->  copy.People@d4d68ac
02-06 02:56:16.397 5943-5943/com.example.lenovo.testdemo D/CopyActivity: people_2的baseInfo--->  copy.BaseInfo@763d65f
02-06 02:56:16.397 5943-5943/com.example.lenovo.testdemo D/CopyActivity: people_2--->  name = 李四,age = 37,email = zhangsan@163.com

最终的结果是people_1的三个属性都变过来了,但是people_2的三个属性中作为基本数据类型的email还是原来的aaa@qq.com,引用数据类型变了过来。
这就印证了浅拷贝的特点:
1)新开辟内存地址,生成一个新的对象;
2)基本数据类型独立开来,不随着原来对象属性的变化而变化;
3)引用数据类型是公用的,随着一个变化,另一个也会跟着变化。

深拷贝

深拷贝会拷贝所有的属性(基本数据类型和引用数据类型),并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。相对于浅拷贝,深拷贝速度较慢,花销也大一些。
现在为了要在clone对象时进行深拷贝, 那么就要Clonable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。由上述的例子可以看到,只是用Object中默认的clone方法也是浅拷贝。
为了实现深拷贝,首先需要将被拷贝对象的引用类型的属性也要实现Cloneable接口,并覆写clone() 方法。

public class BaseInfo implements Cloneable {
    private String name;
    private int age;

    public BaseInfo(String name, int age) {
        setName(name);
        setAge(age);
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

然后覆写被拷贝对象的clone()方法,将默认的clone()方法进行改变,在这个方法中引用对象类型进行拷贝,具体的拷贝如下。

@Override
    protected Object clone() throws CloneNotSupportedException {
        People newPeople = (People) super.clone();
        newPeople.setBaseInfo((BaseInfo) baseInfo.clone());
        return newPeople;
    }

这样操作的话,就会在拷贝时重新为引用对象的属性重新生成一个地址空间并赋值。
Java浅拷贝与深拷贝
具体的日志信息如下:

02-06 03:20:16.366 24786-24786/com.example.lenovo.testdemo D/CopyActivity: people_1--->  copy.People@94b6efe
02-06 03:20:16.366 24786-24786/com.example.lenovo.testdemo D/CopyActivity: people_1的baseInfo--->  copy.BaseInfo@763d65f
02-06 03:20:16.366 24786-24786/com.example.lenovo.testdemo D/CopyActivity: people_1--->  name = 李四,age = 37,email = lisi@126.com
02-06 03:20:16.366 24786-24786/com.example.lenovo.testdemo D/CopyActivity: -------------------------------------------
02-06 03:20:16.367 24786-24786/com.example.lenovo.testdemo D/CopyActivity: people_2--->  copy.People@d4d68ac
02-06 03:20:16.367 24786-24786/com.example.lenovo.testdemo D/CopyActivity: people_2的baseInfo--->  copy.BaseInfo@e22975
02-06 03:20:16.368 24786-24786/com.example.lenovo.testdemo D/CopyActivity: people_2--->  name = 张三,age = 18,email = zhangsan@163.com

可以看到,深拷贝时如果改变了people_1的引用对象类型时并没有改变people_2的引用对象类型的属性值。