Java当中的深拷贝和浅拷贝

文章目录

  • 一、前提
  • 二、浅拷贝
    • 1. BeanUtils实现浅拷贝
  • 三、深拷贝
    • 1. 实现Cloneable接口并重写clone()方法:
    • 2. 使用序列化与反序列化:

一、前提

在计算机的内存中,每个对象都被赋予一个地址,该地址指向对象在内存中存储的位置。当我们使用一个变量来引用一个对象时,实际上是将该对象的地址赋值给该变量。因此,当我们将一个对象复制到另一个变量中时,实际上是将对象的地址复制给了这个变量。

Java当中的深拷贝和浅拷贝

二、浅拷贝

浅拷贝是一种复制对象到另一个变量的操作,它仅复制对象的地址而非对象本身。换句话说,原始对象和复制对象实际上共享同一个内存地址。因此,若我们修改复制对象中的属性或元素,原始对象中对应的属性或元素也会随之改变。

1. BeanUtils实现浅拷贝

🎏下面是一个使用BeanUtils实现深拷贝不生效的示例:🎏

首先,我们定义两个类,分别是Person和Address:

public class Person {private String name;private int age;private Address address;// getters and setters
}public class Address {private String city;private String street;// getters and setters
}

然后,我们创建一个Person对象,并设置其属性:

Person person1 = new Person();
person1.setName("John");
person1.setAge(25);Address address1 = new Address();
address1.setCity("New York");
address1.setStreet("123 Main St");person1.setAddress(address1);

接下来,我们使用BeanUtils进行深拷贝:

Person person2 = new Person();try {BeanUtils.copyProperties(person2, person1);
} catch (IllegalAccessException | InvocationTargetException e) {e.printStackTrace();
}

现在,我们修改person1的属性值,观察person2的属性值是否发生变化:

person1.setName("Mike");
person1.getAddress().setCity("Los Angeles");

最后,我们打印person1和person2的属性值,检查深拷贝是否生效:

System.out.println(person1.getName()); // Output: Mike
System.out.println(person1.getAddress().getCity()); // Output: Los AngelesSystem.out.println(person2.getName()); // Output: Mike
System.out.println(person2.getAddress().getCity()); // Output: Los Angeles

从输出结果可以看出,尽管我们使用BeanUtils进行了拷贝,但person1和person2的属性值仍然是相同的,修改其中一个对象的属性值会同时影响另一个对象的属性值。这说明使用BeanUtils进行拷贝时,并没有进行深拷贝,而是进行了浅拷贝。

三、深拷贝

深拷贝是一种复制对象及其所有子对象到另一个变量的操作。它会创建一个全新的对象,并将原始对象中的所有属性或元素都复制到新的对象中。因此,若我们修改复制对象中的属性或元素,原始对象中对应的属性或元素不会受到任何影响。

在Java中实现深拷贝有以下几种方法:

1. 实现Cloneable接口并重写clone()方法:

✨在需要进行深拷贝的类中,实现Cloneable接口并重写clone()方法,在clone()方法中对引用类型进行逐个拷贝。注意,被拷贝的引用类型也需要实现Cloneable接口并重写clone()方法。

下面是一个使用Cloneable接口实现深拷贝的示例代码:

class Person implements Cloneable {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}public String getName() {return name;}public int getAge() {return age;}public void setName(String name) {this.name = name;}public void setAge(int age) {this.age = age;}
}public class DeepCopyDemo {public static void main(String[] args) {Person person1 = new Person("John", 25);try {// 使用clone()方法进行深拷贝Person person2 = (Person) person1.clone();System.out.println("person1: " + person1.getName() + ", " + person1.getAge());System.out.println("person2: " + person2.getName() + ", " + person2.getAge());person2.setName("Tom");person2.setAge(30);System.out.println("After modifying person2:");System.out.println("person1: " + person1.getName() + ", " + person1.getAge());System.out.println("person2: " + person2.getName() + ", " + person2.getAge());} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}

输出结果:

person1: John, 25
person2: John, 25
After modifying person2:
person1: John, 25
person2: Tom, 30

从输出结果可以看出,person2是通过深拷贝得到的新对象,修改person2的属性不会影响到person1。这说明使用Cloneable接口和clone()方法可以实现深拷贝。需要注意的是,被拷贝的类及其引用类型属性都需要实现Cloneable接口并重写clone()方法。

2. 使用序列化与反序列化:

🎯将对象序列化为字节流,然后再反序列化为新的对象。这种方法需要被拷贝的类实现Serializable接口。🎯

下面是一个使用序列化与反序列化实现深拷贝的示例代码:

import java.io.*;class Person implements Serializable {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}public void setName(String name) {this.name = name;}public void setAge(int age) {this.age = age;}
}public class DeepCopyDemo {public static void main(String[] args) {Person person1 = new Person("John", 25);try {// 序列化ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(person1);// 反序列化ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);Person person2 = (Person) ois.readObject();System.out.println("person1: " + person1.getName() + ", " + person1.getAge());System.out.println("person2: " + person2.getName() + ", " + person2.getAge());person2.setName("Tom");person2.setAge(30);System.out.println("After modifying person2:");System.out.println("person1: " + person1.getName() + ", " + person1.getAge());System.out.println("person2: " + person2.getName() + ", " + person2.getAge());} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}

输出结果:

person1: John, 25
person2: John, 25
After modifying person2:
person1: John, 25
person2: Tom, 30

从输出结果可以看出,person2是通过反序列化得到的新对象,修改person2的属性不会影响到person1。这说明使用序列化与反序列化可以实现深拷贝。需要注意的是,被拷贝的类及其引用类型属性都需要实现Serializable接口。

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

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

相关文章

【MATLAB第59期】基于MATLAB的混沌退火粒子群CSAPSO-BP、SAPSO-BP、PSO-BP优化BP神经网络非线性函数拟合预测/回归预测对比

【MATLAB第59期】基于MATLAB的混沌退火粒子群CSAPSO-BP、SAPSO-BP、PSO-BP优化BP神经网络非线性函数拟合预测/回归预测对比 注意事项 不同版本matlab 不同电脑 加上数据集随机,BP权值阈值随机,进化算法种群随机,所以运行结果不一定和我运行…

集成了32位Cortex®M0内核XMC1302T038X0200AB、XMC1302Q040X0200AB 32MHz 200KB 闪存 工业MCU

XMC1000 32位工业 MCU 将 ARM Cortex™-M0 核心与领先的 65nm 制造工艺相结合,克服了目前 8 位设计的局限。XMC1000系列让目前的 8 位用户有机会享受 32 位的功耗,同时不在价格或易用性上做出妥协。XMC1000 在其细分市场提供最为广泛的闪存产品线&#x…

3分钟,快速上手Postman接口测试

Postman是一个用于调试HTTP请求的工具,它提供了友好的界面帮助分析、构造HTTP请求,并分析响应数据。实际工作中,开发和测试基本上都有使用Postman来进行接口调试工作。有一些其他流程的工具,也是模仿的Postman的风格进行接口测试工…

下载|GitLab 2023 年 DevSecOps 全球调研报告:安全左移深入人心、AI/ML 蔚然成风

目录 谁应该对应用程序安全负主要责任? 安全实践的最大挑战 AI 驱动研发,提升研发效率 各个角色使用的工具数量是多少? 一体化 DevSecOps 平台有哪些优势? 56%、74%、71%、65%、57% 这些数字和 DevSecOps 结合在一起&#xf…

android adb命令获取处于当前屏幕的Activity

android adb命令获取处于当前屏幕的Activity 使用adb命令: adb shell dumpsys activity activities 输出,例如: ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities) Display #0 (activities from top to bottom): * Task{38ef601 #5281 typ…

Java当中的栈

栈的理解 栈(Stack)是一种受限的线性数据结构,所谓受限是指栈只暴露栈顶和栈底的操作,其底层是由数组实现的。栈的特性是先进后出。 常用方法 注意上面的peek()方法和pop()方法的区别! 实例 import java.util.Stack…

grpc中间件之链路追踪(otel+jaeger)

参考文档 https://github.com/grpc-ecosystem/go-grpc-middleware/blob/main/examples/client/main.go https://github.com/grpc-ecosystem/go-grpc-middleware/blob/main/examples/server/main.go https://github.com/open-telemetry/opentelemetry-go/blob/main/example/jaeg…

sql中group by 的使用

1、概述 Group By 从字面意义上理解就是根据By指定的规则对数据进行分组,所谓的分组就是将一个数据集划分为若干个小区域,然后针对若干个小区域进行数据处理 2、原始表 3、简单的Group By 示例1 select 类别,数量 as 数量之和 from A gro…

android studio 离线打包配置push模块

1.依赖引入 SDK\libs aps-release.aar, aps-unipush-release.aar, gtc.aar, gtsdk-3.2.11.0.aar, 从android studio的sdk中找到对应的包放到HBuilder-Integrate-AS\simpleDemo\libs下面 2.打开build.gradle,在defaultConfig添加manifestPlaceholders节点&#xff0c…

【代码随想录 | Leetcode | 第十天】哈希表 | 三数之和 | 四数之和

前言 欢迎来到小K的Leetcode|代码随想录|专题化专栏,今天将为大家带来哈希法~三数之和 | 四数之和的分享✨ 目录 前言15. 三数之和18. 四数之和总结 15. 三数之和 ✨题目链接点这里 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], num…

JVM运行时区域——对象创建内存分配过程

新创建的对象,都存放在伊甸园区域,当垃圾回收时,将伊甸园区域的垃圾数据销毁,然后将存活的对象转移到幸存者0区域,之后创建的新的对象还是存放在伊甸园区域,等到再次垃圾回收后,将伊甸园区域和幸…

HTML5——基础知识及使用

HTML 文件基本结构 <html><head><title>第一个页面</title></head><body>hello world</body> </html> html 标签是整个 html 文件的根标签(最顶层标签).head 标签中写页面的属性.body 标签中写的是页面上显示的内容.title 标…

Ansible自动化运维学习——综合练习

目录 (一)练习一 1.新建一个role——app 2.创建文件 3.删除之前安装的httpd服务和apache用户 4.准备tasks任务 (1)创建组group.yml (2)创建用户user.yml (3)安装程序yum.yml (4)修改模板httpd.conf.j2 (5)编写templ.yml (6)编写start.yml (7)编写copyfile.yml (8…

Python爬虫技术及其原理详解

概要 随着互联网的发展&#xff0c;大量的数据被存储在网络上&#xff0c;而我们需要从中获取有用的信息。Python作为一种功能强大且易于学习的编程语言&#xff0c;被广泛用于网络爬虫的开发。本文将详细介绍Python爬虫所需的技术及其原理&#xff0c;并提供相关的代码案例。 …

Mac电脑文件夹无权限问题

sudo cp 16.5.zip /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport 走到之前的folder &#xff0c;右键选择get info更改權限, 再應用到所有子文件夹 右下解鎖再加自己Read & Write, -右邊拉下應該可以應用到所有子文件 这样就可以…

ES6——Iterator 和 for...of 循环

Iterator:遍历器 是一接口&#xff0c;为不同的数据结构提供统一的访问机制&#xff0c;只要当前数据结构部署了iterator接口&#xff0c;当前数据结构就可以遍历。 作用&#xff1a;1、为不同的数据结构&#xff0c;提供统一的访问机制 2、使当前数据结构的成员依次被访问 3…

[信号与系统系列] 复指数信号

正弦信号的向量表示 正弦信号由幅值、频率和初相位三个要素确定。由于在线性正弦稳态电路中&#xff0c;各处的电流和电压都是正弦信号&#xff0c;并且它们的角频率与正弦的角频率相同&#xff0c;因此&#xff0c;在进行正弦稳态电路分析时&#xff0c;对于正弦信号的幅值和…

真实节点、虚拟节点与影子节点的区别

真实节点、虚拟节点与影子节点的区别 本文将深入介绍真实节点、虚拟节点与影子节点是如何协同工作共同创建一个高性能的文档对象模型。 DOM&#xff08;Document Object Module文档对象模型&#xff09;正如它所描述的那样。网站的 HTML 树由一个名为document的对象表示。在这…

C语言学习笔记 码云及git使用教程-05

目录 一、码云简介 二、码云注册 1.点击右上角的注册按钮 2.填写相应的注册信息 3.使用账号密码进行登陆 三、创建仓库 1.如图新建 2.定义仓库相应参数 3.初始化readme文件 4.效果 5.开源设置 四、git管理 1.安装git 2.打开桌面上的Git bash 3.进行仓库克隆 4. 在其他盘…

查找和二叉树(基础知识和基本操作)

查找&#xff1a; 1.二分查找&#xff1a;先定一个大范围&#xff0c;想一个数&#xff0c;看是在起始范围到中间范围还是中间范围到结束范围&#xff0c;依次循环直到确定值&#xff08;相当于一直把范围折半&#xff0c;直到找到&#xff09; while(low<high) {int mid(…