【Java进阶篇】深拷贝和浅拷贝的理解(保姆级文献)

在这里插入图片描述

✔️深拷贝和浅拷贝的理解


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


浅拷贝是指将一个对象复制到另一个变量中,但是只复制对象的地址,而不是对象本身。也就是说,原始对象和复制对象实际上是共享同一个内存地址的。因此,如果我们修改了复制对象中的属性或元素,原始对象中对应的属性或元素也会被修改。


读不懂?看图吧,帮助理解。


在这里插入图片描述

在Java中,我们常用的各种BeanUtils基本也都是浅拷贝的。


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


看图帮助理解:


在这里插入图片描述

比如我们有一个User类,然后他的定义如下:


package com.ifbranch.test;/*** @author 昕宝爸爸爱编程* */
public class User {private String name;private String password;private Address address;public User(String name, String password, Address address) {super();this.name = name;this.password = password;this.address = address;}public User() {super();// TODO Auto-generated constructor stub}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}@Overridepublic String toString() {return "User [name=" + name + ", password=" + password + ", address=" + address + "]";}}class Address {private String province;private String city;private String area;public Address() {super();// TODO Auto-generated constructor stub}public Address(String province, String city, String area) {super();this.province = province;this.city = city;this.area = area;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getArea() {return area;}public void setArea(String area) {this.area = area;}@Overridepublic String toString() {return "Address [province=" + province + ", city=" + city + ", area=" + area + "]";}}

当我们基于User类的一个对象user1 拷贝出一个新的对象user2的时候,不管怎么样,user1和user2都是两个不同的对象,他们的地址也不会一样。但是其中的成员变量Address的话可能就因为深浅拷贝的不同而呈现不同的现象喽。


如果是浅拷贝,那么user2中的address会和user1中的address共享同一个地址,当其中一个修改时,另一个也会受影响。

如果是深拷贝,那么user2中的address会和user1中的address并不是同一个地址,当其中一个修改时,另一个是不会受影响的。


✔️拓展知识仓


✔️BeanUtils的浅拷贝


我们先举个实际的例子,来看下为什么前面说BeanUtils.copyProperties的过程是浅拷贝。

先来定义两个类:


package com.ifbranch.test;/*** @author 昕宝爸爸爱编程* */
public class User {private String name;private String password;private Address address;public User(String name, String password, Address address) {super();this.name = name;this.password = password;this.address = address;}public User() {super();// TODO Auto-generated constructor stub}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}@Overridepublic String toString() {return "User [name=" + name + ", password=" + password + ", address=" + address + "]";}}class Address {private String province;private String city;private String area;public Address() {super();// TODO Auto-generated constructor stub}public Address(String province, String city, String area) {super();this.province = province;this.city = city;this.area = area;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getArea() {return area;}public void setArea(String area) {this.area = area;}@Overridepublic String toString() {return "Address [province=" + province + ", city=" + city + ", area=" + area + "]";}}

然后写一段测试代码:


User user = new User("aidemeng""123456qwe");
user.setAddress(new Address( "shanghai","fengxian","jinhairoad"));User newUser = new User();
BeanUtils.copyProperties(user, newUser);System.out.printin(user == newUser);
System.out.println(user.getAddress() == newUser.getAddress());//输出结果为://   false
//   true

即,我们BeanUtils.copyProperties 拷贝出来的newUser是一个新的对象,但是,其中的address对象和原来的user中的address对象是同一个对象。


如果我们修改newUser中的Address对象的值的话,是会同时把user对象中的Address的值也修改了的。可以学着修改下newUser中的address对象:
newUser.getAddress( ).setCity("shanghai") ;
System.out.println(JSON.to]SONString(user));
System.out.println(JSON.to]SONString(newUser));

输出结果为:


{“address”:{“area”:“jinhairoad”,“city”:“shanghai”,“province”:“shanghai”},“name”:“aidemeng”,“password”:“123456qwe”}
{“address”:{“area”:“jinhairoad”,“city”:“shanghai”,“province”:“shanghai”},“name”:“aidemeng”,“password”:“123456qwe”}


✔️实现Cloneable接口,重写clone()


在Object类中定义了一个clone方法,这个方法其实在不重写的情况下,其实也是浅拷贝的。如果想要实现深拷贝,就需要重写clone方法,而想要重写clone方法,就必须实现Cloneable,否则会报CloneNotSupportedException异常。


将上述代码修改下,重写clone方法:


package com.ifbranch.test;/*** @author 昕宝爸爸爱编程* */public class Address implements Cloneable {private String province;private String city;private String area;//省略构造函数和setter/getter@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}//------------------------------------------------------class User implements Cloneable {private String name;private String password;private HomeAddress address;//省略构造函数和setter/getter@Overrideprotected Object clone() throws CloneNotSupportedException {User user = (User)super.clone();user.setAddress((HomeAddress)address .clone());return user;}
}

之后,在执行一下上面的测试代码,就可以发现,这时候newUser中的address对象就是一个新的对象了。


这种方式就能实现深拷贝,但是问题是如果我们在User中有很多个对象,那么clone方法就写的很长,而且如果后面有修改,在User中新增属性,这个地方也要改。


那么,有没有什么办法可以不需要修改,一劳永逸呢?


✔️序列化实现深拷贝


我们可以借助序列化来实现深拷贝。先把对象序列化成流,再从流中反序列化成对象,这样就一定是新的对象了序列化的方式有很多,比如我们可以使用各种JSON工具,把对象序列化成JSON字符串,然后再从字符串中反序列化成对象。

如使用fastjson实现:


User newUser = JSON.parsebject(JSON.toJSONString(user)User.class);

也可实现深拷贝。


除此之外,还可以使用Apache Commons Lang中提供的SerializationUtils工具实现。


我们需要修改下上面的User和Address类,使他们实现Serializable接口,否则是无法进行序列化的。


class User implements Serializableclass Address implements Serializable

然后在需要拷贝的时候:

User newUser = (User) SerializationUtils.clone(user);

同样,也可以实现深拷贝啦~!

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

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

相关文章

一台服务器​最大并发 tcp 连接数多少?65535?

首先,问题中描述的65535个连接指的是客户端连接数的限制。 在tcp应用中,server事先在某个固定端口监听,client主动发起连接,经过三次握手后建立tcp连接。那么对单机,其最大并发tcp连接数是多少呢? 如何标…

Selenium4自动化测试框架

Selenium 介绍 Selenium 是目前用的最广泛的 Web UI 自动化测试框架,核心功能就是可以在多个浏览器上进行自动化测试,支持多种编程语言,目前已经被 google,百度,腾讯等公司广泛使用。 开发步骤 1、配置 google 驱动…

机械革命极光Pro重装Win10系统图解

机械革命极光Pro是性能优秀的笔记本电脑,深受广大用户的喜欢,现在用户想给笔记本电脑重新安装一下操作系统,但不知道重装系统的详细步骤。下面小编将带来机械革命极光Pro笔记本电脑重装系统Win10版本的步骤介绍,帮助更多的用户完成…

Elasticsearch:无需搜索 “Christmas” 即可找到有关圣诞节的书籍

随着假期的临近,我期待着变得舒适,拿起一本新书,享受轻松的时光。 但是使用搜索栏在线发现图书并不像看起来那么容易......大多数零售搜索引擎仅依赖于关键字搜索,当我们确切地知道我们正在寻找什么书名时,这很好&…

Kotlin 类

1、声明 class 关键字声明类 类声明由类名、类头(指定其类型参数、主构造函数等)以及由花括号包围的类体构成;类头与类体都是可选的; 如果一个类没有类体,可以省略花括号 class Person { /*……*/ }// 没有声明任何…

分布式事务是什么

分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在微服务架构中,几乎可以说是无法避免,本文就分布式事务来简单聊一下。 数据库事务 我们先从数据库事务说起。数据库事务可能大家…

【大数据存储与处理】开卷考试总复习笔记

实验部分 一、 HBase 的基本操作 1. HBase Shell入门 # 进入HBase Shell环境 hbase shell2. HBase创建数据库表 # 创建表命令 create user, info1, info2 # 查看所有表 list # 查看表结构 describe user3. HBase数据操作 数据插入 put user, 0001, info1:name, jack put us…

傻瓜式教学Docker 使用docker compose部署 php nginx mysql

首先你可以准备这个三个服务,也可以在docker compose 文件中 直接拉去指定镜像,这里演示的是镜像服务已经在本地安装好了,提供如下: PHP # 设置基础镜像 FROM php:8.2-fpm# install dependencies RUN apt-get update && apt-get install -y \vim \libzip-dev \libpng…

【Redis】Redis 的数据类型

有五种常用数据类型:String、Hash、Set、List、SortedSet。以及三种特殊的数据类型:Bitmap、HyperLogLog、Geospatial ,其中HyperLogLog、Bitmap的底层都是 String 数据类型,Geospatial 的底层是 Sorted Set 数据类型。 五种常用…

智慧医疗平台开发:在线问诊系统源码搭建详解

如今,在线问诊系统的出现为患者提供了更为灵活、便捷的医疗服务。我们在开发系统之前,应该先简单了解系统的功能以及相关的技术。 一、核心功能需求 -用户注册和登录:用户可以通过注册账户,或者使用第三方登录方式进入系统。 …

高速风筒4套硬件案子谁会成为王者----【其利天下技术】

关于高速风筒的硬件电路,从MCU的角度来说,严格意义上是可以区分为四种硬件电路的,当前市场上这四种硬件电路也是并行存在的。所以有朋友电话问我,我怎么看待这四种硬件方案的优劣势和未来的?今天特意根据个人视角&…

Hive01_安装部署

Hive的安装 上传安装包 解压 tar zxvf apache-hive-3.1.2-bin.tar.gz mv apache-hive-3.1.2-bin hive解决Hive与Hadoop之间guava版本差异 cd /export/software/hive/ rm -rf lib/guava-19.0.jarcp cp /export/software/hadoop/hadoop-3.3.0/share/hadoop/common/lib/guava-27.0…

基层医疗卫生信息可视化:Flask、MySQL、Echarts的综合利用

基层医疗卫生信息可视化:Flask、MySQL、Echarts的综合利用 正文: 社区卫生健康数据可视化是提升医疗服务水平和促进健康管理的有效手段。本文将介绍一种基于Flask、MySQL和Echarts技术的社区卫生健康数据可视化系统,数据源来自市基层医疗卫生…

Apache RocketMQ,构建云原生统一消息引擎

本文整理于 2023 年云栖大会林清山带来的主题演讲《Apache RocketMQ 云原生统一消息引擎》 演讲嘉宾: 林清山(花名:隆基),Apache RocketMQ 联合创始人,阿里云资深技术专家,阿里云消息产品线负…

rocky linux9 安装go 即接下去

首先,更新系统的软件包索引以获取最新的软件包信息: sudo dnf update使用以下命令安装 Go 语言: sudo dnf install golang安装完成后,你可以通过以下命令验证 Go 语言是否安装成功: go version4、用相对路径初始化g…

适配器模式(Adapter)

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口之间的协作。该模式涉及一个适配器,用于在已存在的接口和需要使用的接口之间进行适配。在Java中,适配器模式主要有两种实现方式:类适配器和对象适配器。 类适配器模式 在类适配器模式中,适配器通过继…

智能优化算法应用:基于孔雀算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于孔雀算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于孔雀算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.孔雀算法4.实验参数设定5.算法结果6.参考文献7.MA…

Go语言学习一

Go语言的发展历史 Go语言的三个作者分别是:罗伯特格利茨默(Robert Griesemer)、罗伯派克 (Rob Pike) 和 肯汤普森(Ken Thompson)。 Robert Griesemer是Google V8、Chubby和HotSpot JVM的主要贡献…

Crowd Counting近期研究(附代码资源)

1.Semi-Supervised Crowd Counting with Contextual Modeling: Facilitating Holistic Understanding of Crowd Scenes paper:https://arxiv.org/abs/2310.10352 code:https://github.com/cha15yq/MRC-Crowd 摘要: 为了减轻训练可靠的人群计数模型所需的繁重标注…

二分查找——OJ题(二)

📘北尘_:个人主页 🌎个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上,不忘来时的初心 文章目录 一、点名1、题目讲解2、算法原理3、代码实现 二、搜索旋转排序数组中的最⼩值1、题目讲解2、算…