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 对象浅拷贝,深拷贝的介绍,使用场景,以及工具类的实现。

相关推荐
A_nanda几秒前
c#WPF开发常见问题
开发语言·c#·wpf
Hello.Reader16 分钟前
REALITY 技术深度解析从 TLS 伪装、抗主动探测到 VLESS + XTLS Vision 完整实践
开发语言·php
(╹◡╹)19 分钟前
11.RK3588本地大模型内存评估优化
java·linux·前端
不正经学生24 分钟前
C语言字符串函数进阶:安全版函数 + 错误处理 + 子串查找
c语言·开发语言·c++·算法·面试
ITKEY_35 分钟前
neovim基于nvim-jdtls 开发java报错
java·开发语言
leon8737 分钟前
MyBatis-Plus与AOP实现动态数据权限过滤的实践与踩坑记录
java
电气研究所37 分钟前
Java + Redis 实现工业设备告警去抖,避免变频器故障重复推送
java·前端
無限進步D38 分钟前
Java 代码块
java·开发语言
吃好睡好便好38 分钟前
MATLAB仿真框图4
开发语言·matlab·仿真·分段函数
用户31268748772042 分钟前
@Async 加了没生效?Spring Boot 异步编程全链路拆解:从线程池到自调用陷阱
java·spring boot