浅拷贝与深拷贝的区别

目录

问题:

区别:

​编辑​编辑

实现:

什么时候会浅拷贝

1.默认的clone()方法:实现Cloneable接口,但未对引用类型字段进行特殊处理,默认的super.clone()只会执行浅拷贝。

2.赋值的时候,直接复制了引用类型的地址

3.序列化的时候,引用类型没有序列化

什么时候会深拷贝

1.自定义clone方法,处理引用数据类型

2.序列化与反序列化处理引用类型

3.引用第三方工具

4.手动复制引用类型字段

拓展:


问题:

刷面经,用博客输出,加深印象,特此记录。

区别:

一图胜千言。

  • 浅拷贝 是 创建一个新的对象,基本数据类型 会原模原样复制过去,而引用类型,则是只复制地址,说白了就是和源对象共用一个引用数据对象。

  • 深拷贝  是 创建一个独立的副本,基本数据类型 会原模原样复制过去,而引用类型,不复制地址而是生成一个新的地址,但是引用类型的值会复制过去。

实现:

什么时候会浅拷贝

1.默认的clone()方法:实现Cloneable接口,但未对引用类型字段进行特殊处理,默认的super.clone()只会执行浅拷贝。

class Address {
    private String city;

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

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

class Person implements Cloneable {
    private String name;
    private Address address;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 默认的浅拷贝
    }

    public String getName() {
        return name;
    }

    public Address getAddress() {
        return address;
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address originalAddress = new Address("New York");
        Person originalPerson = new Person("Alice", originalAddress);

        // 克隆对象
        Person clonedPerson = (Person) originalPerson.clone();

        // 修改克隆对象的引用类型成员
        clonedPerson.getAddress().setCity("San Francisco");

        // 输出结果
        System.out.println("Original Person: " + originalPerson.getName() + ", " + originalPerson.getAddress().getCity());
        System.out.println("Cloned Person: " + clonedPerson.getName() + ", " + clonedPerson.getAddress().getCity());
    }
}class Address {
    private String city;

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

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

class Person implements Cloneable {
    private String name;
    private Address address;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 默认的浅拷贝
    }

    public String getName() {
        return name;
    }

    public Address getAddress() {
        return address;
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address originalAddress = new Address("New York");
        Person originalPerson = new Person("Alice", originalAddress);

        // 克隆对象
        Person clonedPerson = (Person) originalPerson.clone();

        // 修改克隆对象的引用类型成员
        clonedPerson.getAddress().setCity("San Francisco");

        // 输出结果
        System.out.println("Original Person: " + originalPerson.getName() + ", " + originalPerson.getAddress().getCity());
        System.out.println("Cloned Person: " + clonedPerson.getName() + ", " + clonedPerson.getAddress().getCity());
    }
}

2.赋值的时候,直接复制了引用类型的地址

import java.io.*;

class Address {
    String city;

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

class Person implements Serializable {
    String name;       // 引用类型
    int age;           // 基本数据类型
    Address address;   // 引用类型

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    // 使用序列化实现浅拷贝
    public Person shallowCopy() throws IOException, ClassNotFoundException {
        // 序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        // 反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (Person) ois.readObject();
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", 30, address);

        // 浅拷贝
        Person person2 = person1.shallowCopy();

        // 输出比较结果
        System.out.println(person1 == person2); // 输出:false(两个对象不同)
        System.out.println(person1.address == person2.address); // 输出:true(引用相同)

        // 修改引用类型字段
        person2.address.city = "Los Angeles";
        System.out.println(person1.address.city); // 输出:Los Angeles
    }
}

3.序列化的时候,引用类型没有序列化

不了解序列化,可以看看。
Java序列化

import java.io.*;

class Address {
    String city;

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

class Person implements Serializable {
    String name;       // 引用类型
    int age;           // 基本数据类型
    Address address;   // 引用类型

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    // 使用序列化实现浅拷贝
    public Person shallowCopy() throws IOException, ClassNotFoundException {
        // 序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        // 反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (Person) ois.readObject();
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", 30, address);

        // 浅拷贝
        Person person2 = person1.shallowCopy();

        // 输出比较结果
        System.out.println(person1 == person2); // 输出:false(两个对象不同)
        System.out.println(person1.address == person2.address); // 输出:true(引用相同)

        // 修改引用类型字段
        person2.address.city = "Los Angeles";
        System.out.println(person1.address.city); // 输出:Los Angeles
    }
}

什么时候会深拷贝

1.自定义clone方法,处理引用数据类型

class Address implements Cloneable {
    String city;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 深拷贝Address对象
    }
}

class Person implements Cloneable {
    String name;
    Address address;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone();
        cloned.address = (Address) this.address.clone(); // 深拷贝引用类型字段
        return cloned;
    }
}

2.序列化与反序列化处理引用类型

import java.io.*;

class Address implements Serializable {
    String city;

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

class Person implements Serializable {
    String name;
    Address address;

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

    public Person deepCopy() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (Person) ois.readObject();
    }
}

3.引用第三方工具

引入第三方工具 Apache Commonlangs

使用 SerializationUtils工具类来方便的完成序列化。

import org.apache.commons.lang3.SerializationUtils;

import java.io.Serializable;

class Address implements Serializable {
    String city;

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

class Person implements Serializable {
    String name;
    Address address;

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

public class Main {
    public static void main(String[] args) {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", address);
        Person person2 = SerializationUtils.clone(person1); // 深拷贝
    }
}

4.手动复制引用类型字段

class Address {
    String city;

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

    public Address deepCopy() {
        return new Address(this.city); // 手动创建新实例
    }
}

class Person {
    String name;
    Address address;

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

    public Person deepCopy() {
        return new Person(this.name, this.address.deepCopy()); // 递归复制引用类型字段
    }
}

拓展:

 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(this);


        /*
        为什么ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        这里要byteArrayOutputStream.toByteArray()填这个参数?

        因为 ByteArrayOutputStream ByteArrayInputStream 都是从存到内存或者从从内存读写数据
        但是二者没有数据共享机制
        而且ByteArrayOutputStream是把数据存储到了自己在内存中维护的字节数组里。

        ByteArrayInputStream 无法直接访问这个字节数组,需要ByteArrayOutputStream来帮助byteArrayOutputStream.toByteArray()
        
        一个比喻:
        ByteArrayInputStream ByteArrayOutputStream是两个水桶,但是ByteArrayOutputStream是满的,
        需要把水倒进ByteArrayOutputStream ,而toByteArray()就是盛水的盆。
         */
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        Object o = objectInputStream.readObject();
        
        /*
        在java中, ByteArrayInputStream 和 ByteArrayOutputStream二者是独立的,都遵循单一职责原则,所有没有共享机制
        谁也无法访问对方,如果可以随意访问对方,那数据就有可能不一致
        toByteArray() 是生成一个独立的副本,来帮助我们访问,这样也不影响ByteArrayOutputStream本身存储的数据。
         */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值