浅拷贝和深拷贝(Java 与 JavaScript)

 一、Java 浅拷贝和深拷贝

在Java中,浅拷贝和深拷贝的主要区别在于对对象的引用和内容的复制方式。

浅拷贝

        Java 的类型有基本数据类型和引用类型,基本数据类型是可以由 CPU 直接操作的类型,无论是深拷贝还是浅拷贝,都是会复制出另一份。而引用类型仅仅是一个指针,指向的是这个对象在堆内存中分配的内存。

举例: 

class Address {
    String city;

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

class Person implements Cloneable {
    String name;
    Address address;

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

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

public class ShallowCopyExample {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", address);
        Person person2 = (Person) person1.clone();

        // 修改 person2 的地址
        person2.address.city = "Los Angeles";

        System.out.println(person1.address.city); // 输出: Los Angeles
    }
}

        浅拷贝:仅仅是将这个指针拷贝了一份出来,只复制对象的引用,两个指针都指向相同的堆内存地址,原始对象和拷贝对象共享相同的内部对象

深拷贝

举例:

class Address {
    String city;

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

    // 深拷贝方法
    public Address deepCopy() {
        return new Address(this.city);
    }
}

class Person implements Cloneable {
    String name;
    Address address;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone();
        cloned.address = this.address.deepCopy(); // 深拷贝地址
        return cloned;
    }
}

public class DeepCopyExample {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", address);
        Person person2 = (Person) person1.clone();

        // 修改 person2 的地址
        person2.address.city = "Los Angeles";

        System.out.println(person1.address.city); // 输出: New York
    }
}

        深拷贝:拷贝的就不仅仅是一个指针,还会在堆内存中将原来的对象也拷贝出来一份,原始对象和拷贝对象之间完全独立

        需要注意的一点是:多线程中使用浅拷贝时,多个线程可能会同时修改共享的内部对象,导致数据不一致。这种情况需要通过同步机制(如 synchronized)来避免数据冲突。

当然,深拷贝还常常使用序列化的方式来实现:

import java.io.*;

// 可序列化的类
class Address implements Serializable {
    String city;

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

class Person implements Serializable {
    String name;
    Address address;

    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();
    }
}

public class SerializationDeepCopyExample {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", address);
        Person person2 = person1.deepCopy();

        // 修改 person2 的地址
        person2.address.city = "Los Angeles";

        System.out.println(person1.address.city); // 输出: New York
    }
}

或者单独抽象出一个方法出来实现: 

/**
 *  序列化实现深拷贝:
 *  这个方法首先将original对象序列化到一个ByteArrayOutputStream中,然后立即使用ByteArrayInputStream从这个字节流中反序列化对象,
 *  从而得到一个完全独立的深拷贝。这种方法的优点是它相对简单,且能自动处理对象图中的所有复杂关系。然而,它可能比直接使用拷贝构造函数或者克隆方法更慢,
 *  且只有实现了Serializable接口的对象才能被复制。
 */

public class DeepCopyViaSerialization {
    // 实现深拷贝的方法
    public static <T> T deepCopy ( T original ) {
        T copied = null;
        try {
            // 创建一个字节流
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            // 序列化
            try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
                oos.writeObject(original);
            }
            // 反序列化
            try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
                copied = (T) ois.readObject();
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return copied;
    }
}

 

二、JavaScript 浅拷贝和深拷贝

在JavaScript中,浅拷贝和深拷贝是两种复制对象的方式。

浅拷贝

浅拷贝只复制对象的第一层属性,若属性是引用类型(如对象、数组),则复制的是引用。常用方法包括:

  1. Object.assign()

    // Object.assign 缺点: 仅能实现浅拷贝,不适用于深层嵌套对象
    const obj1 = { a: 1, b: { c: 2 } };
    const shallowCopy = Object.assign({}, obj1);
    shallowCopy.b.c = 3;
    console.log(obj1.b.c); // 输出: 3
  2. 扩展运算符

    const obj1 = { a: 1, b: { c: 2 } };
    // 展开运算符(Spread Operator)是 JavaScript 中的一种语法,用于将可迭代对象(如数组或字符串)展开为独立的元素。它使用三个连续的点号(...)作为操作符。
    // 展开运算符可以在多种情况下使用,包括数组、对象和函数调用等。
    const shallowCopy = { ...obj1 };
    shallowCopy.b.c = 3;
    console.log(obj1.b.c); // 输出: 3

深拷贝

深拷贝会递归复制对象及其所有嵌套的属性,确保新对象与原对象完全独立。常用方法包括:

  1. JSON.stringify() 和 JSON.parse()

    const obj1 = { a: 1, b: { c: 2 } };
    const deepCopy = JSON.parse(JSON.stringify(obj1));
    deepCopy.b.c = 3;
    console.log(obj1.b.c); // 输出: 2
  2. 递归函数

    function deepClone(obj) {
        if (obj === null || typeof obj !== 'object') return obj;
        const copy = Array.isArray(obj) ? [] : {};
        for (let key in obj) {
            copy[key] = deepClone(obj[key]);
        }
        return copy;
    }

在JavaScript中,使用浅拷贝和深拷贝的场景各有不同:

使用浅拷贝的场景

  1. 简单数据复制:当对象只包含基本数据类型(如字符串、数字等),可以使用浅拷贝。

    const original = { a: 1, b: 2 }; const copy = { ...original };
  2. 组件状态管理:在React等框架中,更新状态时使用浅拷贝来保持性能。

    setState(prevState => ({ ...prevState, newProp: value }));
  3. 合并对象:合并多个对象时,浅拷贝可以快速实现。

    const merged = Object.assign({}, obj1, obj2);

使用深拷贝的场景

  1. 复杂对象处理:当对象包含嵌套的引用类型,需要独立复制时。

    const deepCopy = JSON.parse(JSON.stringify(complexObj));
  2. 状态管理:在Redux等状态管理库中,深拷贝可以防止状态不小心被修改。

    const newState = deepClone(state);
  3. 数据处理:在处理API返回的复杂数据结构时,确保原始数据不被改变。

    const processedData = deepClone(apiResponse);

总结:

  • 浅拷贝 适合简单对象和性能优化场景。
  • 深拷贝 用于复杂数据结构,确保数据完整性和独立性。

三、总结

  • 浅拷贝:复制对象的引用,对于引用类型的属性会共享内存。
  • 深拷贝:递归复制对象,确保独立性,常用于需要完整复制对象及其嵌套属性的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值