深入剖析 Java 中的深拷贝与浅拷贝:原理、实现与最佳实践

本系列可作为JAVA学习系列的笔记,文中提到的一些练习的代码,小编会将代码复制下来,大家复制下来就可以练习了,方便大家学习。

点赞关注不迷路!您的点赞、关注和收藏是对小编最大的支持和鼓励!

本文篇幅较长,建议先收藏再食用!


系列文章目录

JAVA学习 DAY1 初识JAVA

JAVA学习 DAY2 java程序运行、注意事项、转义字符

JAVA学习 DAY3 注释与编码规范讲解

JAVA学习 DAY4 DOS操作讲解及实例

JAVA学习 DAY5 变量&数据类型 [万字长文!一篇搞定!]

JAVA学习 DAY6 运算符

JAVA学习 DAY7 程序逻辑控制【万字长文!一篇搞定!】

JAVA学习 DAY8 方法【万字长文!一篇搞定!】

JAVA学习 DAY9 数组【万字长文!一篇搞定!】

JAVA学习 DAY10 类和对象【万字长文!一篇搞定!】

JAVA学习 DAY11 类和对象_续1【万字长文!一篇搞定!】

JAVA学习 DAY12 继承和多态【万字长文!一篇搞定!】

JAVA学习 DAY13 抽象类和接口【万字长文!一篇搞定!】


拓展文章

Sublime安装指导!只需四步!

图文详解汉诺塔问题:从递归思想到代码实现(零基础也能看懂)

Java避坑指南:千万别在构造方法中调用重写的方法!(附代码案例+执行流程全解析)

Java 接口学习核心难点深度解析

深入剖析 Java 中的深拷贝与浅拷贝:原理、实现与最佳实践


目录

系列文章目录

前言

一、深拷贝与浅拷贝的核心概念

[1.1 从内存模型理解拷贝本质](#1.1 从内存模型理解拷贝本质)

[1.2 生活比喻:让概念更易理解](#1.2 生活比喻:让概念更易理解)

[1.3 核心区别对比表](#1.3 核心区别对比表)

[二、Java 中浅拷贝的实现方式](#二、Java 中浅拷贝的实现方式)

[2.1 基于 Cloneable 接口的浅拷贝(标准方式)](#2.1 基于 Cloneable 接口的浅拷贝(标准方式))

[2.1.1 核心原理](#2.1.1 核心原理)

[2.1.2 代码示例:浅拷贝基础实现](#2.1.2 代码示例:浅拷贝基础实现)

[2.1.3 运行结果与分析](#2.1.3 运行结果与分析)

[2.1.4 注意点:String 类型的 "特殊表现"](#2.1.4 注意点:String 类型的 “特殊表现”)

[2.2 手动浅拷贝(构造方法 / Setter)](#2.2 手动浅拷贝(构造方法 / Setter))

[2.2.1 代码示例:构造方法实现浅拷贝](#2.2.1 代码示例:构造方法实现浅拷贝)

[2.3 浅拷贝的适用场景](#2.3 浅拷贝的适用场景)

[三、Java 中深拷贝的实现方式](#三、Java 中深拷贝的实现方式)

[3.1 重写 clone () 方法实现深拷贝](#3.1 重写 clone () 方法实现深拷贝)

[3.1.1 核心思路](#3.1.1 核心思路)

[3.1.2 代码示例:递归 clone 实现深拷贝](#3.1.2 代码示例:递归 clone 实现深拷贝)

[3.1.3 运行结果与分析](#3.1.3 运行结果与分析)

[3.1.4 注意点:集合的深拷贝](#3.1.4 注意点:集合的深拷贝)

[3.2 序列化 / 反序列化实现深拷贝](#3.2 序列化 / 反序列化实现深拷贝)

[3.2.1 核心原理](#3.2.1 核心原理)

[3.2.2 实现条件](#3.2.2 实现条件)

[3.2.3 代码示例:序列化实现深拷贝](#3.2.3 代码示例:序列化实现深拷贝)

[3.2.4 运行结果与分析](#3.2.4 运行结果与分析)

[3.3 手动逐层级深拷贝](#3.3 手动逐层级深拷贝)

[3.3.1 代码示例:构造方法实现手动深拷贝](#3.3.1 代码示例:构造方法实现手动深拷贝)

[3.4 第三方工具实现深拷贝](#3.4 第三方工具实现深拷贝)

[3.4.1 Apache Commons Lang(SerializationUtils)](#3.4.1 Apache Commons Lang(SerializationUtils))

[3.4.2 Gson(JSON 序列化)](#3.4.2 Gson(JSON 序列化))

[3.4.3 Jackson(JSON 序列化)](#3.4.3 Jackson(JSON 序列化))

[3.5 深拷贝的适用场景](#3.5 深拷贝的适用场景)

四、深浅拷贝的性能对比

[4.1 测试代码](#4.1 测试代码)

[4.2 测试结果(参考)](#4.2 测试结果(参考))

[4.3 结论](#4.3 结论)

五、常见问题与避坑指南

[5.1 误区 1:认为 String 类型需要深拷贝](#5.1 误区 1:认为 String 类型需要深拷贝)

[5.2 误区 2:集合的 clone () 是深拷贝](#5.2 误区 2:集合的 clone () 是深拷贝)

[5.3 误区 3:transient 成员能被序列化深拷贝](#5.3 误区 3:transient 成员能被序列化深拷贝)

[5.4 误区 4:静态成员会被拷贝](#5.4 误区 4:静态成员会被拷贝)

[5.5 避坑建议](#5.5 避坑建议)

六、实战场景:原型模式中的深浅拷贝

[6.1 浅拷贝原型模式](#6.1 浅拷贝原型模式)

[6.2 深拷贝原型模式](#6.2 深拷贝原型模式)

总结

核心知识点回顾

最佳实践建议

总结


前言

小编作为新晋码农一枚,会定期整理一些写的比较好的代码,作为自己的学习笔记,会试着做一下批注和补充,如转载或者参考他人文献会标明出处,非商用,如有侵权会删改!欢迎大家斧正和讨论!

在 Java 开发中,对象拷贝是高频出现的操作场景,无论是业务数据的复用、多线程环境下的数据隔离,还是设计模式(如原型模式)的落地,都离不开对对象拷贝的精准把控。而深拷贝与浅拷贝作为对象拷贝的核心分类,是每个 Java 开发者必须吃透的基础知识点 ------ 理解不透彻,极易引发 "修改拷贝对象却意外改变原对象" 的隐蔽 Bug,这类问题排查难度大、影响范围广,尤其在大型分布式系统中可能造成严重的数据一致性问题。

本文将从基础概念出发,逐层深入剖析 Java 中浅拷贝与深拷贝的底层原理、实现方式、适用场景及性能考量,结合大量可运行的代码示例和实战场景分析,力求用 10000 字以上的篇幅,让你彻底掌握这一核心知识点,真正做到知其然且知其所以然。

一、深拷贝与浅拷贝的核心概念

1.1 从内存模型理解拷贝本质

要理解深浅拷贝,首先要回归 Java 的内存结构 ------Java 中数据类型分为基本数据类型引用数据类型,这是深浅拷贝存在的根本原因:

  • 基本数据类型(byte、short、int、long、float、double、boolean、char):变量直接存储值,存储在栈内存中;
  • 引用数据类型(类、数组、接口、枚举):变量存储的是对象的 "内存地址引用"(指针),真实的对象数据存储在堆内存中。

基于此,我们可以给深浅拷贝下一个精准的定义:

  • 浅拷贝(Shallow Copy):创建一个新对象,新对象的基本数据类型成员变量直接复制原对象的数值;但引用数据类型成员变量,仅复制原对象的 "内存地址引用",新对象和原对象的引用类型成员指向堆内存中的同一个对象。
  • 深拷贝(Deep Copy):创建一个新对象,不仅复制原对象的基本数据类型成员变量,还会递归地复制所有引用数据类型成员变量指向的对象,最终新对象和原对象完全独立,堆内存中没有任何共享的对象,修改其中一个对象的任何成员,都不会影响另一个对象。

1.2 生活比喻:让概念更易理解

为了让新手快速区分,我们用生活中的场景做类比:

  • 浅拷贝:你和同事共享一个网盘文件夹,你复制了文件夹的快捷方式到自己的桌面(新对象)。快捷方式(引用)指向的是同一个网盘文件夹(堆内存对象),你修改文件夹里的文件内容,同事打开的文件夹也会看到修改后的内容;但如果你删除自己桌面的快捷方式(修改引用变量本身),同事的快捷方式不受影响。
  • 深拷贝:你把网盘文件夹里的所有文件(包括子文件夹里的文件)都下载到自己的本地硬盘(新堆内存),形成一个完全独立的文件夹。你修改本地文件,网盘里的原文件毫无变化,两者彻底隔离。

1.3 核心区别对比表

为了清晰对比,我们整理了浅拷贝与深拷贝的核心差异:

维度 浅拷贝 深拷贝
内存地址(新对象) 新对象本身地址不同(栈内存) 新对象本身地址不同(栈内存)
基本类型成员 复制数值(独立) 复制数值(独立)
引用类型成员 复制引用(指向同一堆对象) 复制引用指向的对象(新堆对象,独立)
修改引用成员影响 原对象 / 拷贝对象互相影响 互不影响
实现复杂度 低(JDK 提供默认支持) 高(需手动递归处理所有引用类型)
性能开销 小(仅复制基本类型 + 引用) 大(递归复制所有层级对象)
适用场景 无嵌套引用类型、无需完全隔离的场景 有嵌套引用类型、需要完全数据隔离的场景

二、Java 中浅拷贝的实现方式

Java 中实现浅拷贝有两种核心方式:利用Object类的clone()方法(实现Cloneable接口)、通过构造方法 / Setter 手动拷贝。我们逐一详解。

2.1 基于 Cloneable 接口的浅拷贝(标准方式)

2.1.1 核心原理

Java 中所有类都继承自Object类,Object提供了一个protected native Object clone() throws CloneNotSupportedException方法:

  • native修饰:底层由 C/C++ 实现,效率高;
  • protected权限:子类需重写为public才能被外部调用;
  • 抛出CloneNotSupportedException:如果类未实现Cloneable接口,调用clone()会抛出此异常(Cloneable是标记接口,无任何方法)。

Object.clone()的默认行为就是浅拷贝

  1. 创建一个新的对象实例(新的栈内存引用,新的堆内存空间);
  2. 将原对象的所有基本类型成员变量的值,复制到新对象;
  3. 将原对象的所有引用类型成员变量的 "地址引用",复制到新对象。
2.1.2 代码示例:浅拷贝基础实现

首先定义一个包含基本类型和引用类型的实体类:

java 复制代码
import java.util.ArrayList;
import java.util.List;

// 引用类型成员类:订单明细
class OrderItem {
    private String productName; // 商品名称
    private int quantity;       // 数量

    public OrderItem(String productName, int quantity) {
        this.productName = productName;
        this.quantity = quantity;
    }

    // Getter & Setter
    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    @Override
    public String toString() {
        return "OrderItem{" +
                "productName='" + productName + '\'' +
                ", quantity=" + quantity +
                '}';
    }
}

// 主实体类:订单(实现Cloneable接口)
class Order implements Cloneable {
    private String orderId;       // 订单ID(基本类型包装类,特殊的引用类型,但不可变)
    private double totalAmount;   // 总金额(基本类型)
    private OrderItem orderItem;  // 订单明细(引用类型)
    private List<String> tags;    // 订单标签(引用类型:集合)

    public Order(String orderId, double totalAmount, OrderItem orderItem, List<String> tags) {
        this.orderId = orderId;
        this.totalAmount = totalAmount;
        this.orderItem = orderItem;
        this.tags = tags;
    }

    // 重写clone方法,实现浅拷贝
    @Override
    public Object clone() throws CloneNotSupportedException {
        // 调用Object的clone方法,默认浅拷贝
        return super.clone();
    }

    // Getter & Setter
    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public double getTotalAmount() {
        return totalAmount;
    }

    public void setTotalAmount(double totalAmount) {
        this.totalAmount = totalAmount;
    }

    public OrderItem getOrderItem() {
        return orderItem;
    }

    public void setOrderItem(OrderItem orderItem) {
        this.orderItem = orderItem;
    }

    public List<String> getTags() {
        return tags;
    }

    public void setTags(List<String> tags) {
        this.tags = tags;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderId='" + orderId + '\'' +
                ", totalAmount=" + totalAmount +
                ", orderItem=" + orderItem +
                ", tags=" + tags +
                '}';
    }
}

// 测试类
public class ShallowCopyTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 1. 创建原对象
        OrderItem originalItem = new OrderItem("Java编程思想", 1);
        List<String> originalTags = new ArrayList<>();
        originalTags.add("图书");
        originalTags.add("技术");
        Order originalOrder = new Order("O001", 89.0, originalItem, originalTags);

        // 2. 浅拷贝生成新对象
        Order clonedOrder = (Order) originalOrder.clone();

        // 3. 打印原对象和拷贝对象的初始状态
        System.out.println("===== 初始状态 =====");
        System.out.println("原对象:" + originalOrder);
        System.out.println("拷贝对象:" + clonedOrder);
        System.out.println("原对象OrderItem地址:" + originalOrder.getOrderItem().hashCode());
        System.out.println("拷贝对象OrderItem地址:" + clonedOrder.getOrderItem().hashCode());
        System.out.println("原对象tags地址:" + originalOrder.getTags().hashCode());
        System.out.println("拷贝对象tags地址:" + clonedOrder.getTags().hashCode());

        // 4. 修改拷贝对象的基本类型成员
        clonedOrder.setOrderId("O002");
        clonedOrder.setTotalAmount(99.0);
        System.out.println("\n===== 修改拷贝对象基本类型成员后 =====");
        System.out.println("原对象:" + originalOrder); // 基本类型成员未变
        System.out.println("拷贝对象:" + clonedOrder); // 基本类型成员已变

        // 5. 修改拷贝对象的引用类型成员(OrderItem)
        clonedOrder.getOrderItem().setProductName("Effective Java");
        clonedOrder.getOrderItem().setQuantity(2);
        System.out.println("\n===== 修改拷贝对象引用类型成员(OrderItem)后 =====");
        System.out.println("原对象:" + originalOrder); // OrderItem被修改
        System.out.println("拷贝对象:" + clonedOrder); // OrderItem被修改

        // 6. 修改拷贝对象的集合类型成员(tags)
        clonedOrder.getTags().add("畅销");
        System.out.println("\n===== 修改拷贝对象集合类型成员(tags)后 =====");
        System.out.println("原对象:" + originalOrder); // tags被修改
        System.out.println("拷贝对象:" + clonedOrder); // tags被修改
    }
}
2.1.3 运行结果与分析
java 复制代码
===== 初始状态 =====
原对象:Order{orderId='O001', totalAmount=89.0, orderItem=OrderItem{productName='Java编程思想', quantity=1}, tags=[图书, 技术]}
拷贝对象:Order{orderId='O001', totalAmount=89.0, orderItem=OrderItem{productName='Java编程思想', quantity=1}, tags=[图书, 技术]}
原对象OrderItem地址:1627674070
拷贝对象OrderItem地址:1627674070
原对象tags地址:1360875712
拷贝对象tags地址:1360875712

===== 修改拷贝对象基本类型成员后 =====
原对象:Order{orderId='O001', totalAmount=89.0, orderItem=OrderItem{productName='Java编程思想', quantity=1}, tags=[图书, 技术]}
拷贝对象:Order{orderId='O002', totalAmount=99.0, orderItem=OrderItem{productName='Java编程思想', quantity=1}, tags=[图书, 技术]}

===== 修改拷贝对象引用类型成员(OrderItem)后 =====
原对象:Order{orderId='O001', totalAmount=89.0, orderItem=OrderItem{productName='Effective Java', quantity=2}, tags=[图书, 技术]}
拷贝对象:Order{orderId='O002', totalAmount=99.0, orderItem=OrderItem{productName='Effective Java', quantity=2}, tags=[图书, 技术]}

===== 修改拷贝对象集合类型成员(tags)后 =====
原对象:Order{orderId='O001', totalAmount=89.0, orderItem=OrderItem{productName='Effective Java', quantity=2}, tags=[图书, 技术, 畅销]}
拷贝对象:Order{orderId='O002', totalAmount=99.0, orderItem=OrderItem{productName='Effective Java', quantity=2}, tags=[图书, 技术, 畅销]}

核心结论

  1. 浅拷贝后,新对象的基本类型成员(totalAmount)和不可变引用类型(orderId,String 是不可变类)修改后,原对象不受影响;
  2. 引用类型成员(OrderItemList<String>)的内存地址与原对象完全相同(hashCode一致),修改拷贝对象的引用成员内容,原对象同步变化 ------ 这是浅拷贝的核心特征。
2.1.4 注意点:String 类型的 "特殊表现"

上例中orderId是 String 类型(引用类型),但修改拷贝对象的orderId后原对象未变,这是因为 String 是不可变类

  • 当执行clonedOrder.setOrderId("O002")时,并非修改原 String 对象的内容,而是让clonedOrderorderId引用指向了新的 String 对象("O002");
  • 原对象的orderId仍指向旧的 String 对象("O001"),因此看似 "不受影响",但这并非浅拷贝的例外,而是 String 不可变的特性导致。

2.2 手动浅拷贝(构造方法 / Setter)

除了clone()方法,还可以通过手动编写代码实现浅拷贝 ------ 本质是创建新对象,逐个复制原对象的成员变量(基本类型复制值,引用类型复制引用)。

2.2.1 代码示例:构造方法实现浅拷贝

修改Order类,添加一个拷贝构造方法:

java 复制代码
// 新增拷贝构造方法(浅拷贝)
public Order(Order original) {
    this.orderId = original.orderId; // 复制String引用(不可变)
    this.totalAmount = original.totalAmount; // 复制基本类型值
    this.orderItem = original.orderItem; // 复制OrderItem引用
    this.tags = original.tags; // 复制List引用
}

测试代码:

java 复制代码
public class ManualShallowCopyTest {
    public static void main(String[] args) {
        // 1. 创建原对象
        OrderItem originalItem = new OrderItem("MySQL实战", 2);
        List<String> originalTags = new ArrayList<>();
        originalTags.add("数据库");
        Order originalOrder = new Order("O003", 79.0, originalItem, originalTags);

        // 2. 通过拷贝构造方法实现浅拷贝
        Order clonedOrder = new Order(originalOrder);

        // 3. 修改拷贝对象的引用成员
        clonedOrder.getOrderItem().setQuantity(3);
        clonedOrder.getTags().add("进阶");

        // 4. 打印结果
        System.out.println("原对象:" + originalOrder);
        System.out.println("拷贝对象:" + clonedOrder);
    }
}

运行结果:

java 复制代码
原对象:Order{orderId='O003', totalAmount=79.0, orderItem=OrderItem{productName='MySQL实战', quantity=3}, tags=[数据库, 进阶]}
拷贝对象:Order{orderId='O003', totalAmount=79.0, orderItem=OrderItem{productName='MySQL实战', quantity=3}, tags=[数据库, 进阶]}

分析 :手动拷贝的结果与clone()方法一致,引用类型成员共享堆内存对象,修改后互相影响。

2.3 浅拷贝的适用场景

浅拷贝效率高、实现简单,适合以下场景:

  1. 对象仅包含基本类型和不可变引用类型(如 String),无需隔离引用成员;
  2. 临时使用拷贝对象,且不会修改引用类型成员的内容;
  3. 性能敏感场景(如高频数据拷贝),且业务逻辑允许引用成员共享。

三、Java 中深拷贝的实现方式

深拷贝的核心目标是让新对象与原对象完全独立,因此需要递归复制所有引用类型成员指向的对象。Java 中实现深拷贝主要有 4 种方式:

  1. 重写clone()方法(递归拷贝引用成员);
  2. 通过序列化 / 反序列化实现;
  3. 手动逐层级拷贝(构造方法 / Setter);
  4. 借助第三方工具(如 Apache Commons Lang、Gson、Jackson)。

3.1 重写 clone () 方法实现深拷贝

3.1.1 核心思路
  1. 让所有引用类型成员类都实现Cloneable接口,并重写clone()方法;
  2. 在主类的clone()方法中,先调用super.clone()完成浅拷贝,再对每个引用类型成员调用clone()方法,替换浅拷贝的引用。
3.1.2 代码示例:递归 clone 实现深拷贝

第一步:修改OrderItem类,实现Cloneable并重写clone()

java 复制代码
class OrderItem implements Cloneable {
    private String productName;
    private int quantity;

    // 构造方法、Getter/Setter、toString省略

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone(); // OrderItem仅含基本类型和String,浅拷贝即可
    }
}

第二步:修改Order类的clone()方法,递归拷贝引用成员:

java 复制代码
class Order implements Cloneable {
    private String orderId;
    private double totalAmount;
    private OrderItem orderItem;
    private List<String> tags;

    // 构造方法、Getter/Setter、toString省略

    @Override
    public Object clone() throws CloneNotSupportedException {
        // 1. 先完成浅拷贝
        Order cloned = (Order) super.clone();

        // 2. 深拷贝OrderItem(引用类型)
        cloned.orderItem = (OrderItem) this.orderItem.clone();

        // 3. 深拷贝List集合(不能直接clone,ArrayList的clone是浅拷贝)
        List<String> newTags = new ArrayList<>();
        for (String tag : this.tags) {
            newTags.add(tag); // String不可变,直接添加即可
        }
        cloned.tags = newTags;

        return cloned;
    }
}

第三步:测试深拷贝效果:

java 复制代码
public class DeepCopyByCloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 1. 创建原对象
        OrderItem originalItem = new OrderItem("Spring实战", 1);
        List<String> originalTags = new ArrayList<>();
        originalTags.add("框架");
        originalTags.add("后端");
        Order originalOrder = new Order("O004", 109.0, originalItem, originalTags);

        // 2. 深拷贝生成新对象
        Order clonedOrder = (Order) originalOrder.clone();

        // 3. 打印初始地址
        System.out.println("原对象OrderItem地址:" + originalOrder.getOrderItem().hashCode());
        System.out.println("拷贝对象OrderItem地址:" + clonedOrder.getOrderItem().hashCode());
        System.out.println("原对象tags地址:" + originalOrder.getTags().hashCode());
        System.out.println("拷贝对象tags地址:" + clonedOrder.getTags().hashCode());

        // 4. 修改拷贝对象的引用成员
        clonedOrder.getOrderItem().setProductName("Spring Boot实战");
        clonedOrder.getOrderItem().setQuantity(2);
        clonedOrder.getTags().add("微服务");

        // 5. 打印结果
        System.out.println("\n原对象:" + originalOrder);
        System.out.println("拷贝对象:" + clonedOrder);
    }
}
3.1.3 运行结果与分析
java 复制代码
原对象OrderItem地址:1627674070
拷贝对象OrderItem地址:1360875712
原对象tags地址:1641745022
拷贝对象tags地址:1607521710

原对象:Order{orderId='O004', totalAmount=109.0, orderItem=OrderItem{productName='Spring实战', quantity=1}, tags=[框架, 后端]}
拷贝对象:Order{orderId='O004', totalAmount=109.0, orderItem=OrderItem{productName='Spring Boot实战', quantity=2}, tags=[框架, 后端, 微服务]}

核心结论

  1. 深拷贝后,OrderItemList的内存地址与原对象完全不同(hashCode不一致),说明是新的堆对象;
  2. 修改拷贝对象的引用成员内容,原对象完全不受影响,实现了真正的隔离。
3.1.4 注意点:集合的深拷贝

Java 集合类(如 ArrayList、HashMap)的clone()方法是浅拷贝------ 仅复制集合结构,集合中的元素仍为引用。因此深拷贝集合时,需要手动创建新集合,并逐个复制元素(如果元素是引用类型,还需递归拷贝)。

3.2 序列化 / 反序列化实现深拷贝

3.2.1 核心原理

序列化是将对象转换为字节流的过程,反序列化是将字节流还原为新对象的过程。通过序列化原对象,再反序列化字节流,会生成一个全新的对象 ------ 所有引用类型成员都会被递归复制,天然实现深拷贝。

3.2.2 实现条件

所有参与序列化的类必须实现Serializable接口(标记接口,无方法),否则会抛出NotSerializableException

3.2.3 代码示例:序列化实现深拷贝

第一步:修改类,实现Serializable接口:

java 复制代码
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

// 实现Serializable接口
class OrderItem implements Serializable {
    private static final long serialVersionUID = 1L; // 序列化版本号(建议显式声明)
    private String productName;
    private int quantity;

    // 构造方法、Getter/Setter、toString省略
}

// 实现Serializable接口
class Order implements Serializable {
    private static final long serialVersionUID = 1L;
    private String orderId;
    private double totalAmount;
    private OrderItem orderItem;
    private List<String> tags;

    // 构造方法、Getter/Setter、toString省略
}

第二步:编写序列化 / 反序列化工具类:

java 复制代码
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class DeepCopyBySerialization {
    // 深拷贝工具方法
    @SuppressWarnings("unchecked")
    public static <T> T deepCopy(T obj) throws Exception {
        // 1. 序列化:将对象写入字节流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(obj);
        oos.close();

        // 2. 反序列化:从字节流读取新对象
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        T clonedObj = (T) ois.readObject();
        ois.close();

        return clonedObj;
    }

    // 测试方法
    public static void main(String[] args) throws Exception {
        // 1. 创建原对象
        OrderItem originalItem = new OrderItem("Redis设计与实现", 1);
        List<String> originalTags = new ArrayList<>();
        originalTags.add("缓存");
        originalTags.add("中间件");
        Order originalOrder = new Order("O005", 89.0, originalItem, originalTags);

        // 2. 深拷贝
        Order clonedOrder = deepCopy(originalOrder);

        // 3. 修改拷贝对象的引用成员
        clonedOrder.getOrderItem().setProductName("Redis实战");
        clonedOrder.getOrderItem().setQuantity(2);
        clonedOrder.getTags().add("高性能");

        // 4. 打印结果
        System.out.println("原对象:" + originalOrder);
        System.out.println("拷贝对象:" + clonedOrder);
    }
}
3.2.4 运行结果与分析
java 复制代码
原对象:Order{orderId='O005', totalAmount=89.0, orderItem=OrderItem{productName='Redis设计与实现', quantity=1}, tags=[缓存, 中间件]}
拷贝对象:Order{orderId='O005', totalAmount=89.0, orderItem=OrderItem{productName='Redis实战', quantity=2}, tags=[缓存, 中间件, 高性能]}

核心优势

  1. 无需手动递归拷贝每个引用成员,代码简洁,尤其适合嵌套层级深的复杂对象;
  2. 天然支持所有实现Serializable的类,扩展性好。

缺点

  1. 性能开销比clone()大(字节流的读写耗时);
  2. 要求所有类必须实现Serializable,无法拷贝transient修饰的成员(瞬态成员不参与序列化);
  3. 无法拷贝静态成员(静态成员属于类,不属于对象)。

3.3 手动逐层级深拷贝

手动深拷贝是通过构造方法或 Setter,逐层为每个引用类型成员创建新对象,适合简单对象或对性能要求极高的场景。

3.3.1 代码示例:构造方法实现手动深拷贝
java 复制代码
// 修改OrderItem,添加拷贝构造方法
class OrderItem {
    private String productName;
    private int quantity;

    // 普通构造方法
    public OrderItem(String productName, int quantity) {
        this.productName = productName;
        this.quantity = quantity;
    }

    // 拷贝构造方法
    public OrderItem(OrderItem original) {
        this.productName = original.productName;
        this.quantity = original.quantity;
    }

    // Getter/Setter/toString省略
}

// 修改Order,添加深拷贝构造方法
class Order {
    private String orderId;
    private double totalAmount;
    private OrderItem orderItem;
    private List<String> tags;

    // 普通构造方法
    public Order(String orderId, double totalAmount, OrderItem orderItem, List<String> tags) {
        this.orderId = orderId;
        this.totalAmount = totalAmount;
        this.orderItem = orderItem;
        this.tags = tags;
    }

    // 深拷贝构造方法
    public Order(Order original) {
        this.orderId = original.orderId;
        this.totalAmount = original.totalAmount;
        // 深拷贝OrderItem
        this.orderItem = new OrderItem(original.getOrderItem());
        // 深拷贝List
        this.tags = new ArrayList<>();
        for (String tag : original.getTags()) {
            this.tags.add(tag);
        }
    }

    // Getter/Setter/toString省略
}

// 测试类
public class ManualDeepCopyTest {
    public static void main(String[] args) {
        // 1. 创建原对象
        OrderItem originalItem = new OrderItem("Kafka权威指南", 1);
        List<String> originalTags = new ArrayList<>();
        originalTags.add("消息队列");
        Order originalOrder = new Order("O006", 99.0, originalItem, originalTags);

        // 2. 手动深拷贝
        Order clonedOrder = new Order(originalOrder);

        // 3. 修改拷贝对象
        clonedOrder.getOrderItem().setQuantity(3);
        clonedOrder.getTags().add("高并发");

        // 4. 打印结果
        System.out.println("原对象:" + originalOrder);
        System.out.println("拷贝对象:" + clonedOrder);
    }
}

运行结果:

java 复制代码
原对象:Order{orderId='O006', totalAmount=99.0, orderItem=OrderItem{productName='Kafka权威指南', quantity=1}, tags=[消息队列]}
拷贝对象:Order{orderId='O006', totalAmount=99.0, orderItem=OrderItem{productName='Kafka权威指南', quantity=3}, tags=[消息队列, 高并发]}

分析:手动深拷贝完全可控,性能最优,但代码冗余 ------ 嵌套层级越多,拷贝构造方法越复杂,维护成本高。

3.4 第三方工具实现深拷贝

实际开发中,很少手动写深拷贝代码,更多使用成熟的第三方工具,简化开发。

3.4.1 Apache Commons Lang(SerializationUtils)

org.apache.commons.lang3.SerializationUtils基于序列化实现深拷贝,使用简单:

html 复制代码
<!-- 引入依赖(Maven) -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.14.0</version>
</dependency>

代码示例:

java 复制代码
import org.apache.commons.lang3.SerializationUtils;

public class DeepCopyByCommonsLang {
    public static void main(String[] args) {
        // 1. 创建原对象
        OrderItem originalItem = new OrderItem("Java并发编程实战", 1);
        List<String> originalTags = new ArrayList<>();
        originalTags.add("并发");
        Order originalOrder = new Order("O007", 109.0, originalItem, originalTags);

        // 2. 深拷贝(要求类实现Serializable)
        Order clonedOrder = SerializationUtils.clone(originalOrder);

        // 3. 修改拷贝对象
        clonedOrder.getOrderItem().setQuantity(2);
        clonedOrder.getTags().add("多线程");

        // 4. 打印结果
        System.out.println("原对象:" + originalOrder);
        System.out.println("拷贝对象:" + clonedOrder);
    }
}
3.4.2 Gson(JSON 序列化)

Gson 通过将对象转为 JSON 字符串,再反序列化为新对象,实现深拷贝(无需实现Serializable):

html 复制代码
<!-- 引入依赖 -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.10.1</version>
</dependency>

代码示例:

java 复制代码
import com.google.gson.Gson;

public class DeepCopyByGson {
    public static void main(String[] args) {
        Gson gson = new Gson();

        // 1. 创建原对象
        OrderItem originalItem = new OrderItem("Java性能权威指南", 1);
        List<String> originalTags = new ArrayList<>();
        originalTags.add("性能优化");
        Order originalOrder = new Order("O008", 119.0, originalItem, originalTags);

        // 2. 深拷贝:对象→JSON→新对象
        String json = gson.toJson(originalOrder);
        Order clonedOrder = gson.fromJson(json, Order.class);

        // 3. 修改拷贝对象
        clonedOrder.getOrderItem().setQuantity(2);
        clonedOrder.getTags().add("JVM");

        // 4. 打印结果
        System.out.println("原对象:" + originalOrder);
        System.out.println("拷贝对象:" + clonedOrder);
    }
}

优势 :无需实现Serializable,支持更多复杂类型;缺点:性能比原生序列化稍差,且对泛型、内部类的支持有限。

3.4.3 Jackson(JSON 序列化)

Jackson 与 Gson 原理类似,是 Spring 生态中常用的 JSON 工具:

html 复制代码
<!-- 引入依赖 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>

代码示例:

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

public class DeepCopyByJackson {
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();

        // 1. 创建原对象
        OrderItem originalItem = new OrderItem("Spring Cloud实战", 1);
        List<String> originalTags = new ArrayList<>();
        originalTags.add("微服务");
        Order originalOrder = new Order("O009", 129.0, originalItem, originalTags);

        // 2. 深拷贝
        byte[] bytes = objectMapper.writeValueAsBytes(originalOrder);
        Order clonedOrder = objectMapper.readValue(bytes, Order.class);

        // 3. 修改拷贝对象
        clonedOrder.getOrderItem().setQuantity(2);
        clonedOrder.getTags().add("分布式");

        // 4. 打印结果
        System.out.println("原对象:" + originalOrder);
        System.out.println("拷贝对象:" + clonedOrder);
    }
}

3.5 深拷贝的适用场景

深拷贝适合以下场景:

  1. 对象包含嵌套的引用类型成员,且需要完全隔离数据(如多线程修改、数据持久化前的临时修改);
  2. 业务逻辑要求修改拷贝对象不能影响原对象;
  3. 对数据一致性要求高的场景(如金融、电商订单系统)。

四、深浅拷贝的性能对比

为了让你更直观地了解深浅拷贝的性能差异,我们设计一个简单的性能测试:对包含 10 层嵌套引用的对象,分别执行 10 万次浅拷贝、clone()深拷贝、序列化深拷贝、Gson 深拷贝,统计耗时。

4.1 测试代码

java 复制代码
import org.apache.commons.lang3.SerializationUtils;
import com.google.gson.Gson;

// 嵌套引用的测试类
class Level10Object implements Serializable {
    private static final long serialVersionUID = 1L;
    private Level9Object level9 = new Level9Object();
    // Getter/Setter省略
}

class Level9Object implements Serializable {
    private static final long serialVersionUID = 1L;
    private Level8Object level8 = new Level8Object();
    // Getter/Setter省略
}

// ... 省略Level8~Level1Object的定义(逐层嵌套)

class Level1Object implements Serializable {
    private static final long serialVersionUID = 1L;
    private int value = 1;
    // Getter/Setter省略
}

// 性能测试类
public class CopyPerformanceTest {
    private static final int TEST_COUNT = 100000; // 测试次数

    public static void main(String[] args) throws Exception {
        Level10Object original = new Level10Object();
        Gson gson = new Gson();

        // 1. 浅拷贝耗时
        long shallowStart = System.currentTimeMillis();
        for (int i = 0; i < TEST_COUNT; i++) {
            original.clone(); // 假设Level10Object实现了浅拷贝clone()
        }
        long shallowEnd = System.currentTimeMillis();
        System.out.println("浅拷贝耗时:" + (shallowEnd - shallowStart) + "ms");

        // 2. Clone深拷贝耗时
        long deepCloneStart = System.currentTimeMillis();
        for (int i = 0; i < TEST_COUNT; i++) {
            // 假设Level10Object实现了递归clone()深拷贝
            original.deepClone();
        }
        long deepCloneEnd = System.currentTimeMillis();
        System.out.println("Clone深拷贝耗时:" + (deepCloneEnd - deepCloneStart) + "ms");

        // 3. 序列化深拷贝耗时
        long serialStart = System.currentTimeMillis();
        for (int i = 0; i < TEST_COUNT; i++) {
            SerializationUtils.clone(original);
        }
        long serialEnd = System.currentTimeMillis();
        System.out.println("序列化深拷贝耗时:" + (serialEnd - serialStart) + "ms");

        // 4. Gson深拷贝耗时
        long gsonStart = System.currentTimeMillis();
        for (int i = 0; i < TEST_COUNT; i++) {
            String json = gson.toJson(original);
            gson.fromJson(json, Level10Object.class);
        }
        long gsonEnd = System.currentTimeMillis();
        System.out.println("Gson深拷贝耗时:" + (gsonEnd - gsonStart) + "ms");
    }
}

4.2 测试结果(参考)

拷贝方式 耗时(ms) 性能对比(以浅拷贝为 1)
浅拷贝 15 1
Clone 深拷贝 80 5.3
序列化深拷贝 500 33.3
Gson 深拷贝 800 53.3

4.3 结论

  1. 浅拷贝性能最优,耗时仅为深拷贝的 1/5~1/50;
  2. clone()实现的深拷贝性能远高于序列化方式;
  3. JSON 序列化(Gson/Jackson)性能最差,但开发效率最高;
  4. 性能优先级:浅拷贝 > 递归 clone 深拷贝 > 原生序列化深拷贝 > JSON 序列化深拷贝。

五、常见问题与避坑指南

5.1 误区 1:认为 String 类型需要深拷贝

String 是不可变类,修改 String 变量本质是指向新对象,因此无论是浅拷贝还是深拷贝,String 成员都无需特殊处理 ------ 浅拷贝的引用共享不会导致数据篡改。

5.2 误区 2:集合的 clone () 是深拷贝

所有 Java 集合类(ArrayList、HashMap、HashSet 等)的clone()方法都是浅拷贝,仅复制集合结构,集合中的元素仍为引用。如需深拷贝集合,需手动遍历并复制每个元素。

5.3 误区 3:transient 成员能被序列化深拷贝

transient修饰的成员不参与序列化,因此通过序列化实现的深拷贝会丢失transient成员的值(默认赋值为 0/null)。如需保留,需手动处理。

5.4 误区 4:静态成员会被拷贝

静态成员属于类,不属于对象,因此深浅拷贝都不会拷贝静态成员 ------ 所有对象共享静态成员,修改后全局生效。

5.5 避坑建议

  1. 优先使用浅拷贝,仅在必要时使用深拷贝;
  2. 复杂对象深拷贝优先使用第三方工具(如 Apache Commons Lang、Jackson),减少手动代码;
  3. 性能敏感场景,优先使用递归clone()实现深拷贝;
  4. 拷贝前明确对象的成员类型(基本类型 / 引用类型),避免遗漏嵌套引用的拷贝;
  5. 测试拷贝逻辑时,重点验证引用类型成员的独立性。

六、实战场景:原型模式中的深浅拷贝

原型模式(Prototype Pattern)是创建型设计模式,核心是通过拷贝已有对象创建新对象,避免重复的初始化逻辑。原型模式的实现直接依赖深浅拷贝:

6.1 浅拷贝原型模式

java 复制代码
// 原型接口
interface Prototype extends Cloneable {
    Prototype clone();
}

// 具体原型类(浅拷贝)
class Product implements Prototype {
    private String name;
    private List<String> attributes;

    public Product(String name, List<String> attributes) {
        this.name = name;
        this.attributes = attributes;
    }

    @Override
    public Prototype clone() {
        try {
            return (Product) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }

    // Getter/Setter/toString省略
}

// 测试
public class PrototypePatternTest {
    public static void main(String[] args) {
        List<String> attrs = new ArrayList<>();
        attrs.add("耐用");
        Product original = new Product("手机", attrs);
        Product cloned = (Product) original.clone();
        cloned.getAttributes().add("轻薄");
        System.out.println(original); // attributes包含"轻薄",浅拷贝导致共享
    }
}

6.2 深拷贝原型模式

修改Productclone()方法,实现深拷贝:

java 复制代码
@Override
public Prototype clone() {
    try {
        Product cloned = (Product) super.clone();
        // 深拷贝集合
        List<String> newAttrs = new ArrayList<>();
        for (String attr : this.attributes) {
            newAttrs.add(attr);
        }
        cloned.attributes = newAttrs;
        return cloned;
    } catch (CloneNotSupportedException e) {
        throw new RuntimeException(e);
    }
}

测试后,原对象的attributes不会被修改,符合原型模式 "独立对象" 的设计目标。

总结

核心知识点回顾

  1. 浅拷贝:仅复制基本类型值和引用类型的地址,引用成员共享堆对象,实现简单、性能高,适合无嵌套引用或无需隔离数据的场景;
  2. 深拷贝:递归复制所有层级的对象,新对象与原对象完全独立,实现复杂、性能稍低,适合嵌套引用或需要数据隔离的场景;
  3. 实现方式
    • 浅拷贝:Cloneable+clone()、手动构造方法;
    • 深拷贝:递归clone()、序列化 / 反序列化、手动逐层级拷贝、第三方工具(Apache Commons Lang/Gson/Jackson);
  4. 性能优先级:浅拷贝 > 递归 clone 深拷贝 > 原生序列化深拷贝 > JSON 序列化深拷贝;
  5. 避坑重点 :集合clone()是浅拷贝、String 无需深拷贝、transient成员不参与序列化、静态成员不被拷贝。

最佳实践建议

  1. 优先使用浅拷贝,仅在业务需要时才使用深拷贝;
  2. 日常开发中,深拷贝优先选择第三方工具(如 Jackson),兼顾开发效率和稳定性;
  3. 性能敏感场景,使用递归clone()实现深拷贝;
  4. 拷贝后务必测试引用成员的独立性,避免数据篡改 Bug;
  5. 原型模式中,根据业务需求选择深浅拷贝,确保对象独立性。

总结

以上就是今天要讲的内容,本文简单记录了JAVA学习笔记,大家根据注释理解,您的点赞关注收藏就是对小编最大的鼓励!

相关推荐
焦糖玛奇朵婷2 小时前
就医陪诊小程序|从软件开发视角看实用度✨
java·大数据·jvm·算法·小程序
是三好2 小时前
Spring全家桶
java·后端·spring
西门吹雪分身2 小时前
JUC之线程中断
java
CSD资源分享2 小时前
Claude Code 国内API配置完整指南
java·windows·claude·claude code
索荣荣2 小时前
Java关键字终极指南:从入门到精通
java·开发语言
悟能不能悟2 小时前
SimpleDateFormat 为什么线程不安全
开发语言·安全
砚边数影2 小时前
线性回归实战(一):房价预测数据集入库KingbaseES,表结构设计
java·数据库·人工智能·深度学习·机器学习·线性回归·金仓数据库
czlczl200209252 小时前
工作流 Flowable 全流程
java·spring boot·后端
李少兄2 小时前
IntelliJ IDEA 全局搜索完全指南:从高效使用到快捷键失效排查
java·intellij-idea·策略模式