解决集合作为类成员变量时深拷贝与浅拷贝的风险

夜杰吖_7143

夜杰吖_7143

2026-03-27

834人浏览

原创

应对外部暴露集合时返回不可变视图或新建浅拷贝;构造/赋值时对可变元素集合做深拷贝;重写clone()需手动处理集合;优先用不可变集合+构建器模式规避拷贝问题。

解决集合作为类成员变量时深拷贝与浅拷贝的风险

当集合(如 List、Set、Map)作为类的成员变量时,若未正确处理拷贝逻辑,极易引发数据意外共享或状态污染——浅拷贝只复制引用,深拷贝才真正隔离对象图。关键不在“要不要拷贝”,而在“何时拷贝”和“如何安全拷贝”。

明确成员集合是否应对外暴露可变性

多数情况下,类内部持有的集合是封装状态的一部分,外部不应直接修改。若提供 getter 方法,返回原集合引用即构成浅拷贝风险。

  • ❌ 错误示例:public List<string> getItems() { return items; }</string> —— 调用方修改返回列表会直接影响内部状态
  • ✅ 安全做法:返回不可变视图,如 Collections.unmodifiableList(items);或每次调用都新建浅拷贝(new ArrayList(items))
  • ⚠️ 注意:不可变视图只是防御性包装,不解决内部集合被其他路径修改的问题;若需彻底隔离,必须在构造/赋值时做深拷贝

在构造函数或 setter 中主动深拷贝传入的集合

若类设计为持有集合的“所有权”,则所有外部传入的集合都应在接收时完成深拷贝,避免后续被原始引用篡改。

  • 对元素为基本类型或不可变对象(如 String、Integer)的集合,只需浅拷贝容器本身(如 new ArrayList(src))即可视为“逻辑深拷贝”
  • 对元素为可变对象的集合(如 List<person></person>),需递归深拷贝每个元素:src.stream().map(Person::new).collect(Collectors.toList())(假设 Person 有拷贝构造函数)
  • 避免使用序列化方式实现深拷贝,性能差且要求所有嵌套类型可序列化,易埋隐患

重写 clone() 时必须显式处理集合成员

Object.clone() 默认执行浅拷贝,若类依赖 clone() 提供拷贝能力,集合字段不会自动深拷贝。

  • ✅ 正确做法:在重写的 clone() 方法中,对每个集合成员手动创建新实例并填充拷贝后的内容
  • 例如:result.items = new ArrayList(this.items);(元素不可变时)或 result.items = this.items.stream().map(Item::copy).collect(...)
  • ❌ 不要仅调用 super.clone() 后忽略集合字段——这是最常见的 clone() 深拷贝遗漏点

优先使用不可变集合 + 构建器模式替代可变成员集合

从设计源头规避拷贝问题:让类在构造完成后不再允许修改集合内容。

  • 使用 ImmutableList.copyOf(items)(Guava)或 List.copyOf(items)(Java 10+)初始化 final 集合字段
  • 若需后期更新,通过构建器返回新实例(如 withAddedItem(item)),内部创建新集合,旧实例不受影响
  • 这种方式天然消除浅/深拷贝抉择困境,线程安全且语义清晰
PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
guava包作用
guava包作用

guava是一个java库,增强了java标准库,提供更有效率和易于使用的集合、实用程序、缓存和并发工具。想了解更多guava的相关内容,可以阅读本专题下面的文章。

2024.05.29

4471

6

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.02

5399

3

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

2023.08.10

3618

6

golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

2025.09.05

450

5

golang map相关教程
golang map相关教程

本专题整合了golang map相关教程,阅读专题下面的文章了解更多详细内容。

2025.11.16

323

7

golang map原理
golang map原理

本专题整合了golang map相关内容,阅读专题下面的文章了解更多详细内容。

2025.11.17

493

20

java判断map相关教程
java判断map相关教程

本专题整合了java判断map相关教程,阅读专题下面的文章了解更多详细内容。

2025.11.27

263

6

Buffalo框架数据库开发全教程
Buffalo框架数据库开发全教程

本专题围绕Buffalo框架数据库开发,讲解database.yml多环境配置、soda与fizz迁移生成回滚、模型结构体标签、增删改查与条件查询、一对多与多对多关联、数据校验、回调钩子、事务处理及原生SQL执行能力。

2026.09.23

180

15

Buffalo框架路由与请求处理实操指南
Buffalo框架路由与请求处理实操指南

本专题讲解Buffalo框架路由与请求处理机制,涵盖路由注册与分组、资源路由、Handler编写规范、Context上下文方法、参数绑定、中间件编写挂载、Session与Cookie读写、Flash消息及错误页面定制方法。

2026.09.23

80

15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.3万人学习