深拷贝和浅拷贝的介绍及实现


介绍

深拷贝和浅拷贝 都会在堆中建立新的对象,将原对象的非静态字段复制到该新对象;对于基本数据类型的字段,直接进行值的复制;对于引用类型的字段,二者的处理有所不同。

浅拷贝:引用类型字段 是直接复制的 原对象中对应字段指向的对象的地址值,即 拷贝对象和原对象的相应引用类型字段 指向的都是同一个对象

深拷贝:不同于 浅拷贝 直接复制对象地址值,深拷贝会将堆中的对象也拷贝一份,即 拷贝对象和原对象的相应引用类型字段 指向的是不同的对象

实现

浅拷贝

Object 类提供的 clone() 方法可以非常简单地实现对象的浅拷贝。

示例如下:
Person 类实现了 Cloneable 接口,并重写了 clone() 方法。
clone() 方法的实现很简单,直接调用的是父类 Object 的 clone() 方法。

public class Address {
    private String city;

    public Address(String city) {
        this.city = city;
    }
    // 省略getter、setter方法
}

public class Person implements Cloneable {

    private Address address;

    public Person(Address address) {
        this.address = address;
    }
    
    // 省略getter、setter方法
    
    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}

测试:

Person person1 = new Person(new Address("Beijing"));
Person person2 = person1.clone();
System.out.println(person1 == person2); // false
System.out.println(person1.getAddress() == person2.getAddress()); // true

person1 == person2为 false,说明拷贝得到的对象是新建立的;
person1.getAddress() == person2.getAddress()为 true,说明拷贝对象与原对象引用的是同一个 Address 对象。

深拷贝

深拷贝有两种实现方式:重写克隆方法序列化

1.重写克隆方法

重写克隆方法,引用类型字段单独克隆

示例如下:
Address 类也实现了 Cloneable 接口,因此可以对 Address 对象进行克隆;
Person 的 clone() 方法做了修改,address 字段单独设置,将原先的 address 对象拷贝一份后赋值给它。

public class Address implements Cloneable {
    private String city;

    public Address(String city) {
        this.city = city;
    }
    
    // 省略getter、setter方法
    
    @Override
    public Address clone() throws CloneNotSupportedException {
        return (Address) super.clone();
    }
}

public class Person implements Cloneable {

    private Address address;

    public Person(Address address) {
        this.address = address;
    }

    // 省略getter、setter方法

    // 重写克隆方法
    @Override
    public Person clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        // 引用类型字段单独克隆
        person.setAddress(person.getAddress().clone());
        return person;
    }
}

测试:

Person person1 = new Person(new Address("Beijing"));
Person person2 = person1.clone();
System.out.println(person1 == person2); // false
System.out.println(person1.getAddress() == person2.getAddress()); // false

person1.getAddress() == person2.getAddress()为 false,说明拷贝对象与原对象引用的 Address 对象是不同的了。

2.序列化

先讲原对象序列化,再反序列化成拷贝对象

示例如下:
编写了深拷贝的工具类 CopyUtils,deepCopy 方法将对象序列化到字节流中,并从字节流中反序列化得到一个对象副本,即可实现深拷贝。
特别注意:Address 和 Person 需要实现序列化接口 Serializable。

// 深拷贝的工具类
public class CopyUtils {

    public static Object deepCopy(Object obj) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(obj);
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }

}

public class Address implements Serializable {
    private String city;

    public Address(String city) {
        this.city = city;
    }
    
    // 省略getter、setter方法
}

public class Person implements Serializable {

    private Address address;

    public Person(Address address) {
        this.address = address;
    }

    // 省略getter、setter方法
}

测试:

Person person1 = new Person(new Address("Beijing"));
Person person2 = (Person) CopyUtils.deepCopy(person1);
System.out.println(person1 == person2); // false
System.out.println(person1.getAddress() == person2.getAddress()); // false

person1.getAddress() == person2.getAddress()为 false,说明拷贝对象与原对象引用的 Address 对象是不同的。


如果有帮助的话,可以点个赞支持一下嘛🙏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值