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.mzph.cn/pingmian/65099.shtml

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

相关文章

Pytorch | 利用FGSM针对CIFAR10上的ResNet分类器进行对抗攻击

Pytorch | 利用FGSM针对CIFAR10上的ResNet分类器进行对抗攻击 CIFAR数据集FGSM介绍FGSM代码实现FGSM算法实现攻击效果 代码汇总fgsm.pytrain.pyadvtest.py 之前已经针对CIFAR10训练了多种分类器: Pytorch | 从零构建AlexNet对CIFAR10进行分类 Pytorch | 从零构建Vgg…

vue3入门教程:ref函数

基本用法 引入 ref 首先,你需要从 vue 包中引入 ref 函数: import { ref } from vue;创建一个响应式变量 使用 ref 函数来创建一个响应式变量: const count ref(0);这里,count 是一个响应式引用对象,其 .value 属性初…

【IC验证】verilog及systemverilog特殊特性的分析

verilog及systemverilog特殊特性的分析 1.概述2.赋值延迟(0)总结(1)情况一:initial中进行阻塞赋值和非阻塞赋值(不延迟)a代码b 电路图c 结果 (2)时钟a 代码b 电路图c 结果…

Hutool工具包的常用工具类的使用介绍

前言 Hutool 是一个轻量级的 Java 工具类库,提供了非常丰富的工具方法,可以大大减少开发时的重复性工作。它的目标是让 Java 开发更简单、更高效。Hutool 提供了多种常用功能,以下是一些常用工具类的使用介绍: 1. StrUtil - 字符…

考前96天 学习巩固 计算机、数学、英语

2024年12月24日到2025年3月29日共有 96​ 天 一、计算机基础 回顾: 三大思维: 数学 推理/理论 物理 证实/实验 计算机 构造/计算 本质——》抽象/自动化 计算复杂性:空间复杂性、时间复杂性 计算机系统的组成: 1️⃣硬件…

*【每日一题 基础题】 [蓝桥杯 2024 省 B] 好数

[蓝桥杯 2024 省 B] 好数 好数 一个整数如果按从低位到高位的顺序,奇数位(个位、百位、万位……)上的数字是奇数,偶数位(十位、千位、十万位……)上的数字是偶数,我们就称之为“好数”。 给定一…

如何用digital实现一个4位的减法器?

文件可以在下方链接下载: https://download.csdn.net/download/dashuchengtian/90178176 组件拼接如下图所示: 其中A和B是一个4位的输入,Cin表示有无借位,S表示和,Cout表示的是借位输出。 运行结果如下,以…

【人工智能】Python中的机器学习管道:如何用scikit-learn构建高效的ML管道

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在机器学习项目中,数据预处理、特征工程、模型训练与评估是不可或缺的环节。随着项目规模的扩大和复杂度的增加,手动管理这些步骤不仅繁琐…

C++之“流”-第5课.三军联动:流 +操作符+函数重载

如何针对特定函数类型重载流输出操作符&#xff1f;这样做有什么用处&#xff1f;C语言中&#xff0c;“流”、“操作符”、“函数重载” 这三大军团如何配合作战&#xff1f; 前言 C中&#xff0c;“流” 的日常运用&#xff0c;最基本的就是在你的代码里使用 << 和 &g…

Spring AOP 中记录日志

Spring AOP 中记录日志 使用 AOP 和 Spring 提供的 RequestContextHolder 在通知中记录 HTTP 请求相关日志。以下是进阶添加日志功能的完整例子和说明。 完整示例 1. 切面类实现 Aspect Component public class LogAspect {Around("annotation(log)") // 拦截所有…

python+PyPDF2实现PDF的文本内容读取、多文件合并、旋转、裁剪、缩放、加解密、添加水印

目录 读取内容 合并文件 旋转 缩放 裁剪 加密和解密 添加水印 安装&#xff1a;pip install PyPDF2 -i https://pypi.tuna.tsinghua.edu.cn/simple 读取内容 from PyPDF2 import PdfReader, PdfMerger, PdfWriterdef read_pdf(pdf_path):pdf_reader PdfReader(pdf_p…

POD 存储、PV、PVC

目录 容器如何持久化存储&#xff1f; PV和PVC 为什么不能直接在 Pod 或容器中存储数据&#xff1f; 什么是 PV和 PVC&#xff1f; 可以使用本地磁盘空间创建PV吗&#xff1f; 如何让客户端通过ftp上传到远端服务器的POD里面&#xff1f; 另一个POD想访问ftp的POD里面的…

并发编程(19)——引用计数型无锁栈

文章目录 十九、day191. 引用计数2. 代码实现2.1 单引用计数器无锁栈2.2 双引用计数器无锁栈 3. 本节的一些理解 十九、day19 上一节我们学习通过侯删链表以及风险指针与侯删链表的组合两种方式实现了并发无锁栈&#xff0c;但是这两种方式有以下缺点&#xff1a; 第一种方式…

【微信小程序】2|轮播图 | 我的咖啡店-综合实训

轮播图 引言 在微信小程序中&#xff0c;轮播图是一种常见的用户界面元素&#xff0c;用于展示广告、产品图片等。本文将通过“我的咖啡店”小程序的轮播图实现&#xff0c;详细介绍如何在微信小程序中创建和管理轮播图。 轮播图数据准备 首先&#xff0c;在home.js文件中&a…

tortoisegit推送失败

tortoisegit推送失败 git.exe push --progress -- "origin" testLidar:testLidar /usr/bin/bash: gitgithub.com: No such file or directory fatal: Could not read from remote repository. Please make sure you have the correct access rights and the reposit…

京准电钟解读,NTP网络授时服务器如何提升DCS系统效率

京准电钟解读&#xff0c;NTP网络授时服务器如何提升DCS系统效率 京准电钟解读&#xff0c;NTP网络授时服务器如何提升DCS系统效率 NTP 网络授时服务器为防火墙内的网络设备、终端、服务器提供准确、可靠和安全的高精度卫星时间参考&#xff0c;可为它支持数万台支持标准的网…

WebGIS实战开源项目:智慧机场三维可视化(学习笔记)

From&#xff1a;新中地 1.简介 智慧机场解决方案&#xff0c;基于数字化大平台&#xff0c;融合AI、大数据、IoT、视频云、云计算等技术&#xff0c;围绕机场“运控、安防、服务”三大业务领域&#xff0c;构建“出行一张脸”及“运行一张图”两大场景化解决方案。 https://…

Codesoft许可证迁移到新计算机的操作步骤

随着科技的不断发展&#xff0c;我们时常需要升级或更换计算机设备以适应更高的工作要求。然而&#xff0c;在迁移至新计算机时&#xff0c;如何确保Codesoft软件的许可证能够顺利转移并继续在新设备上使用&#xff0c;成为许多用户关心的问题。本文将为您详细介绍Codesoft许可…

C++----类与对象(下篇)

再谈构造函数 回顾函数体内赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 class Date{ public: Date(int year, int month, int day) { _year year; _month month; _day day; } private: int _year; int _mo…

重装系统后的那点事儿

每次重装系统完&#xff0c;总是有这有那的问题&#xff0c;比如这几个常见问题&#xff0c;各种C盘的数据最好在重装系统前备份哦&#xff0c;不然有点小麻烦&#xff0c;要下载好多东西&#xff01;&#xff01; 文章目录 需要新应用打开此 ms-paint新链接搜索框不见了 需要…