手写一个深拷贝工具

背景

在面向对象编程中,对象之间的复制是一个常见的需求。对象的复制通常分为浅拷贝(Shallow Copy)和深拷贝(Deep Copy)两种方式。浅拷贝只复制对象的基本数据类型和引用类型的数据地址,而深拷贝则会递归地复制对象及其引用的所有子对象,确保新对象与原对象完全独立。

深拷贝的重要性在于它可以避免对象之间的相互影响。在实际开发中,如果不正确地处理对象的复制,可能会导致数据不一致、内存泄漏等问题。因此,掌握深拷贝的实现方法对于提高代码质量和系统稳定性具有重要意义。

深拷贝介绍

深拷贝(Deep Copy)是指在复制对象时,不仅复制对象本身,还递归地复制对象中引用的所有子对象。这样可以确保新对象与原对象完全独立,修改新对象不会影响原对象。

浅拷贝的不足

在 Java 中,如果我们简单地使用赋值语句将一个对象赋值给另一个对象,例如:

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getters and setters
}

public class Main {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", 25);
        Person person2 = person1;

        person2.setAge(30);

        System.out.println(person1.getAge()); // 输出 30,而不是 25
    }
}

这里,person2 只是 person1 的浅拷贝,它们指向同一个内存地址。当修改 person2 的属性时,person1 的属性也随之改变,因为它们本质上是同一个对象。

深拷贝的应用场景

  1. 多线程环境:在多线程环境中,为了避免多个线程对同一个对象的访问导致的数据不一致问题,可以使用深拷贝来创建独立的对象副本。
  2. 数据备份:在进行数据备份时,深拷贝可以确保备份数据与原始数据完全独立,避免因修改原始数据而影响备份数据。
  3. 历史记录:在需要保存对象的历史状态时,深拷贝可以创建对象的完整副本,以便后续回溯或恢复。
  4. 对象传递:在需要将对象传递给其他方法或模块时,使用深拷贝可以确保传递的是一个独立的副本,避免意外的修改影响原对象。

代码示例

下面是一个使用 Java 反射机制实现深拷贝的示例。这个示例展示了如何将一个复杂的对象深拷贝到另一个对象中,即使这两个对象是不同的类,但具有相同名称和类型的字段。

定义实体类

// 嵌套对象 A
public class NestedObjectA {
    private int id;
    private String name;

    // Getters and Setters
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    @Override
    public String toString() {
        return "NestedObjectA{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

// 嵌套对象 B
public class NestedObjectB {
    private double value;
    private NestedObjectA nestedObjectA;

    // Getters and Setters
    public double getValue() { return value; }
    public void setValue(double value) { this.value = value; }

    public NestedObjectA getNestedObjectA() { return nestedObjectA; }
    public void setNestedObjectA(NestedObjectA nestedObjectA) { this.nestedObjectA = nestedObjectA; }

    @Override
    public String toString() {
        return "NestedObjectB{" +
                "value=" + value +
                ", nestedObjectA=" + nestedObjectA +
                '}';
    }
}

// 源对象
public class SourceObject {
    private int intField;
    private String stringField;
    private List<NestedObjectA> listField;
    private Map<String, NestedObjectB> mapField;

    // Getters and Setters
    public int getIntField() { return intField; }
    public void setIntField(int intField) { this.intField = intField; }

    public String getStringField() { return stringField; }
    public void setStringField(String stringField) { this.stringField = stringField; }

    public List<NestedObjectA> getListField() { return listField; }
    public void setListField(List<NestedObjectA> listField) { this.listField = listField; }

    public Map<String, NestedObjectB> getMapField() { return mapField; }
    public void setMapField(Map<String, NestedObjectB> mapField) { this.mapField = mapField; }

    @Override
    public String toString() {
        return "SourceObject{" +
                "intField=" + intField +
                ", stringField='" + stringField + '\'' +
                ", listField=" + listField +
                ", mapField=" + mapField +
                '}';
    }
}

// 目标对象
public class TargetObject {
    private int intField;
    private String stringField;
    private List<NestedObjectA> listField;
    private Map<String, NestedObjectB> mapField;

    // Getters and Setters
    public int getIntField() { return intField; }
    public void setIntField(int intField) { this.intField = intField; }

    public String getStringField() { return stringField; }
    public void setStringField(String stringField) { this.stringField = stringField; }

    public List<NestedObjectA> getListField() { return listField; }
    public void setListField(List<NestedObjectA> listField) { this.listField = listField; }

    public Map<String, NestedObjectB> getMapField() { return mapField; }
    public void setMapField(Map<String, NestedObjectB> mapField) { this.mapField = mapField; }

    @Override
    public String toString() {
        return "TargetObject{" +
                "intField=" + intField +
                ", stringField='" + stringField + '\'' +
                ", listField=" + listField +
                ", mapField=" + mapField +
                '}';
    }
}

DeepCopyUtil 深拷贝工具类

public class DeepCopyUtil {

    public static void deepCopyFields(Object source, Object target) throws IllegalAccessException {
        Class<?> sourceClass = source.getClass();
        Class<?> targetClass = target.getClass();

        // 获取源对象和目标对象的所有字段
        Field[] sourceFields = getAllFields(sourceClass);
        Field[] targetFields = getAllFields(targetClass);

        // 创建字段名称到字段的映射
        Map<String, Field> targetFieldMap = new HashMap<>();
        for (Field field : targetFields) {
            field.setAccessible(true);
            targetFieldMap.put(field.getName(), field);
        }

        // 遍历源对象的字段
        for (Field sourceField : sourceFields) {
            sourceField.setAccessible(true);
            Object sourceValue = sourceField.get(source);

            // 检查目标对象是否有同名字段
            Field targetField = targetFieldMap.get(sourceField.getName());
            if (targetField != null && sourceField.getType().equals(targetField.getType())) {
                if (isBasicTypeOrWrapperOrString(sourceField.getType())) {
                    // 如果是基本类型或字符串,直接赋值给目标对象的对应字段
                    targetField.set(target, sourceValue);
                } else if (List.class.isAssignableFrom(sourceField.getType())) {
                    // 如果是列表类型,进行列表元素的深拷贝
                    List<?> sourceList = (List<?>) sourceValue;
                    List<Object> targetList = new ArrayList<>();
                    for (Object item : sourceList) {
                        targetList.add(deepCopy(item));
                    }
                    targetField.set(target, targetList);
                } else if (Set.class.isAssignableFrom(sourceField.getType())) {
                    // 如果是集合类型,进行集合元素的深拷贝
                    Set<?> sourceSet = (Set<?>) sourceValue;
                    Set<Object> targetSet = new HashSet<>();
                    for (Object item : sourceSet) {
                        targetSet.add(deepCopy(item));
                    }
                    targetField.set(target, targetSet);
                } else if (Map.class.isAssignableFrom(sourceField.getType())) {
                    // 如果是映射类型,进行映射元素的深拷贝
                    Map<?, ?> sourceMap = (Map<?, ?>) sourceValue;
                    Map<Object, Object> targetMap = new HashMap<>();
                    for (Map.Entry<?, ?> entry : sourceMap.entrySet()) {
                        Object key = deepCopy(entry.getKey());
                        Object value = deepCopy(entry.getValue());
                        targetMap.put(key, value);
                    }
                    targetField.set(target, targetMap);
                } else {
                    // 其他类型进行深拷贝
                    targetField.set(target, deepCopy(sourceValue));
                }
            }
        }
    }

    private static Field[] getAllFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        while (clazz != null) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
            clazz = clazz.getSuperclass();
        }
        return fields.toArray(new Field[0]);
    }

    private static boolean isBasicTypeOrWrapperOrString(Class<?> clazz) {
        return clazz.equals(int.class) || clazz.equals(Integer.class) ||
                clazz.equals(long.class) || clazz.equals(Long.class) ||
                clazz.equals(float.class) || clazz.equals(Float.class) ||
                clazz.equals(double.class) || clazz.equals(Double.class) ||
                clazz.equals(boolean.class) || clazz.equals(Boolean.class) ||
                clazz.equals(char.class) || clazz.equals(Character.class) ||
                clazz.equals(byte.class) || clazz.equals(Byte.class) ||
                clazz.equals(short.class) || clazz.equals(Short.class) ||
                clazz.equals(String.class);
    }

    private static Object deepCopy(Object obj) {
        if (obj == null) {
            return null;
        }
        if (isBasicTypeOrWrapperOrString(obj.getClass())) {
            return obj;
        }
        if (obj instanceof List) {
            List<?> sourceList = (List<?>) obj;
            List<Object> targetList = new ArrayList<>();
            for (Object item : sourceList) {
                targetList.add(deepCopy(item));
            }
            return targetList;
        }
        if (obj instanceof Set) {
            Set<?> sourceSet = (Set<?>) obj;
            Set<Object> targetSet = new HashSet<>();
            for (Object item : sourceSet) {
                targetSet.add(deepCopy(item));
            }
            return targetSet;
        }
        if (obj instanceof Map) {
            Map<?, ?> sourceMap = (Map<?, ?>) obj;
            Map<Object, Object> targetMap = new HashMap<>();
            for (Map.Entry<?, ?> entry : sourceMap.entrySet()) {
                Object key = deepCopy(entry.getKey());
                Object value = deepCopy(entry.getValue());
                targetMap.put(key, value);
            }
            return targetMap;
        }
        // 对于其他复杂对象,递归调用 deepCopyFields 方法
        try {
            Object target = obj.getClass().getDeclaredConstructor().newInstance();
            deepCopyFields(obj, target);
            return target;
        } catch (Exception e) {
            throw new RuntimeException("Failed to deep copy object", e);
        }
    }
}

Main方法

 public static void main(String[] args) throws IllegalAccessException {
        // 创建示例对象
        SourceObject source = new SourceObject();
        TargetObject target = new TargetObject();

        // 初始化源对象
        source.setIntField(10);
        source.setStringField("Hello");

        List<NestedObjectA> listField = new ArrayList<>();
        listField.add(new NestedObjectA());
        listField.get(0).setId(1);
        listField.get(0).setName("A1");
        source.setListField(listField);

        Map<String, NestedObjectB> mapField = new HashMap<>();
        NestedObjectB nestedObjectB = new NestedObjectB();
        nestedObjectB.setValue(3.14);
        nestedObjectB.setNestedObjectA(new NestedObjectA());
        nestedObjectB.getNestedObjectA().setId(2);
        nestedObjectB.getNestedObjectA().setName("A2");
        mapField.put("key1", nestedObjectB);
        source.setMapField(mapField);

        // 进行深拷贝
        deepCopyFields(source, target);

        // 输出结果
        System.out.println("Source: " + source);
        System.out.println("Target: " + target);
    }
}
相关推荐
是小崔啊12 分钟前
开源轮子 - EasyExcel02(深入实践)
java·开源·excel
myNameGL43 分钟前
linux安装idea
java·ide·intellij-idea
青春男大1 小时前
java栈--数据结构
java·开发语言·数据结构·学习·eclipse
HaiFan.1 小时前
SpringBoot 事务
java·数据库·spring boot·sql·mysql
我要学编程(ಥ_ಥ)1 小时前
一文详解“二叉树中的深搜“在算法中的应用
java·数据结构·算法·leetcode·深度优先
music0ant2 小时前
Idea 添加tomcat 并发布到tomcat
java·tomcat·intellij-idea
计算机徐师兄2 小时前
Java基于SSM框架的无中介租房系统小程序【附源码、文档】
java·微信小程序·小程序·无中介租房系统小程序·java无中介租房系统小程序·无中介租房微信小程序
源码哥_博纳软云2 小时前
JAVA智慧养老养老护理帮忙代办陪诊陪护小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台
忒可君3 小时前
C# winform 报错:类型“System.Int32”的对象无法转换为类型“System.Int16”。
java·开发语言
斌斌_____3 小时前
Spring Boot 配置文件的加载顺序
java·spring boot·后端