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

相关推荐
fie88891 小时前
matlab打靶法求解两点边值优化问题
开发语言·算法·matlab
skywalk81631 小时前
请结合以下说明,先完成类似python的内置函数。 然后再去完成内置库(标准款) ‌内置函数‌
开发语言·python
我不是懒洋洋2 小时前
手写一个异步日志库:从printf到高性能无锁日志
java·c语言·开发语言·c++·visual studio
郝学胜-神的一滴2 小时前
Python 高级编程 018:深挖 super
开发语言·python·程序人生·软件构建
hoiii1872 小时前
基于MATLAB实现Lamb波频散曲线求解
开发语言·matlab
李少兄2 小时前
Java 工程化基石:标准目录结构与 META-INF 元信息机制
java·开发语言
就叫_这个吧2 小时前
理解Java反射机制和内省机制应用与实践
java·开发语言·反射
未若君雅裁2 小时前
synchronized 底层原理:Monitor、对象头、Mark Word 与锁升级
java
尤老师FPGA2 小时前
QT代码自适应窗口
开发语言·qt