Java对象的深拷贝

news2024/11/24 15:02:50

什么是深拷贝

在Java中,对象的深拷贝是指创建一个新的对象,并复制原始对象的所有字段和属性,包括嵌套对象。深拷贝确保原始对象和拷贝对象是完全独立的,对其中一个对象的修改不会影响另一个对象。

深拷贝需要注意的点

在Java中,对象的深拷贝需要注意以下几点:

  1. 不可拷贝对象的状态,只能拷贝对象的状态描述。Java中对象的状态包括实例变量和内部类实例等。
  2. 对于可变对象,深拷贝后修改原始对象中的值也会修改拷贝对象中的值。因此,在进行深拷贝时需要创建新的对象,而不是直接引用原始对象。
  3. 对于不可变对象,深拷贝后修改原始对象中的值不会影响拷贝对象中的值。
  4. 对于数组和集合类对象,深拷贝时需要拷贝数组或集合中的每一个元素,而不仅仅是引用。
  5. 对于非内存中的对象,例如文件、网络资源等,深拷贝时需要重新创建资源而不是简单地引用。
  6. 对于非序列化的对象,深拷贝时需要通过实现Cloneable接口并重写clone()方法来实现。
  7. 对于实现了Serializable接口的对象,可以通过序列化和反序列化来实现深拷贝。但是需要注意,如果对象中有不可序列化的字段,则无法通过序列化实现深拷贝。

Java中对象的深拷贝需要注意不可拷贝对象的状态、创建新的对象、拷贝每个元素、重新创建资源以及实现Cloneable接口或实现Serializable接口等方法。需要根据具体情况选择合适的方法来实现深拷贝。

实现深拷贝的几种方式

要实现深拷贝,可以通过以下方式:

使用克隆方法:

Java中的clone()方法可以用于创建对象的深拷贝。为了使一个类可克隆,需要实现Cloneable接口并重写clone()方法。clone()方法默认使用浅拷贝,如果要进行深拷贝,需要在方法内部手动进行。


import java.lang.reflect.*;
import java.util.*;
public class DeepCopyExample {
    public static void main(String[] args) {
        // 创建一个包含嵌套对象的对象
        Person person = new Person("John", 25, new Address(123 Main St, "New York", "NY"));
        // 创建一个深拷贝对象
        Person deepCopy = DeepCopyExample.deepCopyUsingClone(person);
        // 修改原始对象的属性,并查看深拷贝对象的属性是否也发生了改变
        person.setName("Jane");
        person.setAge(30);
        person.getAddress().setStreet("456 Elm St");
        System.out.println("Original Person:");
        System.out.println("Name: " + person.getName());
        System.out.println("Age: " + person.getAge());
        System.out.println("Address:");
        System.out.println(person.getAddress().getStreet());
        System.out.println("Deep Copy Person:");
        System.out.println("Name: " + deepCopy.getName());
        System.out.println("Age: " + deepCopy.getAge());
        System.out.println("Address:");
        System.out.println(deepCopy.getAddress().getStreet());
    }
    public static <T> T deepCopyUsingClone(T object) {
        try {
            T copy = object.getClass().newInstance();
            copy = (T) object.getClass().getMethod("clone").invoke(object);
            return copy;
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
            return null;
        }
    }
}
class Person implements Cloneable, Serializable {
    private String name;
    private int age;
    private Address address;
     // 省略了构造方法和Getter和Setter方法
    @Override
    public Person clone() {
        try {
            return (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}
class Address implements Cloneable, Serializable {
    private String street;
    private String city;
    private String state;
    // Constructor, getters, and setters omitted for brevity
    @Override
    public Address clone() {
        try {
            return (Address) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}


在这个例子中,deepCopyUsingClone()方法使用Java反射机制来调用对象的clone()方法,从而创建对象的深拷贝。注意,这个例子仅仅适用于实现了Cloneable接口的对象。如果对象没有实现Cloneable接口,那么需要使用其他方法来实现深拷贝。同时,需要注意的是,如果对象的字段中有不可序列化的字段,那么使用克隆方法也无法实现深拷贝。

存在的问题
  • 实现深拷贝时需要在每个类中重写clone()方法,这会增加代码的复杂度和维护成本。
  • 如果类中有不可克隆的字段(例如静态字段或非原始类型字段),则无法使用clone()方法实现深拷贝。
  • 如果类中有循环引用,会导致无限递归,从而引发栈溢出异常。

使用反射实现

以下是一个使用Java反射实现深拷贝的示例代码:


import java.lang.reflect.*;
import java.util.*;
public class DeepCopyExample {
    public static void main(String[] args) {
        // 创建一个包含嵌套对象的对象
        Person person = new Person("John", 25, new Address(123 Main St, "New York", "NY"));
        // 创建一个深拷贝对象
        Person deepCopy = DeepCopyExample.deepCopyUsingReflection(person);
        // 修改原始对象的属性,并查看深拷贝对象的属性是否也发生了改变
        person.setName("Jane");
        person.setAge(30);
        person.getAddress().setStreet("456 Elm St");
        System.out.println("Original Person:");
        System.out.println("Name: " + person.getName());
        System.out.println("Age: " + person.getAge());
        System.out.println("Address:");
        System.out.println(person.getAddress().getStreet());
        System.out.println("Deep Copy Person:");
        System.out.println("Name: " + deepCopy.getName());
        System.out.println("Age: " + deepCopy.getAge());
        System.out.println("Address:");
        System.out.println(deepCopy.getAddress().getStreet());
    }
    public static <T> T deepCopyUsingReflection(T object) {
        try {
            T copy = object.getClass().newInstance();
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                if (field.get(object) instanceof Serializable) {
                    field.set(copy, field.get(object));
                } else {
                    field.set(copy, deepCopyUsingReflection(field.get(object)));
                }
            }
            return copy;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }
}
class Person implements Serializable {
    private String name;
    private int age;
    private Address address;
    // 省略了构造方法和Getter和Setter方法
}
class Address implements Serializable {
    private String street;
    private String city;
    private String state;
    // 省略了构造方法和Getter和Setter方法
}


在这个例子中,deepCopyUsingReflection()方法使用Java反射机制来获取对象的所有字段,并递归地深拷贝每个字段的值。如果字段的值是可序列化的,那么可以直接拷贝;如果字段的值是非序列化的,那么需要递归地调用该方法来进行深拷贝。注意,这个例子仅仅适用于没有非序列化字段的对象。如果对象中包含非序列化的字段,那么需要使用其他方法来实现深拷贝。

使用序列化:

另一种实现深拷贝的方法是通过将对象序列化到字节数组中,然后从字节数组重新创建对象。这种方法可以确保对象的完整深拷贝,包括嵌套对象。


import java.util.*;
public class DeepCopyExample {
    public static void main(String[] args) {
        // 创建一个包含嵌套对象的对象
        Person person = new Person("John", 25, new Address(123 Main St, "New York", "NY"));
        // 创建一个深拷贝对象
        Person deepCopy = DeepCopyExample.deepCopy(person);
        // 修改原始对象的属性,并查看深拷贝对象的属性是否也发生了改变
        person.setName("Jane");
        person.setAge(30);
        person.getAddress().setStreet("456 Elm St");
        System.out.println("Original Person:");
        System.out.println("Name: " + person.getName());
        System.out.println("Age: " + person.getAge());
        System.out.println("Address:");
        System.out.println(person.getAddress().getStreet());
        System.out.println("Deep Copy Person:");
        System.out.println("Name: " + deepCopy.getName());
        System.out.println("Age: " + deepCopy.getAge());
        System.out.println("Address:");
        System.out.println(deepCopy.getAddress().getStreet());
    }
    public static <T extends Serializable> T deepCopy(T object) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(object);
            oos.flush();
            oos.close();
            bos.close();
            byte[] byteArray = bos.toByteArray();
            ByteArrayInputStream bis = new ByteArrayInputStream(byteArray);
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (T) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
}
class Person implements Serializable {
    private String name;
    private int age;
    private Address address;
    // 省略了构造方法和Getter和Setter方法
}
class Address implements Serializable {
    private String street;
    private String city;
    private String state;
    // 省略了构造方法和Getter和Setter方法
}


在这个例子中,Person和Address类都实现了Serializable接口,因此它们可以被序列化和反序列化来实现深拷贝。deepCopy()方法使用Java序列化和反序列化的机制来实现深拷贝。注意,这个例子仅仅适用于实现了Serializable接口的对象。如果对象中包含非序列化的字段,那么需要使用其他方法来实现深拷贝。

存在的问题
  • 序列化过程相对较慢,并且会增加对象的开销。
  • 序列化后,对象的类定义需要在序列化的字节数组中保持一致,否则会导致反序列化失败。
  • 如果对象中有不可序列化的字段(例如静态字段或非原始类型字段),则无法使用序列化实现深拷贝。

使用递归方法

以下是另一个实现深拷贝的示例代码,这次使用的是递归方法:

public class DeepCopyExample {
    public static void main(String[] args) {
        // 创建一个包含嵌套对象的对象
        Person person = new Person("John", 25, new Address(123 Main St, "New York", "NY"));
        // 创建一个深拷贝对象
        Person deepCopy = deepCopyUsingRecursion(person);
        // 修改原始对象的属性,并查看深拷贝对象的属性是否也发生了改变
        person.setName("Jane");
        person.setAge(30);
        person.getAddress().setStreet("456 Elm St");
        System.out.println("Original Person:");
        System.out.println("Name: " + person.getName());
        System.out.println("Age: " + person.getAge());
        System.out.println("Address:");
        System.out.println(person.getAddress().getStreet());
        System.out.println("Deep Copy Person:");
        System.out.println("Name: " + deepCopy.getName());
        System.out.println("Age: " + deepCopy.getAge());
        System.out.println("Address:");
        System.out.println(deepCopy.getAddress().getStreet());
    }
    public static Person deepCopyUsingRecursion(Person person) {
        if (person == null) {
            return null;
        }
        Person newPerson = new Person(person.getName(), person.getAge(), null); // 初始化新对象时,第三个参数暂时为null,等待后续处理
        if (person.getAddress() != null) { // 如果原始对象有地址,则递归拷贝地址对象
            newPerson.setAddress(deepCopyUsingRecursion(person.getAddress()));
        }
        return newPerson;
    }
}

使用第三方类库:Google Guava

以下是使用第三方类库Google Guava来实现深拷贝的示例代码:


import com.google.common.reflect.DeepCopy;
public class DeepCopyExample {
    public static void main(String[] args) {
        // 创建一个包含嵌套对象的对象
        Person person = new Person("John", 25, new Address(123 Main St, "New York", "NY"));
        // 使用Guava库实现深拷贝
        Person deepCopy = DeepCopy.copy(person);
        // 修改原始对象的属性,并查看深拷贝对象的属性是否也发生了改变
        person.setName("Jane");
        person.setAge(30);
        person.getAddress().setStreet("456 Elm St");
        System.out.println("Original Person:");
        System.out.println("Name: " + person.getName());
        System.out.println("Age: " + person.getAge());
        System.out.println("Address:");
        System.out.println(person.getAddress().getStreet());
        System.out.println("Deep Copy Person:");
        System.out.println("Name: " + deepCopy.getName());
        System.out.println("Age: " + deepCopy.getAge());
        System.out.println("Address:");
        System.out.println(deepCopy.getAddress().getStreet());
    }
}


需要注意的是,使用第三方类库来实现深拷贝可能会增加代码的依赖性和复杂性。因此,在使用之前需要仔细评估其优缺点。

深拷贝存在的性能问题

  • 深拷贝比浅拷贝需要更多的内存和计算资源,因为它需要创建新的对象并复制原始对象的所有属性。
  • 对于大型对象或复杂对象图,深拷贝可能会变得非常慢,因为它需要复制大量的数据
  • 深拷贝可能会导致程序变得复杂和混乱,因为它需要在对象的各个部分之间建立新的连接。
  • 深拷贝可能会导致对象之间的循环引用,这会使对象无法被正确地释放,从而浪费内存。
  • 深拷贝需要实现Cloneable接口并重写clone()方法,这会增加代码的复杂度和维护成本。
  • 对于非序列化的对象,深拷贝可能无法正确地复制对象的所有属性,特别是对于非原始类型的属性。
  • 对于可变对象,深拷贝可能会导致对象的不一致性,因为修改原始对象也会修改拷贝的对象。

深拷贝的优化方案

  • 对于大型对象或复杂对象图,可以采用分块深拷贝或增量深拷贝的方法,以减少内存和计算资源的消耗。
  • 对于可变对象,可以采用不可变对象替代可变对象的方法,以避免修改原始对象时对拷贝对象的影响。
  • 对于非序列化的对象,可以采用其他方法实现深拷贝,例如使用反射或手动复制每个属性。
  • 对于对象的属性,可以采用浅拷贝的方法来减少内存和计算资源的消耗。
  • 可以使用缓存来避免重复创建相同对象的深拷贝,以减少计算资源的消耗。
  • 可以使用对象池或缓存来管理深拷贝后的对象,以避免内存泄漏和资源浪费。

综上所述,优化方案需要根据具体情况选择合适的方法来减少内存和计算资源的消耗,并避免出现不一致性和循环引用等问题。

在这里插入图片描述

最后

除了Java的深拷贝,还有其它的Java相关的知识,欢迎大家学习:

[玫瑰]Java基本数据类型的初始值
[玫瑰]Java中Deque接口方法解析
[玫瑰]List中set方法和add方法的区别

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

震裕科技-300953 三季报分析(20231108)

震裕科技-300953 基本情况 公司名称&#xff1a;宁波震裕科技股份有限公司 A股简称&#xff1a;震裕科技 成立日期&#xff1a;1994-10-18 上市日期&#xff1a;2021-03-18 所属行业&#xff1a;专用设备制造业 周期性&#xff1a;0 主营业务&#xff1a;精密级进冲压模具及下游…

【hcie-cloud】【5】华为云Stack规划设计之华为云Stack标准化配置、缩略语【下】

文章目录 前言、华为云Stack交付综述为云Stack标准组网华为云Stack标准化配置华为云Stack配置概览华为云Stack云服务全视图华为云Stack部署方案节点类型说明华为云Stack云服务组件部署场景管理节点部署原则云平台管理规格华为云Stack IaaS场景&高阶场景起步必选部署组件x86…

Python高级语法----Python多线程与多进程

文章目录 多线程多进程注意事项多线程与多进程是提高程序性能的两种常见方法。在深入代码之前,让我们先用一个简单的比喻来理解它们。 想象你在一家餐厅里工作。如果你是一个服务员,同时负责多个桌子的顾客,这就类似于“多线程”——同一个人(程序)同时进行多项任务(线程…

RflySim | 滤波器设计实验二

本讲是关于无人机滤波器&#xff0c;其中包括无人机滤波器简介、测量原理、线性互补滤波器设计、线性互补滤波器参数分析、卡尔曼滤波器设计等。 滤波器设计实验2 卡尔曼滤波器是一种递推线性最小方差估计算法&#xff0c;它的最优估计需满足以下三个条件&#xff1a; 1&#…

【Spring】静态代理

例子&#xff1a; 租房子 角色&#xff1a; 我 &#xff08;I ) 中介( Proxy ) 房东( host ) Rent 接口 package org.example;public interface Rent {void rent(); }房东 package org.example;public class Host implements Rent{Overridepublic void rent() …

NAT协议

目录 NAT 前言 NAT地址转换表 NAT分类 前言 静态NAT 192.168.1.2访问200.1.1.2执行过程 动态NAT 192.168.1.2访问200.1.1.2执行过程 NAPT 192.168.1.2的5000端口访问200.1.1.2的80端口执行过程 基本命令 配置动态NAPT转换 定义内外网接口 配置NAPT 静态NAPT配置…

NetworkManager 图形化配置 bond

1、在桌面右下角找到网络连接标识&#xff0c;鼠标右击&#xff0c;选择编辑连接&#xff0c;如下图 注意&#xff1a;此次示例使用 ens37 和 ens38 两张网卡组成 bond。在配置 bond 前为了网 络稳定如果子网卡已有网络连接的建议先删除 bond 子网卡的网络连接。 2、单击按钮&a…

Python高级语法----Python异步编程入门

文章目录 异步编程概念asyncio模块基础event loop和coroutineasync与await关键字代码示例结论在现代软件开发中,异步编程已经成为一个不可或缺的概念,尤其是在处理I/O密集型任务和高并发需求时。Python作为一门多范式编程语言,自3.5版本以来,通过引入asyncio模块和async/aw…

UseGalaxy.cn生信云平台文本文件操作手册

文本文件是生物信息学中应用非常广泛的文本格式&#xff0c;甚至可以说是最重要的文件格式&#xff0c;比如常见的测序下机数据Fastq、参考基因组保存格式Fasta、比对文件SAM&#xff0c;以及突变列表VCF&#xff0c;它们都是文本文件。熟练地进行文本文件的处理&#xff0c;对…

facebook分享-错误记录

无法拉起分享 "code":30000,"msg":"fail:API_ERROR: API_ERROR" 1.确认facebook的app_id是否一致 2.确认是否在app_id应用的白名单里&#xff0c;注册meta开发者&#xff0c;然后把主页的user_id给管理员加 A ContentProvider for this app was…

响应式生活常识新闻博客资讯网站模板源码带后台

模板信息&#xff1a; 模板编号&#xff1a;30483 模板编码&#xff1a;UTF8 模板分类&#xff1a;博客、文章、资讯、其他 适合行业&#xff1a;博客类企业 模板介绍&#xff1a; 本模板自带eyoucms内核&#xff0c;无需再下载eyou系统&#xff0c;原创设计、手工书写DIVCSS&a…

Pandas - 数据转换

数据转换一班包括一列数据转换为多列数据&#xff0c;行列转换&#xff0c;DataFrame转换为字典、DataFrame转换为列表和DataFrame转换为元组等。 1.一列数据转换为多列数据 如原始地址数据为&#xff1a;“广东省 深圳市 罗湖区 xxxx”&#xff0c; 此时如果我们需要按照省来…

解密网易数帆DataOps“三剑客”:从数据开发治理、指标中台到ChatBI

大数据产业创新服务媒体 ——聚焦数据 改变商业 近日&#xff0c;以“数智聚力&#xff0c;共赴新程”为主题的2023网易数字大会在杭州召开。在这次大会上&#xff0c;数据猿采访了网易副总裁、网易数帆总经理汪源&#xff0c;网易数帆大数据产品线总经理余利华&#xff0c;对…

centos7部署Canal与Canal集成使用

1、简介 canal [kə’nl]&#xff0c;译意为水道/管道/沟渠&#xff0c;主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据订阅和消费 早期阿里巴巴因为杭州和美国双机房部署&#xff0c;存在跨机房同步的业务需求&#xff0c;实现方式主要是基于业务 trigge…

项目实战:中央控制器实现(2)-优化Controller,将共性动作抽取到中央控制器

1、FruitController FruitController已经和Web没有关系了&#xff0c;和Web容器解耦&#xff0c;可以脱离Web容器做单元测试 package com.csdn.fruit.controller; import com.csdn.fruit.dto.PageInfo; import com.csdn.fruit.dto.PageQueryParam; import com.csdn.fruit.dto.R…

Linux C基础(7)

1、二维数组 1.1 概念 本质&#xff1a;元素为一堆数组的数组&#xff08;数组的数组&#xff09;数组的特点&#xff1a;&#xff08;1&#xff09;数据类型相同 &#xff08;2&#xff09;地址连续 1.2 定义 数组&#xff1a;存储类型 数据类型 数组名[元素个数]二维数组&…

TinyEngine 开源低代码引擎首次直播答疑QA合集

前言 10月27日晚8点&#xff0c;OpenTiny 社区开启了 TinyEngine 开源低代码引擎首次答疑直播&#xff0c;本次直播我们通过收集开发者诉求&#xff0c;精心策划和组织了内容&#xff0c;希望提供给大家最明确和清晰的答疑方式。这是 TinyEngine 低代码引擎直播计划的开端&…

什么是数字化管理?产业园区如何进行数字化管理?

工业园区的数字化管理涉及利用技术和数据驱动的工具来优化工业园区环境中的运营、提高效率并改进决策流程。它通常包括使用各种数字技术和数据分析技术来监视、控制和增强公园运营的各个方面。 以下是工业园区数字化管理的一些关键方面以及如何实施&#xff1a; 1.数据收集和…

vue3怎么获取el-form的元素节点

在元素中使用ref设置名称 在ts中通过从element-plus引入formInstance,设置formRef同名名称字段来获取el-form节点

酷开科技持续推动智能投影行业创新发展

近年来&#xff0c;投影仪逐渐成为年轻人追捧的家居时尚单品。据国际数据公司&#xff08;IDC&#xff09;报告显示&#xff0c;2022年中国投影机市场总出货量505万台&#xff0c;超80%为家用投影仪。相比于电视&#xff0c;投影仪外观小巧、屏幕大小可调节&#xff0c;无论是卧…