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

目录

一、浅谈深拷贝和浅拷贝

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 深拷贝

// 各线程独立处理自己的对象
相关推荐
间彧10 小时前
对比GraalVM Native Image与传统JVM,在内存管理方面各自适合哪些具体业务场景?
java
明洞日记10 小时前
【数据结构手册002】动态数组vector - 连续内存的艺术与科学
开发语言·数据结构·c++
福尔摩斯张10 小时前
《C 语言指针从入门到精通:全面笔记 + 实战习题深度解析》(超详细)
linux·运维·服务器·c语言·开发语言·c++·算法
daidaidaiyu10 小时前
Spring IOC 源码学习一 基本姿势
java·spring
LSL666_10 小时前
SpringBoot自动配置类
java·spring boot·后端·自动配置类
。puppy10 小时前
MySQL 远程登录实验:通过 IP 地址跨机器连接实战指南
android·adb
甜鲸鱼11 小时前
Java与MySQL中的枚举(Enum)
java·mysql
xxxxxxllllllshi11 小时前
【LeetCode Hot100----14-贪心算法(01-05),包含多种方法,详细思路与代码,让你一篇文章看懂所有!】
java·数据结构·算法·leetcode·贪心算法
pengzhuofan11 小时前
Sentinel 服务保护
java·微服务·sentinel
6***379411 小时前
Java安全
java·开发语言·安全