本系列可作为JAVA学习系列的笔记,文中提到的一些练习的代码,小编会将代码复制下来,大家复制下来就可以练习了,方便大家学习。
点赞关注不迷路!您的点赞、关注和收藏是对小编最大的支持和鼓励!
本文篇幅较长,建议先收藏再食用!
系列文章目录
JAVA学习 DAY2 java程序运行、注意事项、转义字符
JAVA学习 DAY5 变量&数据类型 [万字长文!一篇搞定!]
JAVA学习 DAY7 程序逻辑控制【万字长文!一篇搞定!】
JAVA学习 DAY11 类和对象_续1【万字长文!一篇搞定!】
JAVA学习 DAY12 继承和多态【万字长文!一篇搞定!】
JAVA学习 DAY13 抽象类和接口【万字长文!一篇搞定!】
拓展文章
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()的默认行为就是浅拷贝:
- 创建一个新的对象实例(新的栈内存引用,新的堆内存空间);
- 将原对象的所有基本类型成员变量的值,复制到新对象;
- 将原对象的所有引用类型成员变量的 "地址引用",复制到新对象。
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=[图书, 技术, 畅销]}
核心结论:
- 浅拷贝后,新对象的基本类型成员(
totalAmount)和不可变引用类型(orderId,String 是不可变类)修改后,原对象不受影响; - 引用类型成员(
OrderItem、List<String>)的内存地址与原对象完全相同(hashCode一致),修改拷贝对象的引用成员内容,原对象同步变化 ------ 这是浅拷贝的核心特征。
2.1.4 注意点:String 类型的 "特殊表现"
上例中orderId是 String 类型(引用类型),但修改拷贝对象的orderId后原对象未变,这是因为 String 是不可变类:
- 当执行
clonedOrder.setOrderId("O002")时,并非修改原 String 对象的内容,而是让clonedOrder的orderId引用指向了新的 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 浅拷贝的适用场景
浅拷贝效率高、实现简单,适合以下场景:
- 对象仅包含基本类型和不可变引用类型(如 String),无需隔离引用成员;
- 临时使用拷贝对象,且不会修改引用类型成员的内容;
- 性能敏感场景(如高频数据拷贝),且业务逻辑允许引用成员共享。
三、Java 中深拷贝的实现方式
深拷贝的核心目标是让新对象与原对象完全独立,因此需要递归复制所有引用类型成员指向的对象。Java 中实现深拷贝主要有 4 种方式:
- 重写
clone()方法(递归拷贝引用成员); - 通过序列化 / 反序列化实现;
- 手动逐层级拷贝(构造方法 / Setter);
- 借助第三方工具(如 Apache Commons Lang、Gson、Jackson)。
3.1 重写 clone () 方法实现深拷贝
3.1.1 核心思路
- 让所有引用类型成员类都实现
Cloneable接口,并重写clone()方法; - 在主类的
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=[框架, 后端, 微服务]}
核心结论:
- 深拷贝后,
OrderItem和List的内存地址与原对象完全不同(hashCode不一致),说明是新的堆对象; - 修改拷贝对象的引用成员内容,原对象完全不受影响,实现了真正的隔离。
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=[缓存, 中间件, 高性能]}
核心优势:
- 无需手动递归拷贝每个引用成员,代码简洁,尤其适合嵌套层级深的复杂对象;
- 天然支持所有实现
Serializable的类,扩展性好。
缺点:
- 性能开销比
clone()大(字节流的读写耗时); - 要求所有类必须实现
Serializable,无法拷贝transient修饰的成员(瞬态成员不参与序列化); - 无法拷贝静态成员(静态成员属于类,不属于对象)。
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 深拷贝的适用场景
深拷贝适合以下场景:
- 对象包含嵌套的引用类型成员,且需要完全隔离数据(如多线程修改、数据持久化前的临时修改);
- 业务逻辑要求修改拷贝对象不能影响原对象;
- 对数据一致性要求高的场景(如金融、电商订单系统)。
四、深浅拷贝的性能对比
为了让你更直观地了解深浅拷贝的性能差异,我们设计一个简单的性能测试:对包含 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/5~1/50;
clone()实现的深拷贝性能远高于序列化方式;- JSON 序列化(Gson/Jackson)性能最差,但开发效率最高;
- 性能优先级:浅拷贝 > 递归 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 避坑建议
- 优先使用浅拷贝,仅在必要时使用深拷贝;
- 复杂对象深拷贝优先使用第三方工具(如 Apache Commons Lang、Jackson),减少手动代码;
- 性能敏感场景,优先使用递归
clone()实现深拷贝; - 拷贝前明确对象的成员类型(基本类型 / 引用类型),避免遗漏嵌套引用的拷贝;
- 测试拷贝逻辑时,重点验证引用类型成员的独立性。
六、实战场景:原型模式中的深浅拷贝
原型模式(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 深拷贝原型模式
修改Product的clone()方法,实现深拷贝:
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不会被修改,符合原型模式 "独立对象" 的设计目标。
总结
核心知识点回顾
- 浅拷贝:仅复制基本类型值和引用类型的地址,引用成员共享堆对象,实现简单、性能高,适合无嵌套引用或无需隔离数据的场景;
- 深拷贝:递归复制所有层级的对象,新对象与原对象完全独立,实现复杂、性能稍低,适合嵌套引用或需要数据隔离的场景;
- 实现方式 :
- 浅拷贝:
Cloneable+clone()、手动构造方法; - 深拷贝:递归
clone()、序列化 / 反序列化、手动逐层级拷贝、第三方工具(Apache Commons Lang/Gson/Jackson);
- 浅拷贝:
- 性能优先级:浅拷贝 > 递归 clone 深拷贝 > 原生序列化深拷贝 > JSON 序列化深拷贝;
- 避坑重点 :集合
clone()是浅拷贝、String 无需深拷贝、transient成员不参与序列化、静态成员不被拷贝。
最佳实践建议
- 优先使用浅拷贝,仅在业务需要时才使用深拷贝;
- 日常开发中,深拷贝优先选择第三方工具(如 Jackson),兼顾开发效率和稳定性;
- 性能敏感场景,使用递归
clone()实现深拷贝; - 拷贝后务必测试引用成员的独立性,避免数据篡改 Bug;
- 原型模式中,根据业务需求选择深浅拷贝,确保对象独立性。

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