什么是浅拷贝和深拷贝

目录

一、概念

浅拷贝(Shallow Copy)

深拷贝(Deep Copy)

二、Java中使用工具来帮助进行对象的拷贝

⭐三、bean工具类

总结


一、概念

当涉及到对象复制时,浅拷贝和深拷贝是两个重要的概念。它们描述了在复制对象时如何处理对象内部的引用关系

浅拷贝(Shallow Copy)

浅拷贝是指在复制对象时,只复制对象本身以及其内部的基本数据类型字段,而不复制对象内部的引用类型字段所引用的对象。换句话说,对于引用类型的字段,浅拷贝只复制了引用,而没有复制引用所指向的实际对象。

在这种拷贝中,只复制了原始对象的引用,而不是对象的实际内容。换句话说,原始对象和浅拷贝对象将共享相同的内存地址,因此对浅拷贝对象所做的任何更改也会反映在原始对象上。这通常适用于基本数据类型(如整数、浮点数等)以及引用类型(如字符串、数组、类对象等)。

举个例子,假设有一个包含引用类型字段的对象 A,进行浅拷贝后得到对象 B。如果修改对象 B 内部引用类型字段所指向的对象,这个修改也会影响到对象 A 内部相应的字段所引用的对象,因为它们实际上引用的是同一个对象。

深拷贝(Deep Copy)

深拷贝是指在复制对象时,不仅复制对象本身和其内部的基本数据类型字段,还递归地复制对象内部所有引用类型字段所引用的对象,从而创建全新的对象结构。深拷贝可以保证复制后的对象与原对象完全独立,对复制对象的修改不会影响原对象,反之亦然。这意味着原始对象和深拷贝对象将拥有独立的内存地址,对深拷贝对象所做的更改不会影响原始对象。深拷贝通常适用于包含复杂内部结构的对象(如包含嵌套对象的类)。

使用上面的例子,如果对对象 B 进行深拷贝,修改对象 B 内部引用类型字段所指向的对象不会影响对象 A 内部相应的字段所引用的对象,因为它们引用的是两个不同的对象。

以下是一个简单的示例代码来演示浅拷贝和深拷贝的区别:

import java.util.ArrayList;
import java.util.List;public class DeepCopyExample {public static void main(String[] args) {// 创建一个原始的 List 对象,包含三个字符串元素List<String> originalList = new ArrayList<>();originalList.add("元素 1");originalList.add("元素 2");originalList.add("元素 3");// 执行浅拷贝List<String> shallowCopy = shallowCopy(originalList);// 执行深拷贝List<String> deepCopy = deepCopy(originalList);// 更改原始列表中的元素originalList.set(0, "新元素 1");// 打印原始列表、浅拷贝列表和深拷贝列表的内容System.out.println("原始列表:" + originalList);System.out.println("浅拷贝列表:" + shallowCopy);System.out.println("深拷贝列表:" + deepCopy);}// 执行浅拷贝的方法public static <T> List<T> shallowCopy(List<T> listToCopy) {List<T> newList = new ArrayList<>();for (T item : listToCopy) {newList.add(item);}return newList;}// 执行深拷贝的方法public static <T> List<T> deepCopy(List<T> listToCopy) {List<T> newList = new ArrayList<>();for (T item : listToCopy) {// 如果元素是一个复杂对象,需要递归执行深拷贝if (item instanceof Cloneable) {try {T clonedItem = (T) item.getClass().getMethod("clone").invoke(item);newList.add(clonedItem);} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {e.printStackTrace();}} else {// 如果元素是一个基本类型或不可拷贝的对象,直接添加到新列表中newList.add(item);}}return newList;}
}

在这个示例中,我们创建了一个包含三个字符串元素的List对象originalList。然后,我们执行了浅拷贝和深拷贝操作,分别将originalList复制到shallowCopydeepCopy变量中。接着,我们更改了原始列表中的第一个元素,并打印出原始列表、浅拷贝列表和深拷贝列表的内容。

可以看到,浅拷贝列表和原始列表共享相同的内存地址,因此对原始列表的更改也会反映在浅拷贝列表中。而深拷贝列表则拥有独立的内存地址,对原始列表的更改不会影响深拷贝列表。

请注意,深拷贝对于包含复杂内部结构的对象(如包含嵌套对象的类)可能会比较复杂,因为需要递归地执行拷贝操作。此外,如果对象的类没有实现Cloneable接口,或者其clone()方法被覆盖并且没有正确实现拷贝逻辑,那么深拷贝可能无法正常工作。在这种情况下,你可能需要手动实现拷贝逻辑,或者使用其他方法来复制对象。

二、Java中使用工具来帮助进行对象的拷贝

以下是一些常见的工具和方法:

1. clone()方法:Cloneable接口中的clone()方法可以用来拷贝对象。如果一个类实现了Cloneable接口并正确重写了clone()方法,那么可以使用clone()方法来创建对象的副本。示例如下:

public class MyClass implements Cloneable {// 定义字段private int field;// 构造函数public MyClass(int field) {this.field = field;}@Overridepublic Object clone() {try {MyClass clonedObject = (MyClass) super.clone();return clonedObject;} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}public static void main(String[] args) {MyClass obj1 = new MyClass(10);MyClass obj2 = obj1.clone();System.out.println(obj1 == obj2);  // false,输出为 false 表示 obj1 和 obj2 是不同的对象System.out.println(obj1.field == obj2.field);  // true,输出为 true 表示 obj1 和 obj2 的 field 字段值相同}
}

上述示例中,MyClass类实现了Cloneable接口,并正确重写了clone()方法。通过调用clone()方法,可以创建MyClass对象的副本。

2. ObjectInputStreamObjectOutputStream类:这些类位于java.io包中,可以用于将对象序列化到流中,然后再从流中反序列化回对象。示例如下:

import java.io.*;public class DeepCopyExample {public static void main(String[] args) {// 创建一个原始的 MyClass 对象MyClass obj1 = new MyClass(10);// 将 obj1 对象序列化到流中try (ObjectOutputStream objOutputStream = new ObjectOutputStream(new FileOutputStream("object.ser"))) {objOutputStream.writeObject(obj1);} catch (IOException e) {e.printStackTrace();}// 从流中反序列化回对象try (ObjectInputStream objInputStream = new ObjectInputStream(new FileInputStream("object.ser"))) {MyClass obj2 = (MyClass) objInputStream.readObject();} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}System.out.println(obj1 == obj2);  // false,输出为 false 表示 obj1 和 obj2 是不同的对象System.out.println(obj1.field == obj2.field);  // true,输出为 true 表示 obj1 和 obj2 的 field 字段值相同}
}class MyClass implements Serializable {// 定义字段private int field;// 构造函数public MyClass(int field) {this.field = field;}
}

上述示例中,MyClass类实现了Serializable接口。通过使用ObjectOutputStreamObjectInputStream,可以将MyClass对象序列化到文件中,然后再从文件中反序列化回对象。这也实现了对象的拷贝。

需要注意的是,对象的拷贝涉及到对象的状态和内容的复制。在进行拷贝时,需要确保拷贝的对象具有相同的状态和内容。以上介绍的方法适用于大部分情况下的对象拷贝,但在某些特殊情况下,可能需要根据具体需求进行更复杂的拷贝操作。

⭐三、bean工具类

在开发中可以自己封装一个通用的工具方法,用于将一个对象的属性复制到另一个对象中。以下参考b站up主三更草堂博客项目中封装的工具类,如下:

public class BeanCopyUtils {private BeanCopyUtils() {}public static <V> V copyBean(Object source, Class<V> clazz) {//创建目标对象V result = null;try {result = clazz.newInstance();//实现属性copycopyProperties(source, result);} catch (Exception e) {e.printStackTrace();}//返回结果return result;}public static <O, V> List<V> copyBeanList(List<O> list, Class<V> clazz) {return list.stream().map(o -> copyBean(o, clazz)).collect(Collectors.toList());}
}

其中包含两个方法:

  1. copyBean 方法接收一个源对象和目标对象的类作为参数,通过反射创建目标对象,并将源对象的属性复制到目标对象中。
  2. copyBeanList 方法接收一个源对象列表和目标对象的类作为参数,使用 Java 8 的 Stream API 对列表中的每个对象调用 copyBean 方法,最终返回复制后的对象列表。

这种方式可以减少重复的属性复制代码,提高代码的复用性和可维护性。需要注意的是,该方法在处理属性复制时使用的是浅拷贝,如果需要深拷贝属性,可能需要额外的处理。

总结

  • 浅拷贝只复制对象本身和基本数据类型字段,引用类型字段仍然指向相同的对象。
  • 深拷贝除了复制对象本身和基本数据类型字段外,还递归复制所有引用类型字段所引用的对象,创建全新的对象结构。

浅拷贝只复制对象的引用,而深拷贝则递归复制了所有的值。选择使用哪种方式需要根据具体的需求,浅拷贝更高效但有引用相同的风险,深拷贝对于防止修改原对象很有用,但是相对来说更慢且更耗费内存。

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

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

相关文章

Docker部署ChatGLM3、One API、FastGPT

创建并运行chatglm3容器 docker run --name chatglm3 -p 8000:8000 registry.cn-hangzhou.aliyuncs.com/ryyan/chatglm.cpp:chatglm3-q5_1 创建并运行one-api容器 (其中挂载路径 D:\one-api 可以选择你自己喜欢的目录) docker run --name oneapi -d -p 3000:3000 -e TZAsia…

Linux下的多线程编程:原理、工具及应用(2)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;Flower of Life—陽花 0:34━━━━━━️&#x1f49f;──────── 4:46 &#x1f504; ◀️ ⏸ ▶️ ☰ …

第二十四节 Java 异常处理

什么是异常&#xff1f; 程序运行时&#xff0c;发生的不被期望的事件&#xff0c;它阻止了程序按照程序员的预期正常执行&#xff0c;这就是异常。异常发生时&#xff0c;是任程序自生自灭&#xff0c;立刻退出终止&#xff0c;还是输出错误给用户&#xff1f;或者用C语言风格…

中国电子学会(CEIT)2020年12月真题C语言软件编程等级考试三级(含详细解析答案)

中国电子学会&#xff08;CEIT&#xff09;考评中心历届真题&#xff08;含解析答案&#xff09; C语言软件编程等级考试三级 2020年12月 编程题五道 总分:100分一、完美立方&#xff08;20分&#xff09; 形如1、完美立方 形如a^3 b^3 c^3 d^3的等式被称为完美立方等式…

Machine Vision Technology:Lecture3 Edge detection | Fitting

Machine Vision Technology&#xff1a;Lecture3 Edge detection | Fitting Finite difference filters有限差分滤波器Effects of noise噪声对边缘检测影响Derivative theorem of convolution卷积的导数定理Derivative of Gaussian filter高斯滤波器的导数Smoothing vs. deriva…

2、设计模式之单例模式详解(Singleton)

单例模式详解 一、什么是单例模式 单例模式是Java中最简单的设计模式之一。这种类型的设计模式属于创建者模式&#xff0c;它提供了一种访问对象的最佳方式。 这种设计模式涉及到一个单一的类&#xff0c;该类负责创建自己的对象&#xff0c;同时确保只有单个对象被创建。这个…

后端系统开发之——创建SpringBoot工程

原文地址&#xff1a;后端框架系统开发之——创建SpringBoot工程 - Pleasure的博客 下面是正文内容&#xff1a; 前言 现在的市场环境&#xff0c;如果你单单只是作为前端工程师或者是后端工程师&#xff0c;在开发Web应用的时候都需要去读取企业提供的接口文档。而当你前后端…

【ARM】UBL本地服务器离线激活license

【更多软件使用问题请点击亿道电子官方网站查询】 1、 文档目标 UBL本地服务器离线激活license。 2、 问题场景 解决有用户外出时激活 license。 3、软硬件环境 1&#xff09;、软件版本&#xff1a;MDK5.39 2&#xff09;、电脑环境&#xff1a;Ubuntu 20.04 LTS 3&…

NCV4275CDT50RKG稳压器芯片中文资料规格书PDF数据手册引脚图图片价格功能

产品概述&#xff1a; NCV4275C 是一款低漏稳压器&#xff0c;可用于严酷汽车环境。它包括了较宽的运行温度范围和输出电压范围。输出调节为 5.0 V 或 3.3 V&#xff0c;额定输出电流为 450 mA。它还提供过电流保护、超温保护和可编程微处理器重置等多种功能。NCV4275C 采用 D…

linux ffmpeg编译

下载源码 https://ffmpeg.org/ csdn下载源码包 不想编译可以直接下载使用静态版本 https://ffmpeg.org/download.html https://johnvansickle.com/ffmpeg/ 根据cpu类型&#xff0c;下载解压后就可以直接使用了。 linux编译 安装底层依赖 yum install gcc yum isntall …

机器学习模型—CatBoost

机器学习模型—CatBoost 作为俄罗斯科技公司Yandex推出的开源机器学习库,CatBoost可以说是当前Gradient Boosting算法发展的新里程碑。相较于广为人知的XGBoost,CatBoost在处理类别特征、纵向样本采样和有序训练数据方面做出了创新性的改进,展现了卓越的性能。 我们经常遇到包…

接口测试和功能测试有什么区别

本文主要分为两个部分&#xff1a; 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者之前的区别与联系。但该部分只交代了怎么做和如何做&#xff1f;并没有解释为什么要做&#xff1f; 第二部分&#xff1…

vue3项目随笔1

1,Eslint Prettier 报错情况&#xff1a; 解决办法&#xff1a; &#xff08;1&#xff09;下载Prettier - code formatter &#xff08;2&#xff09;配置setting.json文件 文件 -> 首选项 -> 设置 -> 用户 -> Eslint "editor.defaultFormatter":…

浅易理解:非极大抑制NMS

什么是非极大抑制NMS 非极大值抑制&#xff08;Non-Maximum Suppression&#xff0c;简称NMS&#xff09;是一种在计算机视觉和图像处理领域中广泛使用的后处理技术&#xff0c;特别是在目标检测任务中。它的主要目的是解决目标检测过程中出现的重复检测问题&#xff0c;即对于…

nuxtjs 如何通过ecosystem.config.js配置pm2?

在 Nuxt.js 项目中&#xff0c;您可以通过 ecosystem.config.js 文件来配置 PM2&#xff0c;以便使用 PM2 来管理 Nuxt.js 应用的进程。ecosystem.config.js 是一个特殊的配置文件&#xff0c;它允许您定义应用的各种属性&#xff0c;如脚本路径、环境变量、日志设置等。 下面…

mysql笔记:14. 权限管理

文章目录 MySQL权限授予权限查看权限撤销权限权限生效机制访问控制的实现 在实际生产中&#xff0c;为了保证数据的安全&#xff0c;数据库管理人员需要为不同的操作人员分配不同的权限&#xff0c;限制登录MySQL服务器的用户只能在其权限范围内操作。同时管理员还可以根据不同…

金三银四,风控建模面试高频问题大全

随着春天的到来,招聘市场的“金三银四”也悄然而至。公众号的小伙伴问我有没有面试相关指导课程,上完课后,把整理的部分材料通过文章分享给更多有需要的朋友。预祝大家顺利获得心仪的职位。 文章目录 一、建模经验高频面试问题1.简单描述风控建模流程:2.请谈谈在过去风控建…

药用植物与生药学试卷

1【单选题】以下不属于植物特有的细胞器是 C A、细胞壁 B、叶绿体 C、高尔基体 D、液泡 2【单选题】四强雄蕊的雄蕊数目是 A A、6个 B、4 C、2个 D、8个 3【单选题】根类药材采收时期通常是 A A、秋季至次年早春植株开始生长时期 B、开花前或果实成熟前 C、花开放…

【物联网】Modbus 协议简介

Modbus 协议简介 QingHub设计器在设计物联网数据采集时不可避免的需要针对Modbus协议的设备做相关数据采集&#xff0c;这里就我们的实际项目经验分享Modbus协议 你可以通过QingHub作业直接体验试用&#xff0c;也可以根据手册开发相应的代码块。 qinghub项目已经全面开源。 …

认清趋势的力量!北大请摆摊鹅姨做演讲!阿伟告白失败,AI有资格做僚机?——早读(逆天打工人爬取热门微信文章解读)

趋势的力量&#xff0c;AWSL 引言Python 代码第一篇 人民日报 【夜读】最好的伯乐&#xff0c;是努力的自己第二篇 来啦新闻早班车要闻社会政策 结尾 “识时务者为俊杰&#xff0c;通机变者为英豪” 生活中&#xff0c;我们如骑行者穿越车水马龙 察觉人潮车流的趋势 不论体能优…