Java对象拷贝的浅与深:如何选择?

在日常开发中,我们经常需要将一个对象的属性复制到另一个对象中。无论是使用第三方工具类还是自己手动实现,都会涉及到浅拷贝深拷贝的问题。本文将深入讨论浅拷贝的潜在风险,并给出几种实现深拷贝的方式,帮助大家避免潜在的坑。

一、什么是浅拷贝?

在Java中,浅拷贝只会复制对象的基本类型字段,而对引用类型字段只复制引用的内存地址,不会递归复制引用的对象。这意味着,多个对象共享同一个引用,修改其中一个对象的引用字段可能会影响其他对象。

示例:Hutool和Apache Common工具类的浅拷贝

在项目中我们常使用工具类如 HutoolBeanUtil.copyProperties() 或 Apache Commons 的 BeanUtils.copyProperties() 来进行对象的拷贝。这些工具类默认情况下都执行浅拷贝。

本篇以Hutool的举例,依赖如下

      <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.1</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
import cn.hutool.core.bean.BeanUtil;@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
class User {private Long userId;private String name;private String email;public static void main(String[] args) {User oldUser = new User(1L, "lps", "email");User newUser = new User();// 使用 Hutool 工具类拷贝属性BeanUtil.copyProperties(oldUser, newUser);// 修改原对象的 userIdoldUser.setUserId(2L);// 输出新对象的属性System.out.println(newUser); // 结果:User(userId=1, name=lps, email=email)}
}

这个例子中的 Hutool 工具类对 oldUser 进行了浅拷贝。修改 oldUseruserId 并不会影响 newUser,因为 Long 是不可变类型。但如果 User 类中包含引用类型(例如 List、自定义对象),浅拷贝就会带来问题。

二、浅拷贝的潜在问题

浅拷贝最大的风险在于引用类型数据的共享。当你修改一个对象中的引用字段时,拷贝出来的对象也会随之改变。

示例:浅拷贝带来的问题
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
class Address {private String city;
}@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
class User {private Long userId;private String name;private String email;private Address address;public static void main(String[] args) {Address address = new Address("Beijing");User oldUser = new User(1L, "lps", "email", address);User newUser = new User();// 浅拷贝 oldUser 到 newUserBeanUtil.copyProperties(oldUser, newUser);// 修改 oldUser 的地址oldUser.getAddress().setCity("Shanghai");// 输出新对象的地址System.out.println(newUser.getAddress().getCity()); // 结果:"Shanghai"}
}

在这个例子中,修改了 oldUseraddress 对象,导致 newUseraddress 也被改变。这就是浅拷贝的典型问题。

三、深拷贝:如何避免共享引用的问题?

为了避免浅拷贝带来的问题,深拷贝通过递归地复制所有引用对象来确保两个对象完全独立。实现深拷贝有多种方式,下面介绍几种常见的做法。

1. 手动实现深拷贝

最常见的方法是手动在 clone() 方法中递归调用所有引用对象的 clone() 方法。

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
class Address implements Cloneable {private String city;@Overrideprotected Address clone() {try {return (Address) super.clone();} catch (CloneNotSupportedException e) {throw new AssertionError(); }}
}@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
class User implements Cloneable {private Long userId;private String name;private String email;private Address address;@Overrideprotected User clone() {try {User cloned = (User) super.clone();cloned.setAddress(this.address.clone());  // 手动深拷贝return cloned;} catch (CloneNotSupportedException e) {throw new AssertionError();}}
}

手动实现深拷贝虽然可以控制每个引用的拷贝逻辑,但对于复杂对象来说,编写和维护都比较繁琐。


2. 使用序列化实现深拷贝

序列化是另一种常见的深拷贝方法,它通过将对象序列化为字节流,再反序列化为新的对象来实现深拷贝。

public User deepCopy() {try {ByteArrayOutputStream byteOut = new ByteArrayOutputStream();ObjectOutputStream out = new ObjectOutputStream(byteOut);out.writeObject(this);ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());ObjectInputStream in = new ObjectInputStream(byteIn);return (User) in.readObject();} catch (IOException | ClassNotFoundException e) {throw new RuntimeException("深拷贝失败", e);}
}

虽然序列化方法较为简单通用,但它要求所有参与拷贝的类都实现 Serializable 接口,并且序列化和反序列化的性能开销较大。

四、总结
  • 浅拷贝:通过工具类如 HutoolApache Commons 可以轻松实现属性拷贝,但要小心引用类型字段的共享问题。
  • 深拷贝:如果需要完整独立的对象,深拷贝是必要的。你可以选择手动实现 clone() 或使用序列化方式实现。

在选择合适的拷贝方式时,应根据对象的复杂度和性能需求作出决策。如果对象层级简单且性能要求较高,手动实现 clone() 是不错的选择;如果对象层级较复杂,可以考虑使用序列化来简化深拷贝的实现。

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

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

相关文章

gin 通过 OpenTelemetry 实现链路追踪

相关阅读:https://juejin.cn/post/7275550548946337829https://juejin.cn/post/7275550548946337829 OpenTelemetry 是 Cloud Native Computing Foundation (CNCF) 下的一个开源项目,旨在标准化遥测数据的生成和收集;遥测数据包括日志、指标和跟踪。 Gin 是一个用 Go (Gol…

69页PPT全面预算管理体系的框架与落地

一、明确企业战略目标企业战略目标是预算指标体系确立的根本出发点。它为预算指标的设定提供了方向和指导。 深入分析企业长期发展规划 企业需要对自身的长期发展规划进行全面、深入的分析。这包括对市场趋势、行业竞争态势、技术发展方向等外部环境因素的研究&#xff0c;以…

从API到应用:直播美颜SDK如何助力主播美颜工具开发

美颜SDK&#xff08;软件开发工具包&#xff09;作为一套强大的工具&#xff0c;能够帮助开发者快速构建美颜功能。通过集成API&#xff0c;开发者可以实现复杂的实时美颜效果&#xff0c;助力主播美颜工具的开发和应用。 一、美颜SDK的基本功能 美颜SDK是一套为开发者提供的…

【2024高教社杯全国大学生数学建模竞赛】B题模型建立求解

目录 1问题重述1.1问题背景1.2研究意义1.3具体问题 2总体分析3模型假设4符号说明&#xff08;等四问全部更新完再写&#xff09;5模型的建立与求解5.1问题一模型的建立与求解5.1.1问题的具体分析5.1.2模型的准备 目前B题第一问的详细求解过程以及对应论文部分已经完成&#xff…

中国生态环境胁迫数据(栅格/县域尺度)-为研究生态环境压力提供数据支撑

中国生态环境胁迫矢量数据&#xff08;2000-2010年&#xff09; 数据介绍 2000-2010年中国生态环境胁迫数据为2000-2010年中国范围内人口、农业生产等生态环境胁迫因子的空间分布图&#xff0c;包括人口密度、农药使用强度、化肥施用强度。数据可用于分析全国生态环境胁迫因子…

QT笔记 - QProcess读取外部程序(进程)消息

简要介绍 QProcess可用于在当前程序中启动独立的外部程序(进程)&#xff0c;并进行通讯&#xff0c;通讯原理是通过程序的输入或输出流&#xff0c;即通过c中的printf()和或c的std::cout等。 函数 void QProcess::start(const QString & program, const QStringList &am…

系统分析师7:数学与经济管理

文章目录 1 图论应用1.1 最小生成树1.2 最短路径1.3 网络与最大流量 2 运筹方法2.1 线性规划2.2 动态规划2.2.1 供需平衡问题2.2.2 任务指派问题 3 预测与决策3.1 不确定型决策分析3.2 风险型决策3.2.1 决策树3.2.2 决策表 4 随机函数5 数学建模 1 图论应用 ①最小生成树 连接…

Android 存储之 SharedPreferences 框架体系编码模板

一、SharedPreferences 框架体系 1、SharedPreferences 基本介绍 SharedPreferences 是 Android 的一个轻量级存储工具&#xff0c;它采用 key - value 的键值对方式进行存储 它允许保存和读取应用中的基本数据类型&#xff0c;例如&#xff0c;String、int、float、boolean …

解决Type-C接口供电难题:LDR6328取电协议芯片的关键作用

在智能设备快速发展的今天&#xff0c;Type-C接口因其便捷性、高速传输能力和双向充电功能&#xff0c;已成为众多设备的标准接口。然而&#xff0c;随着设备功率需求的不断提升&#xff0c;Type-C接口的供电难题也日益凸显。为解决这一难题&#xff0c;LDR6328取电协议芯片应运…

HTB-Pennyworth(cve查询 和 exp使用)

前言 各位师傅大家好&#xff0c;我是qmx_07,今天给大家讲解Pennyworth靶场 渗透过程 信息搜集 服务器端口开放了8080http端口 访问网站 服务器使用jenkins cms系统&#xff0c;版本是2.289.1 通过弱口令爆破&#xff0c;账户是root,密码是password 通过命令执行nday 连…

2.1ceph集群部署准备-硬件及拓扑

硬件配置及建议 时至今日&#xff0c;ceph可以运行在各种各样的硬件平台上&#xff0c;不管是传统的x86架构平台(intel 至强系列、基于amd的海光系列等)&#xff0c;还是基于arm的架构平台(比如华为鲲鹏)&#xff0c;都可以完美运行ceph集群&#xff0c;展现了其强大的适应能力…

结合AI图片增强、去背景,如何更好的恢复旧照片老照片?

随着数字时代的到来&#xff0c;我们越来越依赖于技术来保存和恢复珍贵的记忆。在众多技术中&#xff0c;人工智能&#xff08;AI&#xff09;在恢复旧照片方面展现出了其独特的魅力和潜力。AI不仅能够修复破损的照片&#xff0c;还能够增强图像质量&#xff0c;让那些褪色的记…

WPS中JS宏使用说明(持续优化...)

前言 好久没发文章了&#xff0c;今天闲来无事发篇文章找找之前的码字感觉。 正文 最近在写教案&#xff0c;发现之前的技术又可以派上用场了。就是JS&#xff0c;全称JavaScript&#xff0c;这个语言太强大了&#xff0c;我发现WPS里的宏现在默认就是JS。功能选项如下图&…

开源模型应用落地-qwen2-7b-instruct-LoRA微调合并-ms-swift-单机单卡-V100(十三)

一、前言 本篇文章将使用ms-swift去合并微调后的模型权重&#xff0c;通过阅读本文&#xff0c;您将能够更好地掌握这些关键技术&#xff0c;理解其中的关键技术要点&#xff0c;并应用于自己的项目中。 二、术语介绍 2.1. LoRA微调 LoRA (Low-Rank Adaptation) 用于微调大型语…

算法练习题14——leetcode84柱形图中最大的矩形(单调栈)

题目描述&#xff1a; 解题思路&#xff1a; 要解决这个问题&#xff0c;我们需要找到每个柱子可以扩展的最大左右边界&#xff0c;然后计算以每个柱子为高度的最大矩形面积。 具体步骤如下&#xff1a; 计算每个柱子左侧最近的比当前柱子矮的位置&#xff1a; 使用一个单调…

vue3获取视频时长、码率、格式等视频详细信息

前言&#xff1a; 我们在上传视频需要视频的帧数等信息的时候&#xff0c;上传组件无法直接读取帧数等信息 方法&#xff1a;通过mediainfo.js来获取视频的帧率、总帧数和视频的总时长 mediainfo.js地址&#xff0c;想详细了解的可以去看看git地址&#xff1a;https://githu…

【最新华为OD机试E卷-支持在线评测】查找充电设备组合(200分)-多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,…

【机器学习-神经网络】循环神经网络

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…

软件测试基础总结+面试八股文

一、什么是软件&#xff1f; 软件是计算机系统中的程序和相关文件或文档的总称。 二、什么是软件测试&#xff1f; 说法一&#xff1a;使用人工或自动的手段来运行或测量软件系统的过程&#xff0c;以检验软件系统是否满足规定的要求&#xff0c;并找出与预期结果之间的差异…

C++笔记15•数据结构:二叉树之二叉搜索树•

二叉搜索树 1.二叉搜索树 概念&#xff1a; 二叉搜索树又称二叉排序树也叫二叉查找树&#xff0c;它可以是一棵空树。 二叉树具有以下性质: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值 若它的右子树不为空&#xff0c;则右子树上所有节点的值都…