深入了解:浅拷贝&深拷贝

浅拷贝和深拷贝

在java中 理解深拷贝和浅拷贝的概念对于处理对象复制时 保持数据的一致性和独立性至关重要  这两种拷贝方式主要区别在于它们如何处理对象内部的引用类型成员变量

在学习浅拷贝和深拷贝前 我们要区分 对象引用 和 对象本身的区别

对象引用:在Java中 对象引用是一个指向对象内存地址的变量  它本身不是对象  而是对象在内存中的位置的标识

对象本身:对象是在内存中分配的一块区域  存储了对象的实际数据和状态

在接下来的示例中 我们详细讲解

浅拷贝

浅拷贝会创建一个对象  这个新对象有着原始对象属性值的一份精确拷贝  

如果属性是基本数据类型 拷贝的就是值本身

如果这个属性是引用类型  拷贝的就是内存地址(此时便共享空间)

如果原始对象的引用类型属性被修改  浅拷贝出来的对象中的对应属性也会受到影响

赋值和浅拷贝的区别

赋值操作是将一个对象的引用(即对象的内存地址)赋值给另一个对象 这意味着两个变量现在都指向了内存的同一个对象

总结来说 浅拷贝在处理基本数据类型字段时 会为新对象分配新的内存空间来存储这些字段的副本  而对于引用类型字段  浅拷贝只是复制了引用的值(即内存地址)  而没有复制引用所指向的对象本身

在java中 实现浅拷贝的一个常用方法是使用Object类的clone方法(注意 这个方法是protected的  所以我们需要继承该类并重写clone方法 并改为public  或者使用实现了Cloneable接口的类)  但是 默认情况下 clone方法实现的是浅拷贝

示例1:

class Address {
    private String city;
    public Address(String city) {
        this.city = city;
    }
    public String getCity() {
        return city;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Person implements Cloneable {
    private Address address;

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

    public Address getAddress() {
        return address;
    }

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

public class ShallowCopy {
    public static void main(String[] args) {
        try {
            Person person1 = new Person(new Address("乌镇"));
            Person person1Copy = person1.clone();
            // true
            System.out.println(person1.getAddress() == person1Copy.getAddress());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

可能我们会思考 为什么person1和person1Copy 不能共享空间呢?

person1和person1Copy是两个不同的Person对象实例  它们不共享Person对象的内存空间

person1和person1Copy的address字段都指向同一个Address对象  这是因为浅拷贝只复制了引用   而没有复制引用的对象本身

注意:由于Address类中的city字段是String类型的  而String是不可变的  所以在这个特定的例子中  Address类的clone方法实际上并没有做太多有用的事情(除了复制引用之外)  然而 如果Address类包含其他可变字段或复杂的对象引用  那么实现深拷贝就变得重要了

示例2:

在Person类中  age和name是基本数据类型(尽管String在Java中是特殊的  但在这里它作为引用传递 但其不可变性使得它表现得像值类型)  而m是一个Money类型的对象引用

person1和person2是两个不同的Person对象实例  它们有不同的内存地址

但是 它们的m字段都指向同一个Money对象实例(浅拷贝)

深拷贝

深拷贝会创建一个新对象  并且递归地复制对象中的所有属性  包括对象中的引用类型属性  这意味着  深拷贝会创建一个全新的对象  以及对象中引用的其他对象的全新拷贝  因此原始对象和拷贝对象之间是完全独立的  互不影响

实现深拷贝的一个常用方法是通过序列化和反序列化  这种方式虽然简单  但要求对象及其所有引用的对象的必须实现Serializable接口  另一个方法是手动编写深拷贝的代码  为每个引用类型属性创建一个新的实例 并复制其内容

示例1:

class Address implements Cloneable{
    private String city;

    public Address(String city) {
        this.city = city;
    }

    public String getCity() {
        return city;
    }

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

class Person implements Cloneable {
    private Address address;

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

    public Address getAddress() {
        return address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned=(Person) super.clone();
        cloned.address=(Address) this.address.clone();
        return cloned;
    }
}

public class Test {
    public static void main(String[] args) {
        try {
            Address address1 = new Address("白城");
            Person person1 = new Person(address1);
            Person person1Copy=(Person) person1.clone();
            System.out.println(person1.getAddress()==person1Copy.getAddress());//false
            System.out.println(person1.getAddress().getCity().equals(person1Copy.getAddress().getCity()));//true
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

在这段代码中 Person类包含了一个Address类型的成员变量address 当我们调用Person类的clone方法时 首先会调用Object类的clone方法来创建一个Person对象的浅拷贝  即新对象的address成员变量会引用与原始对象相同的Address对象  

但是 随后在Person类的clone方法中 我们通过this.address.clone( )创建了Address对象的一个新实例 并将其复制给新对象的address成员变量  这样就确保了Person对象中的Address成员变量也被复制了 从而实现了深拷贝

示例2:

在这里  当clone方法调用时 person2指向tmp所指向的对象 当clone方法结束  tmp被回收  此时只有person2指向原先tmp所指的对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值