Java对象克隆的深与浅:彻底搞懂拷贝陷阱

作为一个老牛马程序员,在编写代码的时候肯定遇到过对象需要克隆的问题,但是有时候比如说修改了克隆的副本以后,把原始数据也同步的给改了,这种情况还是挺烦人的,也可能不是我们要的效果。

为什么呢?因为克隆的时候它是分深拷贝和浅拷贝两种情况的,接下来我们就这两种情况进行一个好好的分析!

一、技术核心:堆内存中的对象真相

Java对象在内存中分两部分存储:

java 复制代码
Person person1 = new Person("张三", new Address("北京"));
  • person1 变量本身(对象引用)存在栈内存
  • 实际包含数据(name="张三", address对象)存在堆内存

二、浅拷贝:只复制第一层

浅拷贝只复制原始对象本身,不复制其引用的其他对象。新老对象共享内部引用对象。

实现方式1:默认的clone()方法

java 复制代码
HardwareParameters implements Cloneable {
    String name;
    Address address; // 引用类型

  //重写clone()方法,因为它是的类型是protected的
@Override
public HardwareParameters clone() throws CloneNotSupportedException {
    return (HardwareParameters) super.clone();
    }
}


public static void main(String[] args) throws CloneNotSupportedException {
    HardwareParameters hardwareParameters1 = new HardwareParameters("柜体尺寸", new CaseList("这是第一个呀"));
    //调用重写的clone方法
    HardwareParameters hardwareParameters2 = (HardwareParameters) hardwareParameters1.clone();
    //修改拷贝后的参数
    hardwareParameters2.getCaseList().setTitle("这是第二个呀");

    System.out.println("hardwareParameters2的值:"+hardwareParameters2.getCaseList().getTitle());

    System.out.println("hardwareParameters1的值: "+hardwareParameters1.getCaseList().getTitle());
}

实现方式2:Maven中的BeanUtils类

那么什么时候使用:

  • 只包含基本类型的DTO对象传输
  • 临时对象快照(确保不修改原始对象的基础属性)
  • 配合不可变对象(如String)使用

四、深拷贝:

深拷贝会递归复制所有层级的对象,生成一个完全独立的对象网络。

实现方式:特别简单直接使用JSON序列化

java 复制代码
    public static void main(String[] args)  {
        Gson gson = new Gson();
        HardwareParameters hardwareParameters1 = new HardwareParameters("柜体尺寸", new CaseList("这是第一个呀"));
//        HardwareParameters hardwareParameters2 = new HardwareParameters();

        HardwareParameters hardwareParameters2 = gson.fromJson(gson.toJson(hardwareParameters1), HardwareParameters.class);

//        BeanUtils.copyProperties(hardwareParameters1, hardwareParameters2);
        //修改拷贝后的参数
        hardwareParameters2.getCaseList().setTitle("这是第二个呀");

        System.out.println("hardwareParameters2的值:"+hardwareParameters2.getCaseList().getTitle());

        System.out.println("hardwareParameters1的值: "+hardwareParameters1.getCaseList().getTitle());
    }
}

那么什么时候使用:

  • 需要修改副本且不影响原始对象的场景(如缓存数据更新)
  • 复杂对象图的版本快照
  • 多线程环境下共享数据的安全隔离
  • 游戏开发中的状态保存/恢复

五、关键决策:如何选择拷贝方式?

根据我的项目经验,遵循以下原则能避开90%的坑:

  1. 对象结构简单 → 优先考虑浅拷贝
  2. 包含嵌套可变对象 → 必须深拷贝
  3. 性能敏感 → 浅拷贝速度更快(深拷贝递归消耗大)
  4. 网络传输或持久化 → 序列化实现深拷贝最可靠

总结关键知识点

特性 浅拷贝 深拷贝
复制深度 仅第一层对象 所有层级对象
引用对象处理 共享引用 创建新对象
实现复杂度 简单(默认clone即可) 复杂(需递归或序列化)
数据隔离性 弱(修改影响原对象) 强(完全独立)

下次遇到对象复制需求时,不妨先问自己:此处需要的是复印件,还是完全独立的副本?答案自然就很清晰了。

相关推荐
爱勇宝3 小时前
第 1 章:别把“需求文档”当成真正的需求
前端·后端·程序员
IT_陈寒7 小时前
闭包陷阱让我加了两天班,JavaScript你真行
前端·人工智能·后端
易协同低代码9 小时前
通达OA核心类库TD类深度解析
后端
Gopher_HBo9 小时前
Go语言学习笔记(十八)Gin处理Session
后端
谭光志9 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
她说..9 小时前
Java 默认值设置方式
java·开发语言·后端·springboot
foggyprojects10 小时前
从0开始,一句话启动AI DataAgent
后端·数据分析·ai编程
郡杰10 小时前
一些基础和问题解决
后端
陈随易10 小时前
前端项目部署只要30秒
前端·后端·程序员
YIAN10 小时前
从零手写文件读取 MCP 服务:一文吃透 Model Context Protocol 全链路通信原理
前端·后端·mcp