Java 对象拷贝(浅拷贝 / 深拷贝)

一、对象拷贝的核心背景

1. 为什么需要 "拷贝"?

Java 中直接赋值对象(如 User u2 = u1)属于引用传递,新引用仅指向原对象的内存地址,并非创建新对象:

java

运行

复制代码
// 直接赋值:引用共享,修改一个会影响另一个
User u1 = new User("张三", 20);
User u2 = u1; 
u2.setName("李四");
System.out.println(u1.getName()); // 输出:李四(u1被意外修改)

拷贝的核心目的:创建独立的新对象,新对象与原对象初始值相同,但后续修改互不影响。

2. 拷贝的核心分类

表格

类型 核心特征
浅拷贝 仅拷贝对象表层,引用类型字段共享内存
深拷贝 拷贝对象及所有嵌套子对象,所有字段独立

二、浅拷贝(Shallow Copy):仅拷贝 "表层"

1. 定义

创建新对象后:

  • 基本数据类型字段:直接复制值;
  • 引用数据类型字段:仅复制引用(仍指向原对象的内存地址)。简单说:浅拷贝只拷贝 "对象本身",不拷贝对象里的 "子对象"。

2. 实现方式(核心)

方式:实现 Cloneable 接口 + 重写 clone() 方法

Cloneable 是标记接口(无任何方法),仅表示类支持拷贝;Object 类的 clone() 方法默认实现浅拷贝。

步骤 1:定义嵌套对象结构

java

运行

复制代码
// 地址类(引用类型)
class Address {
    private String city;
    private String street;

    public Address(String city, String street) {
        this.city = city;
        this.street = street;
    }

    // getter/setter 省略
    @Override
    public String toString() {
        return "Address{" + "city='" + city + '\'' + ", street='" + street + '\'' + '}';
    }
}

// 用户类(实现 Cloneable 支持拷贝)
class User implements Cloneable {
    // 基本数据类型
    private String name;
    private int age;
    // 引用数据类型(子对象)
    private Address address;

    public User(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

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

    // getter/setter、toString 省略
}
步骤 2:测试浅拷贝效果

java

运行

复制代码
public class ShallowCopyDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 1. 创建原对象
        Address address = new Address("北京", "朝阳区");
        User user1 = new User("张三", 20, address);

        // 2. 浅拷贝得到新对象
        User user2 = (User) user1.clone();

        // 3. 修改基本类型字段:不影响原对象
        user2.setName("李四");
        user2.setAge(22);
        System.out.println("原对象user1:" + user1); 
        // 输出:User{name='张三', age=20, address=Address{city='北京', street='朝阳区'}}
        System.out.println("拷贝对象user2:" + user2);
        // 输出:User{name='李四', age=22, address=Address{city='北京', street='朝阳区'}}

        // 4. 修改引用类型字段(子对象):影响原对象
        user2.getAddress().setCity("上海");
        System.out.println("修改子对象后user1:" + user1);
        // 输出:User{name='张三', age=20, address=Address{city='上海', street='朝阳区'}}
        System.out.println("修改子对象后user2:" + user2);
        // 输出:User{name='李四', age=22, address=Address{city='上海', street='朝阳区'}}
    }
}

3. 核心特征

表格

字段类型 拷贝方式 是否独立
基本数据类型 复制值
引用数据类型 复制引用(指向原对象)

4. 适用场景

  • 对象仅包含基本数据类型字段;
  • 无需修改引用类型字段,或有意共享子对象(如只读数据)。

三、深拷贝(Deep Copy):拷贝 "所有层级"

1. 定义

创建新对象后,不仅拷贝对象本身,还递归拷贝所有引用类型的子对象。简单说:深拷贝创建 "完全独立" 的新对象,原对象和拷贝对象的所有字段(包括子对象)互不影响。

2. 实现方式(两种核心方式)

方式 1:重写 clone() 方法(递归拷贝子对象)

核心思路 :让所有嵌套的引用类型类都实现 Cloneable,在顶层对象的 clone() 中递归拷贝子对象。

步骤 1:改造嵌套类和顶层类

java

运行

复制代码
// 地址类实现 Cloneable
class Address implements Cloneable {
    private String city;
    private String street;

    public Address(String city, String street) {
        this.city = city;
        this.street = street;
    }

    // 地址类重写 clone 方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    // getter/setter、toString 省略
}

// 用户类改造 clone 方法(递归拷贝子对象)
class User implements Cloneable {
    private String name;
    private int age;
    private Address address;

    public User(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 1. 先拷贝当前对象(浅拷贝)
        User cloneUser = (User) super.clone();
        // 2. 手动拷贝引用类型字段(递归深拷贝)
        cloneUser.address = (Address) this.address.clone();
        return cloneUser;
    }

    // getter/setter、toString 省略
}
步骤 2:测试深拷贝效果

java

运行

复制代码
public class DeepCopyDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("北京", "朝阳区");
        User user1 = new User("张三", 20, address);
        User user2 = (User) user1.clone();

        // 修改子对象:原对象不受影响
        user2.getAddress().setCity("上海");
        System.out.println("原对象user1:" + user1);
        // 输出:User{name='张三', age=20, address=Address{city='北京', street='朝阳区'}}
        System.out.println("拷贝对象user2:" + user2);
        // 输出:User{name='张三', age=20, address=Address{city='上海', street='朝阳区'}}
    }
}
方式 2:序列化 / 反序列化(推荐,适配复杂嵌套)

核心优势:无需手动递归拷贝,适配多层嵌套对象(如 User→Address→House→Room)。

步骤 1:所有嵌套类实现 Serializable 接口

java

运行

复制代码
import java.io.*;

// 地址类实现 Serializable
class Address implements Serializable {
    // 序列化版本号(避免反序列化异常)
    private static final long serialVersionUID = 1L;
    private String city;
    private String street;

    public Address(String city, String street) {
        this.city = city;
        this.street = street;
    }

    // getter/setter 省略
}

// 用户类实现 Serializable
class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    private Address address;

    public User(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    // 通用深拷贝方法
    public User deepCopy() throws IOException, ClassNotFoundException {
        // 1. 序列化:将对象写入字节流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        // 2. 反序列化:从字节流重建对象(全新的对象)
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (User) ois.readObject();
    }

    // getter/setter 省略
}
步骤 2:测试序列化深拷贝

java

运行

复制代码
public class SerializeCopyDemo {
    public static void main(String[] args) throws Exception {
        Address address = new Address("北京", "朝阳区");
        User user1 = new User("张三", 20, address);
        User user2 = user1.deepCopy();

        user2.getAddress().setCity("上海");
        System.out.println(user1.getAddress().getCity()); // 输出:北京(原对象无变化)
    }
}

3. 核心特征

表格

字段类型 拷贝方式 是否独立
基本数据类型 复制值
引用数据类型 递归拷贝子对象(新内存)

4. 适用场景

  • 对象包含多层嵌套的引用类型字段;
  • 需要完全隔离原对象和拷贝对象(如多线程操作、数据备份、核心业务数据修改)。

四、浅拷贝 vs 深拷贝:核心对比

表格

维度 浅拷贝 深拷贝
拷贝范围 仅拷贝对象本身,不拷贝子对象 拷贝对象 + 所有层级的子对象
引用类型字段 共享同一块内存 拥有独立的内存空间
性能 速度快,内存开销小 速度慢,内存开销大(递归 / 序列化)
实现复杂度 简单(默认 clone () 即可) 复杂(递归 clone / 序列化)
数据独立性 基本类型独立,引用类型共享 完全独立

五、避坑指南:常见错误与最佳实践

1. 常见错误

  • 误以为 clone() 方法默认是深拷贝(实际是浅拷贝);
  • 嵌套对象未实现 Cloneable/Serializable,导致深拷贝失败;
  • 对不可变对象(如 String、Integer)过度使用深拷贝(无意义,不可变对象本身无法修改)。

2. 最佳实践

(1)选型原则
  • 简单对象(仅基本类型):用浅拷贝(clone()),高效;
  • 复杂嵌套对象:用序列化实现深拷贝(通用、易维护);
  • 避免手动递归拷贝(易漏写子对象,维护成本高)。
(2)第三方工具简化(推荐)

使用 Apache Commons Lang 的 SerializationUtils.clone() 简化序列化拷贝:

步骤 1:引入依赖

xml

复制代码
<!-- Maven 依赖 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.14.0</version>
</dependency>
步骤 2:一行实现深拷贝

java

运行

复制代码
// 前提:目标类及所有嵌套类实现 Serializable
User user2 = SerializationUtils.clone(user1);

六、核心总结

  1. 拷贝的核心是 "数据独立性":需要完全隔离原对象和拷贝对象用深拷贝,仅表层隔离用浅拷贝;
  2. 浅拷贝仅拷贝对象本身,引用类型字段共享内存,适用于简单对象或只读共享场景;
  3. 深拷贝递归拷贝所有子对象,数据完全独立,复杂嵌套场景优先用序列化(或第三方工具)实现,避免手动递归的繁琐和错误。
相关推荐
架构师沉默1 小时前
程序员真的要失业了吗?
java·后端·架构
Flittly2 小时前
【从零手写 ClaudeCode:learn-claude-code 项目实战笔记】(9)Agent Teams (智能体团队)
python·agent
小王不爱笑1322 小时前
SpringBoot 自动装配深度解析:从底层原理到自定义 starter 实战(含源码断点调试)
java·spring boot·mybatis
森林里的程序猿猿2 小时前
Spring Aop底层源码实现(一)
java·后端·spring
ℳ๓₯㎕.空城旧梦2 小时前
C++中的解释器模式
开发语言·c++·算法
DevnullCoffe2 小时前
Open Claw × 跨境电商:5个最有价值的 AI Agent 应用场景深度拆解
python·api
JdayStudy2 小时前
SIR 网络传播仿真软件说明书
开发语言·网络·php
有点傻的小可爱2 小时前
【MATLAB】新安装并口如何实现能通过PTB启用?
开发语言·windows·经验分享·matlab
zh路西法2 小时前
【宇树机器人强化学习】(六):TensorBoard图表与手柄遥控go2测试
python·深度学习·机器学习·机器人