Java 深拷贝全面解析

1. 引言

在 Java 编程中,对象之间的复制是一个常见的需求。根据复制的深度不同,我们可以将复制分为浅拷贝和深拷贝。本文将深入探讨 深拷贝(Deep Copy) 的概念、应用场景、具体实现方法及其优缺点,并提供一些实用的建议和注意事项。


2. 深拷贝的定义

深拷贝(Deep Copy) 是指创建一个新的对象,并且递归地复制该对象中的所有属性,包括其引用类型的属性。这意味着新对象与其副本之间没有任何共享的数据,即原始对象及其副本各自拥有独立的一份数据。如果原始对象中包含其他对象的引用,则这些被引用的对象也会被复制,而不是简单地复制引用。

例如,如果你有一个 Person 对象,其中包含一个 Address 对象,那么深拷贝不仅会复制 Person 对象本身,还会复制 Address 对象,确保两个 Person 对象中的 Address 是完全独立的。

3个 🌰🌰🌰 帮助理解:

  • 1: 日常生活中的例子
    想象你有一个装满照片的相册,这些照片是你珍贵的记忆。如果你想给家人也准备一本一模一样的相册,你会怎么做?最简单的方法是去复印店,让他们帮你一张张地复印所有的照片,然后重新装订成一个新的相册。这就是深拷贝:你得到了一个全新的、独立的相册,任何对新相册的修改都不会影响原来的相册。
  • 角度 2: 文件系统的比喻
    考虑一下计算机文件系统。当你复制一个文件夹时,操作系统不仅会复制文件夹本身,还会递归地复制文件夹内的所有文件和子文件夹。这种方式确保了新文件夹和原文件夹之间没有任何共享的内容,你可以自由地修改新文件夹中的文件,而不会影响到原始文件夹。这也是深拷贝的概念:递归地复制所有内容,确保完全独立。
  • 角度 3: 图书馆借阅系统
    假设你是一个图书馆管理员,有读者想要借阅一本书。为了方便其他读者也可以阅读这本书,你可以选择为每个读者制作一份完全相同的副本。这些副本是独立的,读者可以在上面做笔记、画线,而不会影响到其他读者手中的副本。这就是深拷贝:每个副本都是独立的,互不影响。

3. 使用背景与应用场景

工业界的常见使用场景
  1. 避免修改影响

    • 在多用户系统或多人协作环境中,为了避免对共享对象的修改影响到其他用户或模块,通常需要使用深拷贝来确保每个用户或模块都有自己的独立副本。
  2. 多线程环境

    • 在并发编程中,多个线程可能需要操作相同的数据结构。为了避免线程间的干扰,可以为每个线程创建数据的深拷贝,确保线程安全。
  3. 数据备份与恢复

    • 在进行复杂计算或状态管理时,保存某个时刻的数据快照非常重要。通过深拷贝,可以在不改变原始数据的情况下保存当前状态,以便之后能够恢复到这个状态。
  4. 安全传递数据

    • 当你需要将数据传递给另一个组件或模块,但又不希望接收方能够修改你的原始数据时,可以通过深拷贝来实现这一点,确保数据的安全性。
  5. 缓存机制

    • 在某些缓存实现中,为了防止缓存中的数据被意外修改,通常会对缓存项进行深拷贝,确保缓存中的数据与实际数据保持一致。

4. 具体使用方式

方法 1: 使用构造函数或方法

通过编写一个接收另一个对象作为参数的构造函数或方法来实现深拷贝。这种方式需要手动为每个成员变量赋值,特别是对于复杂对象,还需要递归地调用其深拷贝方法。

public class Person {private String name;private Address address;// 构造函数public Person(String name, Address address) {this.name = name;this.address = new Address(address); // 确保 Address 也有深拷贝机制}// 深拷贝构造函数public Person(Person other) {this.name = other.name; // 基本类型直接赋值this.address = new Address(other.address); // 复杂类型使用深拷贝}
}
方法 2: 实现 Cloneable 接口并重写 clone() 方法

Java 提供了 Cloneable 接口和 Object.clone() 方法来支持对象的复制。默认情况下,clone() 方法执行的是浅拷贝。为了实现深拷贝,你需要重写 clone() 方法并在其中处理复杂类型的深拷贝。

public class Person implements Cloneable {private String name;private Address address;@Overrideprotected Object clone() throws CloneNotSupportedException {Person cloned = (Person) super.clone(); // 浅拷贝cloned.address = (Address) this.address.clone(); // 深拷贝 Address 对象return cloned;}
}public class Address implements Cloneable {private String city;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone(); // 因为 Address 只有基本类型,所以这里可以是浅拷贝}
}
方法 3: 序列化与反序列化

利用 Java 的序列化机制(Serializable 接口),你可以将对象序列化为字节流,然后再反序列化为新的对象。这种方法适用于大多数情况下的深拷贝,但需要注意性能问题以及类结构的变化可能导致的问题。

import java.io.*;public class Person implements Serializable {private String name;private Address address;public Person deepCopy() throws IOException, ClassNotFoundException {// 序列化ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream out = new ObjectOutputStream(bos);out.writeObject(this);// 反序列化ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream in = new ObjectInputStream(bis);return (Person) in.readObject();}
}
方法 4: 使用第三方库

有些第三方库如 Apache Commons Lang 提供了 SerializationUtils 类,可以简化深拷贝的操作。

import org.apache.commons.lang3.SerializationUtils;public class Person {private String name;private Address address;public Person deepCopy() {return SerializationUtils.clone(this);}
}

5. 常见优点

  • 独立性:深拷贝确保了新对象与原对象之间的完全独立,修改一个不会影响另一个。
  • 安全性:在多线程环境下或者当数据需要传递给不可信的代码时,深拷贝提供了更好的数据保护。
  • 一致性:深拷贝可以帮助你在不同时间点保存数据的状态,保证数据的一致性和可追溯性。
  • 灵活性:通过深拷贝,你可以在不改变原始数据的情况下自由地操作副本,增加了程序的灵活性。

6. 注意事项

  • 性能问题:深拷贝通常比浅拷贝更耗时,特别是在处理大型对象或嵌套较深的对象结构时。选择适当的深拷贝方法以平衡代码简洁性和性能。
  • 循环引用:如果对象图中有循环引用,必须特别小心处理,以避免无限递归。
  • 不可变对象:对于不可变对象(immutable objects),可以直接返回对象本身,因为它们不会被修改。
  • 线程安全:深拷贝并不保证线程安全,这取决于具体的实现方式和所使用的同步机制。
  • 内存消耗:深拷贝会创建全新的对象实例,可能会导致较大的内存消耗,尤其是在频繁复制大量数据时。

7. 进阶

1. 循环引用的处理

当对象图中存在循环引用时,简单的递归深拷贝可能会导致无限递归。解决方法包括:

  • 记忆化:使用一个映射表(如 HashMap)来记录已经复制过的对象,避免重复复制。
  • 弱引用:对于某些特定类型的循环引用,可以考虑使用弱引用(WeakReference)或其他策略来打破循环。
2. 性能优化

对于大规模数据或频繁的深拷贝操作,可以考虑以下优化措施:

  • 懒加载:仅在需要时才进行深拷贝,减少不必要的资源消耗。
  • 批量处理:对于多个对象的深拷贝,可以考虑批量处理以提高效率。
  • 自定义深拷贝逻辑:针对特定对象结构,编写高效的自定义深拷贝逻辑,避免通用方法带来的开销。
3. 深拷贝框架的选择

在实际项目中,选择合适的深拷贝框架非常重要。除了上述提到的 Apache Commons Lang,还有其他框架如:

  • Dozer:一个对象映射工具,支持深拷贝和对象转换。
  • MapStruct:用于生成类型安全的 Bean 映射代码,支持深拷贝。
  • ModelMapper:一个灵活的对象映射库,支持深拷贝和其他高级功能。

8. 总结

深拷贝是 Java 编程中非常重要的概念和技术,它确保了对象的完全独立复制,避免了共享引用带来的副作用。通过理解深拷贝的定义、应用场景、具体实现方法及其优缺点,你可以在实际开发中更好地应用这一技术,提升代码的质量和可靠性。同时,注意处理循环引用、优化性能等问题,以确保深拷贝的有效性和高效性。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ES 磁盘使用率检查及处理方法

文章目录 1. 检查原因2. 检查方法3. 处理方法3.1 清理数据3.2 再次检查磁盘使用率 1. 检查原因 磁盘使用率在 85%以下,ES 可正常运行,达到 85%及以上会影响 PEIM 数据存储。 在 ES 磁盘分配分片控制策略中,为了保护数据节点的安全&#xff0…

论文解读 | EMNLP2024 一种用于大语言模型版本更新的学习率路径切换训练范式

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 点击 阅读原文 观看作者讲解回放! 作者简介 王志豪,厦门大学博士生 刘诗雨,厦门大学硕士生 内容简介 新数据的不断涌现使版本更新成为大型语言模型(LLMs&#xff…

【Linux 系统负载详情解析】

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默, 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……) 2、学会Oracle数据库入门到入土用法(创作中……) 3、手把…

欲海航舟:探寻天性驱动下的欲望演变与人生驾驭

欲海航舟:探寻天性驱动下的欲望演变与人生驾驭。 欲望之源起,本乎天性。 鸿蒙初辟,生灵乍现,欲望即随人之性灵而生,如花木之根柢,虽隐匿于地下,却为生长之根基。 人之初诞,懵懂无…

WebRTC 环境搭建

主题 本文主要描述webrtc开发过程中所需的环境搭建 环境: 运行环境:ubuntu20.04 Node.js环境搭建 安装编译 Node.js 所需的依赖包: sudo apt-get updatesudo apt-get install -y build-essential libssl-dev下载 Node.js 源码: curl -sL https://…

QT-------认识QT

QT简介 QT是一个跨平台的C图形用户界面应用程序框架,由挪威Trolltech公司于1991年开发并发布。它为开发者提供了一套丰富的类库和工具,用于创建各种类型的应用程序,包括桌面应用、移动应用、嵌入式系统应用等。QT具有高度的可定制性和可扩展…

SpringBoot配置文件、热部署、YAML语法、配置文件值注入

SpringBoot的配置文件 文章目录 SpringBoot的配置文件1.SpringBoot的热部署2.配置文件2.1配置文件的作用2.2YAML配置文件:2.3YAML 与 JSON 和 XML 的对比 3.YAML语法3.1键值对3.2值的写法3.3对象、Map(属性和值)(键值对&#xff0…

基于BiTCN双向时间卷积网络实现电力负荷多元时序预测(PyTorch版)

Bidirectional Temporal Convolutional Network \begin{aligned} &\text{\Large \color{#CDA59E}Bidirectional Temporal Convolutional Network}\\ \end{aligned} ​Bidirectional Temporal Convolutional Network​ Bidirectional Temporal Convolutional Network (BiTC…

【JavaEE】Spring Web MVC

目录 一、Spring Web MVC简介 1.1 MVC简介1.2 Spring MVC1.3 RequestMapping注解1.3.1 使用1.3.2 RequestMapping的请求设置 1.3.2.1 方法11.3.2.2 方法2 二、Postman介绍 2.1 创建请求2.2 界面如下:2.3 传参介绍 一、Spring Web MVC简介 官方文档介绍&#xff…

低成本Type - C单口便携屏的全方位解析

在科技迅猛发展的当下,便携式电子产品在日常生活中的地位愈发重要。Type - C单口便携显示器作为新兴显示设备脱颖而出。本文将简述其特点、应用场景、技术原理与发展趋势。 一、特点 Type - C单口便携显示器最大的优势是便携性。它以Type - C接口进行数据传输与供电…

TestMAX/DFT Compiler:时序单元的类型、连接顺序和后DFT优化

相关阅读 TestMAX/DFT Compilerhttps://blog.csdn.net/weixin_45791458/category_12865937.html?spm1001.2014.3001.5482 时序单元的状态 未映射的时序单元(Unmapped Sequential Cell) 在Design Compiler读取了一个RTL设计后,Design Compiler内置的HDL Compiler工…

「瑞仕云曜璟庭」多轨交通+成熟配套 杨浦滨江宜居之高地

1. 均价不足11w 内环内真正高性价比 作为顶级城市资源的集中地,内环向来是购房者争相角逐的重中之重。 但是,内环很小,价格很高。 目前内环的二手房最高挂牌价已经达到约31.95万/㎡,最高成交价已经达到约33.79万/㎡&#xff…

基于STM32的智能家居环境监控系统设计

目录 引言系统设计 硬件设计软件设计系统功能模块 环境监控模块控制模块显示模块系统实现 硬件实现软件实现系统调试与优化结论与展望 1. 引言 随着智能家居技术的发展,环境监控系统已经成为家居管理的重要组成部分。智能家居环境监控系统通过实时监测室内温度、湿…

游戏开发线性空间下PS工作流程

前言 使用基于物理的渲染,为了保证光照计算的准确,需要使用线性空间; 使用线性空间会带来一个问题,ui 在游戏引擎中的渲染结果与 PS 中的不一致: PS(颜色空间默认是sRGB伽马空间)&#xff1a…

JavaWeb(一) | 基本概念(web服务器、Tomcat、HTTP、Maven)、Servlet 简介

1. 基本概念 1.1、前言 web开发: web,网页的意思,www.baidu.com静态 web html,css提供给所有人看的数据始终不会发生变化! 动态 web 淘宝,几乎是所有的网站;提供给所有人看的数据始终会发生变化&#xf…

在国产电脑上运行PDFSAM软件使用pdf分割合并交替混合处理pdf文档

软件下载地址: https://sourceforge.net/projects/pdfsam/files/ 需要注意事项,系统需要java环境,确认系统有java环境,根据软件版本需求安装对应的java运行环境。 下载pdfsam-4.3.4-linux.tar.gz安装包,解压,将runt…

windows下Redis的使用

Redis简介: Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API。 Redis通常被称为数据结构服务器,因为值(value&#xff…

【Linux系统编程】:信号(4)——信号的处理

1.进程地址空间内核区 我们之前都是谈进程地址空间的用户区,接下来我们谈谈内核区。 进程地址空间中的内核区是操作系统内核在进程地址空间中所占据的特定区域。 一般情况下,一个操作系统只有一个内核区以及一个内核级页表。而进程可以有多个页表。不论…

用Python写炸金花游戏

文章目录 **代码分解与讲解**1. **扑克牌的生成与洗牌**2. **给玩家发牌**3. **打印玩家的手牌**4. **定义牌的优先级**5. **判断牌型**6. **确定牌型优先级**7. **比较两手牌的大小**8. **打印结果** 完整代码 以下游戏规则: 那么我们要实现的功能,就是…

掌握软件工程基础:知识点全面解析【chap03、chap05、chap06、chap08、chap09】

chap03 UML UML定义了哪些视图?分别具有什么特点? 1.用例图(Use case diagram) 用例图展示各类外部执行者与系统所提供的用例之间的连接。一个用例是系统所提供的一个功能的描述,执行者是指使用这些用例的…