浅拷贝与深拷贝详解:概念、代码示例与后端应用场景

目录

一、浅谈深拷贝和浅拷贝

1、拷贝对象

对象拷贝(Object Copy)指将一个对象的属性复制到另一个类型相同的对象中。

在编程中,拷贝对象非常常见,通常是为了在新的环境中复用已有对象的数据。


1.浅拷贝 (Shallow Copy)

浅拷贝仅复制对象的引用,而不是对象本身。新旧对象共享同一块内存区域,修改其中一个对象会影响另一个。

  • 拷贝基本类型

    直接复制基本数据类型的值。

  • 拷贝引用类型

    仅复制引用地址,因此修改一个对象的数据会影响另一个对象。

  • 总结

    多个引用指向同一对象,修改其中一个会影响所有引用该对象的地方。


2.深拷贝 (Deep Copy)

深拷贝会创建一个新的对象,且新旧对象不共享内存,修改新对象不会影响原对象。

深拷贝复制源对象的所有属性,包括所有引用类型的对象,因此源对象和拷贝对象完全独立。

  • 总结
    深拷贝创建了一个完全独立的新对象,源对象和新对象之间没有任何引用关系。

2、为什么要使用深拷贝

避免共享引用

浅拷贝只是复制引用,可能导致修改副本时影响原对象。深拷贝确保创建独立副本,避免这种问题。

线程安全

在多线程环境中,多个线程可能同时修改同一个对象,造成数据冲突。使用深拷贝可以让每个线程操作独立副本,从而避免线程安全问题。


3、代码验证目录

1.cloneable接口

源码

知识点

  • 如果类没有实现 Cloneable 接口,调用 clone() 方法时会抛出异常。
  • 需要实现 Cloneable 接口后,才能正常调用 clone() 方法。

代码案例


2.浅拷贝示例

java 复制代码
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

public class ShallowDeepCopyDemo
{
    public static void main(String[] args) throws CloneNotSupportedException
    {
        m1();
    }

    private static void m1() throws CloneNotSupportedException
    {
        Emp emp = new Emp("z3", 15, "雷军", "CEO");
        System.out.println("原始对象:" + emp.getBoss().getTitle());

        Emp emp2 = (Emp) emp.clone();
        System.out.println("拷贝对象:" + emp2.getBoss().getTitle());

        System.out.println();
        emp2.getBoss().setTitle("CTO");
        System.out.println("------emp2拷贝对象修改Title=CTO,是否会影响原始对象");

        System.out.println("原始对象:" + emp.getBoss().getTitle());
        System.out.println("拷贝对象:" + emp2.getBoss().getTitle());
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Boss implements Cloneable
{
    private String bossName;
    private String title;

    @Override
    protected Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Emp implements Cloneable
{
    private String empName;
    private Integer age;
    private Boss boss;

    public Emp(String empName, Integer age, String bossName, String title)
    {
        this.empName = empName;
        this.age = age;
        this.boss = new Boss(bossName, title);
    }

    @Override
    protected Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }
}

3.深拷贝示例

修改Emp

当使用深拷贝,值就被修改了


二、浅拷贝和深拷贝的使用场景

1、浅拷贝在 Web 后端的应用

浅拷贝一般用在 对象之间的属性复制轻量数据传输,因为它快、开销小。

1.DTO ↔ Entity 转换

在 Controller 层收到请求 DTO,需要转成 Entity 保存数据库时,常用浅拷贝工具类:

java 复制代码
import org.springframework.beans.BeanUtils;

UserDTO dto = new UserDTO("Alice", 20);
UserEntity entity = new UserEntity();
BeanUtils.copyProperties(dto, entity);  // 浅拷贝

System.out.println(entity.getName());  // Alice

👉 常见于:

  • Request → Entity
  • Entity → ResponseVO

1. 浅拷贝的本质

浅拷贝做的事情就是:

  • 创建一个 新的外层对象UserEntity)。
  • 把源对象(UserDTO)的字段值逐个赋值给目标对象的字段。
  • 如果字段是基本类型 / 不可变对象int, long, String, Integer...) → 拷贝的是值。
  • 如果字段是可变引用对象List, Map, 自定义对象) → 拷贝的是引用。

所以,BeanUtils.copyProperties(dto, entity) 只是"复制了一层属性",不会深度复制内部引用对象


2. 为什么 DTO ↔ Entity 常用浅拷贝

我们来看常见代码:

java 复制代码
UserDTO dto = new UserDTO("Alice", 20);
UserEntity entity = new UserEntity();
BeanUtils.copyProperties(dto, entity);

System.out.println(entity.getName()); // Alice

这里 nameStringageint

  • String 是不可变对象,即便两个对象指向同一个字符串引用也没问题。
  • int 是基本类型,直接复制值,完全独立。

所以这种场景下,浅拷贝就够了。


3. 什么时候浅拷贝会出问题?

假设 DTO 里有个集合字段:

java 复制代码
class UserDTO {
    private String name;
    private List<String> roles;
}

class UserEntity {
    private String name;
    private List<String> roles;
}

代码:

java 复制代码
UserDTO dto = new UserDTO();
dto.setName("Alice");
dto.setRoles(new ArrayList<>(List.of("ADMIN")));

UserEntity entity = new UserEntity();
BeanUtils.copyProperties(dto, entity);

entity.getRoles().add("USER");

System.out.println(dto.getRoles()); // ❌ ["ADMIN", "USER"] 也被改了

👉 因为 roles 是可变对象,浅拷贝后 dto.rolesentity.roles 指向同一个 List


4. 为什么大部分场景没问题

  • Request → Entity 时,通常 DTO 里字段是简单类型(String, int),浅拷贝足够。
  • Entity → ResponseVO 时,也主要是简单类型(id, name, age, status),浅拷贝完全满足需求。
  • 很少会直接把复杂的集合对象在 DTO 和 Entity 之间原样拷贝。即便有,也常常在 Service 层做 额外转换(比如 MapStruct、手写转换逻辑)。

5. 总结

  • BeanUtils.copyProperties浅拷贝:只复制属性,不递归复制引用对象。
  • DTO ↔ Entity 里,字段多为简单类型,所以浅拷贝完全够用。
  • 如果有集合或嵌套对象,浅拷贝会共享引用,可能导致数据串改 → 这时要么手动处理,要么用 MapStruct 这种强类型映射工具。

2、深拷贝在 Web 后端的应用

深拷贝适合 数据快照并发安全防止数据污染 的场景。

1. 数据快照 / 历史归档

例如电商订单支付前,需要保存一份"冻结快照":

java 复制代码
import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper mapper = new ObjectMapper();
Order orderSnapshot = mapper.readValue(mapper.writeValueAsString(order), Order.class); // JSON 深拷贝

orderSnapshot.setStatus("FROZEN");

👉 这样修改快照不会影响到原始订单。


2. 并发安全

多线程处理时,如果多个线程共享对象,必须深拷贝避免互相污染:

java 复制代码
Order original = orderService.getOrderById(1001);
Order threadOrder = (Order) SerializationUtils.clone(original); // Apache Commons 深拷贝

// 各线程独立处理自己的对象
相关推荐
DolphinScheduler社区3 小时前
# 3.1.8<3.2.0<3.3.1,Apache DolphinScheduler集群升级避坑指南
java·大数据·开源·apache·任务调度·海豚调度
枫叶丹43 小时前
【Qt开发】输入类控件(六)-> QDial
开发语言·qt
思考的笛卡尔3 小时前
Go语言实战:高并发服务器设计与实现
服务器·开发语言·golang
Le1Yu3 小时前
黑马商城微服务项目准备工作并了解什么是微服务、SpringCloud
java·微服务·架构
ZhengEnCi3 小时前
🚀创建第一个 SpringBoot 应用-零基础体验开箱即用的神奇魅力
java·spring boot
宠友信息3 小时前
仿小红书短视频APP源码:Java微服务版支持小程序编译的技术解析
java·微服务·音视频
努力努力再努力wz3 小时前
【C++进阶系列】:万字详解智能指针(附模拟实现的源码)
java·linux·c语言·开发语言·数据结构·c++·python
凤年徐3 小时前
【C++】string的模拟实现
c语言·开发语言·c++
敲代码的嘎仔3 小时前
JavaWeb零基础学习Day2——JS & Vue
java·开发语言·前端·javascript·数据结构·学习·算法