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

news2024/12/28 4:29:28

 一、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);

总结:

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

三、总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2167540.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ANSYS Workbench晶体结构Voronoi泰森多边形建模

在ANSYS Workbench内建立包含晶格及晶格边界在内的晶体结构模型&#xff0c;可用于模拟多种物理现象及材料行为。晶格模型适用于研究微观尺度下的材料性质&#xff0c;以及它们如何影响宏观性能&#xff0c;如进行金属晶体结构建模及断裂的模拟等。 晶体结构模型可采用CAD Vo…

fastapp-微信开发GPT项目第一课

0. 开发说明 在学习开发本项目之前&#xff0c;必须保证有以下知识储备和环境工具。 技术栈说明python>3.9、pydantic>2.7.1python基础&#xff0c;http协议fastapi>0.111.0web协程异步框架&#xff0c;有web开发基础&#xff0c;异步编程&#xff0c;类型标注[pyth…

银河麒麟操作系统中查看动态库函数的方法

银河麒麟操作系统中查看动态库函数的方法 1、查看单个动态库中的函数2、查找特定函数位于哪个动态库中 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Linux系统&#xff0c;包括银河麒麟操作系统中&#xff0c;动态库&#xff08;.so文件…

单片机项目合集列表与专栏说明——Excel合集列表目录查阅(持续更新)

阿齐Archie《单片机项目合集》专栏项目 为方便查找本专栏的项目&#xff0c;特整理Excel合集列表供查阅&#xff08;可搜索或按系列查找&#xff09; 持续更新链接如下&#xff1a; 阿齐单片机项目合集 (kdocs.cn)https://www.kdocs.cn/l/cmrxCxJN05YN 打开链接如下Exce表所…

【LeetCode:219. 存在重复元素 II + 哈希表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

人工智能领域-----机器学习和深度学习的区别

机器学习和深度学习都是人工智能领域中的重要概念&#xff0c;它们之间存在以下一些区别&#xff1a; 一、定义与概念 机器学习&#xff1a; 是一种让计算机自动学习和改进的方法&#xff0c;通过从数据中学习模式和规律&#xff0c;从而能够对新的数据进行预测或决策。涵盖了…

Android compose 的基本环境搭建

1.创建项目 导入版本 1.gradle/libs.versions.toml [versions] accompanistPermissions "0.36.0" agp "8.5.0-beta01" coilCompose "2.7.0" constraintlayoutComposeVersion "1.0.1" hiltAndroid "2.51.1" hiltNavi…

git其他人有改动,自己这边不要pull,先stash一下,pull了然后apply自己的stash。

git其他人有改动&#xff0c;自己这边不要pull&#xff0c;先stash一下&#xff0c;pull了然后apply自己的stash&#xff0c; git 冲突了怎么处理处理&#xff1f; git会提示冲突的文件 On branch experiments You have unmerged paths.(fix conflicts and run "git comm…

代码随想录 -- 回溯 -- 子集II

90. 子集 II - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 题目中说明nums中可能包含重复元素&#xff0c;所以要去重。 去重的前提是将数组nums排序&#xff01; 递归参数&#xff1a;nums&#xff0c;index&#xff0c;path递归出口&#xff1a;当遍历完num…

编译和链接笔记

翻译环境和运⾏环境 在ANSI C的任何⼀种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执⾏的机器指令&#xff08;⼆进制指令&#xff09;。 第2种是执⾏环境&#xff0c;它⽤于实际执⾏代码。 1.翻译环境 其实翻译环境…

这3个证书在手,失业了也不怕.

在忙碌的职场生活中&#xff0c;考取一两个证书已成为众多职场人士的热门选择。 拥有这些证书不仅能为个人职业发展带来机遇&#xff0c;还能为职业转型铺平道路&#xff0c;特别是在主业遇到波折时。 接下来&#xff0c;让我们一同探索三个适合上班族的热门证书。 PMP认证&…

Unity 新NavMesh演示(1)

新版Navmash 导航寻路 保姆级入门讲解-CSDN博客 演示&#xff1a; 第一步 给场景中的BK添加导航网格表面组件 并设置详细参数 第二步 为player添加导航网格代理 并编写脚本设置target public class Text : MonoBehaviour {private NavMeshAgent agent;public Transform targe…

【从0开始自动驾驶】ros2编写自定义消息 msg文件和msg文件嵌套

【从0开始自动驾驶】ros2编写自定义消息 msg文件和msg文件嵌套 在工作空间内新建一个功能包在msg内创建对应的msg文件创建名为TestMsg.msg的文件创建名为TestSubMsg.msg的文件&#xff08;在前一个msg文件中引用&#xff09;修改CmakeList.txt修改package.xml文件编译 在工作空…

获取交易软件【热度排行数据】2024年9月26日,一股淡淡的牛味

2024年9月26日&#xff0c;一股淡淡的牛味 概念热度的排行榜和行业热度排行榜。 像是这种类型的数据&#xff0c;能不能加到我们的量化模型里面&#xff0c;作为选股和下单指令的判断条件之一呢&#xff1f; 下面图片&#xff0c;有很多数据接口&#xff0c;可以1对1帮助您解…

水面巡检船垃圾漂浮物检测系统源码分享

水面巡检船垃圾漂浮物检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of …

由于找不到vcruntime140.dll的原因分析及6种解决方法分享

在计算机使用过程中&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是找不到vcruntime140.dll文件。那么&#xff0c;究竟vcruntime140.dll是什么&#xff1f;为什么会出现找不到的情况呢&#xff1f;本文将详细解析vcruntime140.dll的作用以及丢失的原因&#…

MySql Explain优化命令使用

MySql Explain优化命令使用 truncate table student // 自增id 从 0 开始 delete from student // 自增id 会保留 &#xff0c; 108 区别&#xff1a; 1&#xff1a;自增id 2&#xff1a;delete 可以恢复 truncate 无法恢复 前言 EXPLAIN 是一个用于获取 SQL 语句执行计划的…

从碳基到硅基,个人记忆留存方兴未艾!

关注我们 - 数字罗塞塔计划 - 说到记忆&#xff0c;我们可能会想到海马体&#xff0c;海马体是人类大脑中负责将短时记忆向长期存储转换的部分。科学家指出&#xff0c;海马体能够帮助大脑建立信息归档系统&#xff0c;并在需要的时候&#xff0c;快速将有用的信息检索出来。因…

HarmonyOS鸿蒙开发实战( Beta5.0)图片压缩实践方案

鸿蒙HarmonyOS NEXT开发实战往期文章必看&#xff08;持续更新......&#xff09; HarmonyOS NEXT应用开发性能实践总结 HarmonyOS NEXT应用开发案例实践总结合集 最新版&#xff01;“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线&#xff01;&#xff08;从零基础入门…