手写一个深拷贝工具

背景

在面向对象编程中,对象之间的复制是一个常见的需求。对象的复制通常分为浅拷贝(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);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/922180.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习实战人脸识别

文章目录 前言一、人脸识别一般过程二、人脸检测主流算法1. MTCNN2. RetinaFace3. CenterFace4. BlazeFace5. YOLO6. SSD7. CascadeCNN 三、人脸识别主流算法1.deepface2.FaceNet3.ArcFace4.VGGFace5.DeepID 四、人脸识别系统实现0.安装教程与资源说明1. 界面采用PyQt5框架2.人…

Qt 字符串、信号槽详解

目录 1.QT中字符串QString的使用 1.1.常见的函数 1.2.总结QT中跟字符&#xff0c;字符串有关的类 1.3.字符串列表StringList 2.信号与槽 1.作用 2.右键转到槽函数 3.关联信号与槽函数(让你的信号跟槽函数之间形成一一对应关系) 4.信号与槽的特点 3.总结 1.继承自QMainWin…

Vscode进行Java开发环境搭建

Vscode进行Java开发环境搭建 搭建Java开发环境(Windows)1.Jdk安装2.VsCode安装3.Java插件4.安装 Spring 插件5.安装 Mybatis 插件5.安装Maven环境6.Jrebel插件7.IntelliJ IDEA Keybindings8. 收尾 VS Code&#xff08;Visual Studio Code&#xff09;是由微软开发的一款免费、开…

响应式数据(v-on、v-if、v-show、v-for、v-bind、v-model、computed、watch)

目录 一、事件绑定指令v-on 二、条件渲染指令v-if 三、v-show 四、遍历指令v-for 1、遍历对象的值 2、遍历对象的值和键&#xff08;先值后键&#xff09; 3、遍历对象的值、键和索引 4、遍历数组的值和索引 五、属性动态化指令v-bind(单向) 【CSS样式的绑定&#…

设计模式之 命令模式

命令模式&#xff08;Command Pattern&#xff09;是行为型设计模式之一&#xff0c;它将请求&#xff08;或命令&#xff09;封装成一个对象&#xff0c;从而使用户能够将请求发送者与请求接收者解耦。通过命令模式&#xff0c;调用操作的对象与执行操作的对象不直接关联&…

微信小程序2-地图显示和地图标记

一、index修改页面&#xff0c;让页面能够显示地图和一个添加标记的按钮。 index.wxml <scroll-view class"scrollarea" scroll-y type"list"><view class"index_container"><map id"map" style"width: 100%; h…

使用eclipse构建SpringBoot项目

我这里用eclipse2018版本做演示&#xff0c;大家有需要的可以下载Eclipse Downloads | The Eclipse Foundation 1.打开eclipse&#xff0c;选择存放代码的位置 2.选择 file >> new >> project >> 选择springboot文件下的 spring starter project 2.这里选择N…

uniapp接入BMapGL百度地图

下面代码兼容安卓APP和H5 百度地图官网&#xff1a;控制台 | 百度地图开放平台 应用类别选择《浏览器端》 /utils/map.js 需要设置你自己的key export function myBMapGL1() {return new Promise(function(resolve, reject) {if (typeof window.initMyBMapGL1 function) {r…

绕过CDN寻找真实IP

在新型涉网案件中&#xff0c;我们在搜集到目标主站之后常常需要获取对方网站的真实IP去进一步的信息搜集&#xff0c;但是现在网站大多都部署了CDN&#xff0c;将资源部署分发到边缘服务器&#xff0c;实现均衡负载&#xff0c;降低网络堵塞&#xff0c;让用户能够更快地访问自…

【计算机网络】网段划分

一、为什么有网段划分 IP地址 网络号(目标网络) 主机号(目标主机) 网络号: 保证相互连接的两个网段具有不同的标识 主机号: 同一网段内&#xff0c;主机之间具有相同的网络号&#xff0c;但是必须有不同的主机号 互联网中的每一台主机&#xff0c;都要隶属于某一个子网 -&…

HarmonyOS笔记5:ArkUI框架的Navigation导航组件

ArkUI框架的Navigation导航组件 在移动应用中需要在不同的页面进行切换跳转。这种切换和跳转有两种方式&#xff1a;页面路由和Navigation组件实现导航。HarmonyOS推荐使用Navigation实现页面跳转。在本文中在HarmonyOS 5.0.0 Release SDK (API Version 12 Release)版本下&…

基于阿里云服务器部署静态的website

目录 一&#xff1a;创建服务器实例并connect 二&#xff1a;本地文件和服务器share 三&#xff1a;关于IIS服务器的安装预配置 四&#xff1a;设置安全组 五&#xff1a;建站流程 六&#xff1a;关于备案 一&#xff1a;创建服务器实例并connect 创建好的服务器实例在云…

mysql根据日期查询没有的日期也要显示数据

先查询出日期数据(当前日期往前推12个月) select bb.datefrom (select num : num 1,date_format(adddate(date_sub(date_sub(curdate(),interval 12 month),interval 1 month),interval num month), %Y-%m) as datefrom mysql.help_topic,(select num : 0) as twhere addd…

Go语言链接Redis数据库

1.使用go get命令安装go-redis/v8库&#xff1a; 我这里使用的vscode工具安装&#xff1a; go get github.com/go-redis/redis/v82.创建Redis客户端实例 使用以下Go代码连接到Redis服务器并执行命令&#xff1a; package mainimport ("context""fmt"&q…

1-测试go-redis缓存数据

1-测试go-redis缓存数据 1.go-redis缓存数据测试效果 a.测试页面 测试页面&#xff1a;--这里使用 Postman 来做测试 http://127.0.0.1:8000/article/getone/3 http://127.0.0.1:8000/article/getone/4 http://127.0.0.1:8000/article/getone/5b.测试效果 查看终端&#xf…

查看浏览器的请求头

爬虫时用到了请求头&#xff0c;虽然可以用网上公开的&#xff0c;但是还是想了解一下本机浏览器的。以 Edge 为例&#xff0c;其余浏览器通用。 打开浏览器任一网页&#xff0c;按F12打开DevTools&#xff1b;或鼠标右键&#xff0c;选择“检查”。首次打开界面应该显示在网页…

git branch -d 删除分支

Git进行版本控制时&#xff0c;删除分支是常见的操作。特别是当特定的功能开发完成或者分支不再需要时&#xff0c;删除分支可以帮助保持仓库的整洁。删除本地分支和删除远端分支是两个独立的操作。如果需要同时删除本地和远端的分支&#xff0c;需要分别执行以下两个命令。 一…

【漏洞复现】|智互联SRM智联云采系统quickReceiptDetail SQL注入漏洞

漏洞描述 智互联(深圳)科技有限公司SRM智联云采系统针对企业供应链管理难题&#xff0c;及智能化转型升级需求&#xff0c;智联云采依托人工智能、物联网、大数据、云等技术&#xff0c;通过软硬件系统化方案&#xff0c;帮助企业实现供应商关系管理和采购线上化、移动化、智能…

[论文阅读] 异常检测 Deep Learning for Anomaly Detection: A Review(三)总结梳理-疑点记录

《深度异常检测综述》总结梳理 目录 一、研究背景与挑战二、深度异常检测方法分类三、实验评估四、结论在这篇文章中&#xff0c;**异常检测的异构性**主要从以下几个方面来理解&#xff1a;如何理解多源数据融合的困难“学习正常性的特征表示”与“用于特征提取的深度学习”在…

linux下i2c开发与框架源码分析

目录 1 概述 2 I2c子系统框架 3 I2C的使用流程 3.1 在驱动里使用 3.2 在应用层使用 3.3 I2ctool的使用 4 为硬件i2c注册一个适配器 5 i2c子系统源码流程分析 5.1 i2c device与driver绑定过程 5.1.1 Driver的注册与处理 5.1.2 Client device的生成 5.2 I2c的发送与接…