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即可) 复杂(需递归或序列化)
数据隔离性 弱(修改影响原对象) 强(完全独立)

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

相关推荐
程序员爱钓鱼4 小时前
Go语言实战案例 — 工具开发篇:实现一个图片批量压缩工具
后端·google·go
ChinaRainbowSea6 小时前
7. LangChain4j + 记忆缓存详细说明
java·数据库·redis·后端·缓存·langchain·ai编程
舒一笑6 小时前
同步框架与底层消费机制解决方案梳理
后端·程序员
minh_coo6 小时前
Spring框架事件驱动架构核心注解之@EventListener
java·后端·spring·架构·intellij-idea
白初&7 小时前
SpringBoot后端基础案例
java·spring boot·后端
计算机学姐10 小时前
基于Python的旅游数据分析可视化系统【2026最新】
vue.js·后端·python·数据分析·django·flask·旅游
该用户已不存在10 小时前
你没有听说过的7个Windows开发必备工具
前端·windows·后端
David爱编程11 小时前
深入 Java synchronized 底层:字节码解析与 MonitorEnter 原理全揭秘
java·后端
KimLiu11 小时前
LCODER之Python:使用Django搭建服务端
后端·python·django
再学一点就睡11 小时前
双 Token 认证机制:从原理到实践的完整实现
前端·javascript·后端