浅拷贝和深拷贝(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;}@Overrideprotected 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;}@Overrideprotected 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中,浅拷贝和深拷贝是两种复制对象的方式。
浅拷贝
浅拷贝只复制对象的第一层属性,若属性是引用类型(如对象、数组),则复制的是引用。常用方法包括:
-
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
-
扩展运算符
const obj1 = { a: 1, b: { c: 2 } }; // 展开运算符(Spread Operator)是 JavaScript 中的一种语法,用于将可迭代对象(如数组或字符串)展开为独立的元素。它使用三个连续的点号(...)作为操作符。 // 展开运算符可以在多种情况下使用,包括数组、对象和函数调用等。 const shallowCopy = { ...obj1 }; shallowCopy.b.c = 3; console.log(obj1.b.c); // 输出: 3
深拷贝
深拷贝会递归复制对象及其所有嵌套的属性,确保新对象与原对象完全独立。常用方法包括:
-
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
-
递归函数
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中,使用浅拷贝和深拷贝的场景各有不同:
使用浅拷贝的场景
简单数据复制:当对象只包含基本数据类型(如字符串、数字等),可以使用浅拷贝。
const original = { a: 1, b: 2 }; const copy = { ...original };
组件状态管理:在React等框架中,更新状态时使用浅拷贝来保持性能。
setState(prevState => ({ ...prevState, newProp: value }));
合并对象:合并多个对象时,浅拷贝可以快速实现。
const merged = Object.assign({}, obj1, obj2);
使用深拷贝的场景
复杂对象处理:当对象包含嵌套的引用类型,需要独立复制时。
const deepCopy = JSON.parse(JSON.stringify(complexObj));
状态管理:在Redux等状态管理库中,深拷贝可以防止状态不小心被修改。
const newState = deepClone(state);
数据处理:在处理API返回的复杂数据结构时,确保原始数据不被改变。
const processedData = deepClone(apiResponse);
总结:
- 浅拷贝 适合简单对象和性能优化场景。
- 深拷贝 用于复杂数据结构,确保数据完整性和独立性。
三、总结
- 浅拷贝:复制对象的引用,对于引用类型的属性会共享内存。
- 深拷贝:递归复制对象,确保独立性,常用于需要完整复制对象及其嵌套属性的场景。
相关文章:

浅拷贝和深拷贝(Java 与 JavaScript)
一、Java 浅拷贝和深拷贝 在Java中,浅拷贝和深拷贝的主要区别在于对对象的引用和内容的复制方式。 浅拷贝 Java 的类型有基本数据类型和引用类型,基本数据类型是可以由 CPU 直接操作的类型,无论是深拷贝还是浅拷贝,都是会复制出…...
力扣每日一题 2306.公司命名
做题过程中使用到的java语法: 1.从一个字符串中取出一部分字符串: String str "Hello, World!"; String part str.substring(7); // 从索引7开始到字符串末尾 System.out.println(part); // 输出: World! class Solution { public lo…...

HTML-DOM模型
1.DOM模型 window对象下的document对象就是DOM模型。 DOM描绘了一个层次化的节点树,每一个节点就是一个html标签,而且每一个节点也是一个DOM对象。 2.操作DOM 2.1.获取DOM对象常用方法 获取DOM对象的常用方法有如下几种: getElementById(…...
vue项目报错: At least one is required in a single file component.的主要原因及解决办法
本篇文章主要讲解 vue项目报错: At least one is required in a single file component.的主要原因及解决办法 作者:任聪聪 日期:2024年9月25日 报文信息: Compiled with problems: ERROR in ./src/xxxx.vue Module Error (from …...

03DSP学习-利用syscfg配置IO
上一篇博客介绍了syscfg,对syscfg有了初步的了解,但是在真正使用上它之前,还不能理解他是一个神器。 (在写博客的时候,我是在从头到尾重新完成这个步骤,希望对初学者有点帮助) 找到Board Component 打开syscfg文件&…...

web - RequestResponse
##Request&Response 1,Request和Response的概述 Request是请求对象,Response是响应对象。这两个对象在我们使用Servlet的时候有看到: 此时,我们就需要思考一个问题request和response这两个参数的作用是什么? request:获取请…...
个人文章汇总
文章模块文章汇总心得&资料 真正优秀的人,更懂得尊重别人 如何用沟通解决80%的工作问题 一个IT青年北漂四年的感悟 史上最污技术解读 操作系统相关 操作系统基础 操作系统:从工厂的角度来理解进程线程操作系统:详述对进程和线程的认识操作…...

Java | Leetcode Java题解之第436题寻找右区间
题目: 题解: class Solution {public int[] findRightInterval(int[][] intervals) {int n intervals.length;int[][] startIntervals new int[n][2];int[][] endIntervals new int[n][2];for (int i 0; i < n; i) {startIntervals[i][0] inter…...
大模型智能体在金融公告理解领域的应用 | OPENAIGC开发者大赛高校组AI创新之星奖
在第二届拯救者杯OPENAIGC开发者大赛中,涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到,我们特意开设了优秀作品报道专栏,旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者,希望能带给…...
链表入门(LeetCode题目)
来源:左程云算法 链表的题目我们经常是有思路但是实现起来总有些小问题,所以是准备笔试应多加练习的一类题 206. 反转链表 这道题我们可以新开链表来存,但是如果面试中有这道题,面试官让你优化又该如何呢?所以我们采…...

kibana开启访问登录认证
编辑es配置文件,添加以下内容开启es认证 vim /etc/elasticsearch/elasticsearch.yml http.cors.enabled: true http.cors.allow-origin: "*" http.cors.allow-headers: Authorization xpack.security.enabled: true xpack.security.transport.ssl.enable…...
Java 14Java 15新特性概述
一、Java 14 发布于2020年3月17日。Java 14主要新特性如下: JEP 305:Pattern Matching for instanceof (Preview)instanceof 的模式匹配(预览) JEP 358:Helpful NullPointerExceptions 有用的 NullPointerExceptions…...

流量特征随机ua修改
作为一个蓝队吗喽,总是能看见因为ua头特征而直接被拦截的ip,当然了还有些是通过X-Forwarded-For被拦截的(X-Forwarded-For:fofa.info,不拦你才怪), 主要是通过python的mitmproxy和fake_useragent两个模块进行实现,代码量极低 fr…...
CSP-S 2024 提高级 第一轮(初赛) 阅读程序(3)
【题目】 CSP-S 2024 提高级 第一轮(初赛) 阅读程序(3) 1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn 1000000 5; 7 const int P1 998…...
如何在 Rust 中通过 Rumqttc 实现 MQTT 通信
Rust 简介 Rust 是一门系统级编程语言,以其卓越的性能、并发能力以及内存安全特性著称。Rust 由 Mozilla 推出,目标是在现代软件开发中提供一种安全高效的编程语言。其设计旨在提供安全、并发和高效的编程体验,同时保持开发效率和代码质量不…...

广东高校建设AIGC实验室时需要注意哪几个关键点?
随着人工智能技术的飞速发展,特别是生成式人工智能(AIGC)在各行各业中的广泛应用,它已经成为推动新一轮科技革命和产业变革的关键力量。教育部等相关部门近年来也高度重视人工智能领域的人才培养工作,强调要加快推动高…...
设计模式-PIMPL 模式
PIMPL(Pointer to IMPLementation),又称Opaque Pointer模式或编译防火墙,是一种在C中广泛应用的编程技术。其核心思想是将类的实现细节从公共接口中分离出来,通过指向实现类的指针来访问类的具体功能。这种模式在提高代…...
Docker部署MongoDB教程
嘿,大家好!今天我在三丰云免费服务器上进行了一次激动人心的MongoDB部署测试。这款免费云服务器1核CPU、1G内存、10G硬盘、5M带宽,是不错的免费服务器选择。 首先,让我们简要介绍一下使用到的Docker和MongoDB软件。Docker是一个开…...

堆排序易错点
1.建堆和调整堆(插入和删除) 建堆和调整堆的过程是不一样的: 建堆 从非终端节点编号的结点开始依次建立大根堆,例如: 拿第2个图说,首先比较-1,7,从中选一个小的,即“-1”…...

安卓13长按电源按键直接关机 andriod13不显示关机对话框直接关机
总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 有些设备需要在长按电源键的时候,直接关机。不需要弹出对话框进行询问。 2.问题分析 过滤电源按键,需要在系统里面处理的话,那么我们需要熟悉android的事件分发,然后再…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...