一文彻底搞懂Java浅拷贝与深拷贝

在Java开发中,浅拷贝(Shallow Copy)深拷贝(Deep Copy) 是必须掌握的基础概念。很多Bug的出现,尤其是数据莫名被修改、缓存污染、并发问题,都是源于错误的拷贝方式。

本文会详细介绍两者区别、实现方法,并明确指出常见7大深拷贝的典型使用场景,每个场景都带明确的代码示例。


📌一、浅拷贝和深拷贝的区别

  • 浅拷贝:只复制对象本身和引用地址。
  • 深拷贝:复制整个对象树,完全独立于原对象。

浅拷贝示例:

java 复制代码
Person p2 = BeanUtil.copyProperties(p1, Person.class); //浅拷贝
p2.address.city = "上海";
System.out.println(p1.address.city); // 上海,原对象被修改了

🔧二、常见的深拷贝实现方式(推荐)

① 构造方法(推荐)

ini 复制代码
java
复制编辑
Person(Person other) {
    this.name = other.name;
    this.address = new Address(other.address.city);
}

② 工厂方法(推荐)

java 复制代码
static Person deepCopy(Person other) {
    return new Person(other.name, new Address(other.address.city));
}

③ 使用SerializationUtils.clone()(简单高效)

借助 Apache Commons Lang 提供的工具类:

Maven依赖:

xml 复制代码
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.14.0</version>
</dependency>

实体类必须实现Serializable接口:

java 复制代码
class Address implements Serializable {
    String city;
}

class Person implements Serializable {
    String name;
    Address address;
}

使用示例:

java 复制代码
Person p2 = SerializationUtils.clone(p1); // 深拷贝
p2.address.city = "上海";
System.out.println(p1.address.city); // 北京,原对象未被影响

🚩三、明确需要深拷贝的7大典型场景(含代码示例)

场景1:原对象不允许被修改(请求参数、缓存对象)

从缓存或HTTP请求拿到对象后,不允许直接修改,需创建副本:

java 复制代码
User userFromRequest = getUserFromRequest();
User userCopy = SerializationUtils.clone(userFromRequest);
userCopy.setRole("admin"); // 安全修改

否则:可能修改原请求对象,导致参数污染。


场景2:多线程环境(并发安全)

并发场景中对象共享容易引发竞态条件:

java 复制代码
for (User u : userList) {
    User copy = SerializationUtils.clone(u); 
    executor.submit(() -> processUser(copy)); 
}

否则:线程间数据混乱。


场景3:临时计算或试验(模拟计算)

模拟某个业务逻辑的计算,保护原始数据不受影响:

java 复制代码
Order originalOrder = orderService.findById(123);
Order tmpOrder = SerializationUtils.clone(originalOrder);
tmpOrder.applyDiscount(0.8); // 临时计算

否则:原对象被误修改。


场景4:缓存中取出对象后修改(避免缓存污染)

从Redis或本地缓存获取数据后,不直接修改缓存对象:

java 复制代码
Product product = cache.get("product:123");
Product copy = SerializationUtils.clone(product);
copy.setPrice(copy.getPrice() * 0.9); // 安全修改副本

否则:其他读取缓存数据的人拿到脏数据。


场景5:框架自动注入对象(Spring Bean等)

Spring自动注入的单例Bean不允许修改其状态:

java 复制代码
@Component
public class UserService {
    private final Config config;
    
    public UserService(Config config) {
        this.config = SerializationUtils.clone(config);
    }
}

否则:全局配置被污染。


场景6:历史快照、版本管理(撤销、回滚)

实现数据快照,以便后续撤销或历史版本管理:

java 复制代码
List<Document> history = new ArrayList<>();
history.add(SerializationUtils.clone(currentDocument)); // 保存快照
currentDocument.edit(...);
//回滚时:
currentDocument = SerializationUtils.clone(history.get(0));

否则:无法准确回滚或撤销修改。


场景7:ORM框架中级联更新避免误更新(如JPA、MyBatis)

保存实体副本到数据库时,避免级联修改原实体:

java 复制代码
User oldUser = userRepository.findById(1);
User newUser = SerializationUtils.clone(oldUser);
newUser.setId(null); // 清除ID保存为新记录
userRepository.save(newUser);

否则:原始记录意外被修改或覆盖。


🚨 四、常见工具类(BeanUtil)的误区

BeanUtil.copyProperties() 都是浅拷贝,可能会带来数据共享问题:

java 复制代码
BeanUtil.copyProperties(p1, p2);
p2.address.city = "上海";
System.out.println(p1.address.city); // 上海,污染原数据

因此,建议使用时务必手动补充引用字段的深拷贝。


🛠️ 五、深拷贝性能的提示与注意事项

  • 深拷贝涉及对象递归创建,性能消耗较大。
  • 日常实践:按需深拷贝关键字段,平衡性能与安全。

📝 六、面试官常问的几个核心点(应对技巧)

  • 浅拷贝深拷贝的区别?
  • 常用深拷贝方法有哪些?
  • SerializationUtils.clone() 为什么可以深拷贝?
  • BeanUtil是浅拷贝吗?
  • 举例说明什么场景要用深拷贝?

🚀 七、完整总结(直接背诵级)

项目 浅拷贝 深拷贝
复制对象 本身引用地址 整个对象树
数据影响 会影响原对象 不影响原对象
场景 简单对象,不修改 缓存、并发、快照、框架对象保护
性能 较差
  • 推荐用构造方法工厂方法SerializationUtils.clone()实现深拷贝。
  • 工具类(如BeanUtil)默认浅拷贝,注意避免陷阱。

🎯 以上内容涵盖了Java浅拷贝和深拷贝的核心知识点与面试常考内容,帮助你彻底掌握相关概念。

相关推荐
渡我白衣7 小时前
并行的野心与现实——彻底拆解 C++ 标准并行算法(<execution>)的模型、陷阱与性能真相
java·开发语言·网络·c++·人工智能·windows·vscode
czlczl200209257 小时前
SpringBoot中web请求路径匹配的两种风格
java·前端·spring boot
bill4477 小时前
BPMN2.0,flowable工作流指向多节点,并且只能选择其中一个节点的处理方式
java·工作流引擎·bpmn
2022.11.7始学前端7 小时前
n8n第四节 表单触发器:让问卷提交自动触发企微消息推送
java·前端·数据库·n8n
Catcharlotte7 小时前
异常(3)
java
岁岁种桃花儿7 小时前
Java应用篇如何基于Redis共享Session实现短信登录
java·开发语言
资深低代码开发平台专家7 小时前
通用编程时代正在向专用化分层演进
java·大数据·c语言·c++·python
开心香辣派小星7 小时前
23种设计模式-17备忘录模式
java·设计模式·备忘录模式
q_19132846957 小时前
基于SpringBoot2+Vue2+uniapp的考研社区论坛网站及小程序
java·vue.js·spring boot·后端·小程序·uni-app·毕业设计
源码技术栈7 小时前
Java智能诊所管理系统源码 SaaS云门诊运维平台源码
java·大数据·运维·人工智能·源码·诊所·门诊