Java 对象的深拷贝和浅拷贝

文章目录

  • 前言
  • 一、对象的深拷贝和浅拷贝
    • [1.1 浅拷贝(Shadow Copy)](#1.1 浅拷贝(Shadow Copy))
    • [1.2 深拷贝(Deep Copy)](#1.2 深拷贝(Deep Copy))
    • [1.3 最直观对比图](#1.3 最直观对比图)
    • [1.4 什么时候用?](#1.4 什么时候用?)
  • 二、拷贝工具类:
    • [2.1 浅拷贝](#2.1 浅拷贝)
    • [2.2 深拷贝](#2.2 深拷贝)
  • 总结

前言

本文记录一个java 对象的深度copy 工具类。


一、对象的深拷贝和浅拷贝

  • 浅拷贝:只拷贝第一层,里面的引用对象还是同一个(改一个,另一个也变)
  • 深拷贝:把整个对象树全部复制一份,两个对象完全独立(改一个,另一个不受影响)

1.1 浅拷贝(Shadow Copy)

原理:

  • 基本类型:值拷贝
  • 引用类型:只拷贝地址,共用同一个对象!

代码实现(实现 Cloneable 接口)

c 复制代码
@Data
public class User implements Cloneable {
    private String name;
    private Address address; // 引用对象

    // 浅拷贝
    @Override
    public User clone() {
        try {
            return (User) super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}

危险后果 :

c 复制代码
User user1 = new User();
user1.setAddress(new Address("上海"));

User user2 = user1.clone(); // 浅拷贝
user2.getAddress().setCity("北京");

// 结果:user1 的城市也变成 北京!!!

1.2 深拷贝(Deep Copy)

原理

  • 所有层级对象全部重新创建
  • 拷贝后 两个对象 100% 独立,互不影响

最常用实现方式(序列化 / JSON)

c 复制代码
// 深拷贝工具类
public static <T> T deepCopy(T obj, Class<T> clazz) {
    ObjectMapper mapper = new ObjectMapper();
    try {
        return mapper.readValue(mapper.writeValueAsString(obj), clazz);
    } catch (Exception e) {
        return null;
    }
}

使用:

c 复制代码
User user2 = deepCopy(user1, User.class);
user2.getAddress().setCity("北京");

// user1 完全不受影响 ✅

1.3 最直观对比图

浅拷贝

c 复制代码
user1 ──┐
         ├─ name (复制)
         └─ address → 【同一个对象】 ← user2.address

深拷贝

c 复制代码
user1 ── name (复制)
     └─ address → 对象A
user2 ── name (复制)
     └─ address → 对象B(全新)

1.4 什么时候用?

  • 基本类型、String → 浅拷贝就行
  • 对象里有 自定义类、集合、数组 → 必须深拷贝

二、拷贝工具类:

2.1 浅拷贝

代码如下(示例): Spring 的 BeanUtils.copyProperties 是 浅拷贝(Shadow Copy)

c 复制代码
import org.springframework.beans.BeanUtils;
BeanUtils.copyProperties(Object source, Object target)

2.2 深拷贝

代码如下(示例):

c 复制代码
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Map;

/**
 * copy
 */
public class DeepCopyUtil {

    /**
     * 深拷贝方法(支持父类字段)
     *
     * @param object 要拷贝的对象
     * @param <T>    对象类型
     * @return 深拷贝后的新对象
     * @throws Exception 拷贝失败时抛出异常
     */
    public static <T> T deepCopy(T object) throws Exception {
        if (object == null) {
            return null;
        }

        // 基本类型/不可变对象直接返回
        if (isImmutable(object.getClass())) {
            return object;
        }

        // 集合类特殊处理
        if (object instanceof Collection) {
            return (T) deepCopyCollection((Collection<?>) object);
        }
        if (object instanceof Map) {
            return (T) deepCopyMap((Map<?, ?>) object);
        }

        // 数组处理
        if (object.getClass().isArray()) {
            return (T) deepCopyArray(object);
        }

        // 普通对象处理
        return deepCopyObject(object);
    }

    // 判断是否为不可变类型
    private static boolean isImmutable(Class<?> clazz) {
        return clazz.isPrimitive()
                ||
                Number.class.isAssignableFrom(clazz)
                ||
                clazz == String.class
                ||
                clazz == Boolean.class
                ||
                clazz == Character.class;
    }

    // 深拷贝集合
    private static <E> Collection<E> deepCopyCollection(Collection<E> collection) throws Exception {
        Collection<E> copy = collection.getClass().newInstance();
        for (E item : collection) {
            copy.add(deepCopy(item));
        }
        return copy;
    }

    // 深拷贝Map
    private static <K, V> Map<K, V> deepCopyMap(Map<K, V> map) throws Exception {
        Map<K, V> copy = map.getClass().newInstance();
        for (Map.Entry<K, V> entry : map.entrySet()) {
            copy.put(deepCopy(entry.getKey()), deepCopy(entry.getValue()));
        }
        return copy;
    }

    // 深拷贝数组
    private static Object deepCopyArray(Object array) throws Exception {
        int length = Array.getLength(array);
        Object newArray = Array.newInstance(array.getClass().getComponentType(), length);
        for (int i = 0; i < length; i++) {
            Array.set(newArray, i, deepCopy(Array.get(array, i)));
        }
        return newArray;
    }

    // 深拷贝普通对象(包含父类字段)
    private static <T> T deepCopyObject(T object) throws Exception {
        // 序列化方式实现深拷贝(简单但性能较低)
        if (object instanceof Serializable) {
            return serializableDeepCopy(object);
        }

        // 反射方式实现深拷贝(性能更好)
        return reflectionDeepCopy(object);
    }

    // 通过序列化实现深拷贝
    private static <T> T serializableDeepCopy(T object) throws Exception {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(object);
            try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                 ObjectInputStream ois = new ObjectInputStream(bais)) {
                return (T) ois.readObject();
            }
        }
    }

    // 通过反射实现深拷贝(支持父类字段)
    private static <T> T reflectionDeepCopy(T object) throws Exception {
        @SuppressWarnings("unchecked")
        Class<T> clazz = (Class<T>) object.getClass();
        T copy = clazz.newInstance();

        // 拷贝当前类及所有父类的字段
        Class<?> currentClass = clazz;
        while (currentClass != null && currentClass != Object.class) {
            for (Field field : currentClass.getDeclaredFields()) {
                if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
                    continue; // 跳过静态字段
                }
                field.setAccessible(true);
                Object value = field.get(object);
                field.set(copy, deepCopy(value));
            }
            currentClass = currentClass.getSuperclass();
        }

        return copy;
    }
}

总结

以上为 java 对象浅拷贝,深拷贝的介绍,使用场景,以及工具类的实现。

相关推荐
Scott9999HH6 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔6 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社7 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海7 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖8 小时前
JDK 26 新特性详解
java·开发语言
马优晨8 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区10 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大11 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai11 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说11 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json