“深入探讨Java中的对象拷贝:浅拷贝与深拷贝的差异与应用“

        前言:在Java编程中,深拷贝(Deep Copy)与浅拷贝(Shallow Copy)是两个非常重要的概念。它们涉及到对象在内存中的复制方式,对于理解对象的引用、内存管理以及数据安全都至关重要。


✨✨✨这里是秋刀鱼不做梦的BLOG

✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客

先让我们看一下本文的大致内容:

目录

1.深拷贝与浅拷贝的概念

(1)浅拷贝

(2)深拷贝

2.浅拷贝的实现

3.深拷贝的实现

4.深浅拷贝的作用

浅拷贝的作用:

深拷贝的作用:


1.深拷贝与浅拷贝的概念

        ——在了解Java中是如何实现对象的深浅拷贝之前,我们需要先了解一下什么是深拷贝、浅拷贝:

(1)浅拷贝

        在浅拷贝中,只复制对象本身,而不复制对象引用的内容。这意味着,如果对象中包含了引用类型的成员变量,那么这些成员变量的引用将会被复制,但是它们仍然指向相同的内存地址。因此,对于引用类型成员变量的修改会影响到原始对象和拷贝对象。

(2)深拷贝

        与浅拷贝不同,深拷贝会递归地复制对象及其所有引用的对象,直到所有对象都被复制到一个新的内存地址上。这样,原始对象和拷贝对象完全独立,彼此的修改不会相互影响。

        嗯嗯嗯......感觉看了和没看没什么区别,还是不太能理解到底什么是Java中的深浅拷贝,那么我们使用一个生活中的案例来解释一下:

浅拷贝的情景:

        ——如果你选择了浅拷贝,那么你会简单地把整个礼物篮进行复制,然后送给你的朋友。在这种情况下,你的朋友会得到一个看起来一模一样的礼物篮。然而,当你的朋友拆开礼物篮,他们发现里面的食品和饰品并没有改变,他们是和你的礼物篮里的相同的食品和饰品。

深拷贝的情景:

        ——相比之下,如果你选择了深拷贝,那么你会仔细地把礼物篮里的每一样东西都复制一份,然后把这些复制品装进一个新的礼物篮里,送给你的朋友。在这种情况下,你的朋友得到的是一个全新的礼物篮,里面的食品和饰品和你的礼物篮里的完全一样。但是,现在他们拥有的是独立于你的礼物篮的新的食品和饰品。

不知道上面的生活案例有没有使你更好的理解Java中的深浅拷贝,如果还是没有,那么直接往下看即可!

大致的了解了什么是Java中的深浅拷贝之后,那么我们又该如何使用代码去实现它们呢?

2.浅拷贝的实现

        在Java中,实现浅拷贝通常使用clone()方法。该方法会创建一个新对象,并将原始对象的所有字段值复制到新对象中。但是需要注意的是,对于引用类型的成员变量,仍然是浅拷贝,即复制的是引用而不是对象本身。

        下面是在Java中实现浅拷贝的详细步骤:

1.实现Cloneable接口:

class MyClass implements Cloneable {// 类的定义
}

 2.重写clone()方法并调用super.clone()

class MyClass implements Cloneable {// 类的定义@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}

3.在使用时捕获CloneNotSupportedException异常:

try {MyClass copy = (MyClass) original.clone();
} catch (CloneNotSupportedException e) {e.printStackTrace();
}

4.强制转换:由于clone()方法返回的是Object类型,因此在使用时需要进行类型转换。

try {MyClass copy = (MyClass) original.clone();
} catch (CloneNotSupportedException e) {e.printStackTrace();
}

       

         这就是实现Java中浅拷贝的四步实现流程,相信你仔细的读完上边的代码之后,对Java中浅拷贝的实现流程已经有了初步的理解了,现在让我们使用一个完整的案例,来实现一下Java中的浅拷贝:    

class Person implements Cloneable {private String name;private Address address;public Person(String name, Address address) {this.name = name;this.address = address;}// Getter and setter 方法@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}class Address {private String city;public Address(String city) {this.city = city;}// Getter and setter 方法
}public class ShallowCopyExample {public static void main(String[] args) {Address address = new Address("New York");Person person1 = new Person("Alice", address);try {Person person2 = (Person) person1.clone();// 输出: trueSystem.out.println(person1.getAddress() == person2.getAddress()); } catch (CloneNotSupportedException e) {e.printStackTrace();}}
}

以上代码演示了一个浅拷贝的例子。让我来解释一下它的执行过程和输出:

  1. 首先,我们定义了两个类:PersonAddressPerson类有一个name属性和一个address属性,而Address类只有一个city属性。

  2. ShallowCopyExample类的main方法中,我们创建了一个Address对象,表示Alice的地址是"New York"。然后,我们创建了一个Person对象person1,传入了名字"Alice"和上面创建的Address对象。

  3. 接着,我们调用person1.clone()方法进行浅拷贝。由于Person类实现了Cloneable接口并重写了clone()方法,因此它支持克隆操作。在clone()方法内部,我们调用了super.clone()来复制Person对象本身,但是对于address属性,只是复制了其引用,而没有对Address对象进行深度复制。

  4. 输出语句System.out.println(person1.getAddress() == person2.getAddress());比较了person1person2address属性是否是同一个对象。由于浅拷贝只是复制了引用,所以person1person2address属性指向的是同一个Address对象,因此输出结果为true

这样我们就大致的了解了在Java中如何去实现对象的浅拷贝了。

3.深拷贝的实现

        在Java中实现深拷贝相对于浅拷贝来说更为复杂,因为需要确保对象及其引用的所有对象都被复制到新的内存地址上。

        下面是在Java中实现深拷贝的详细流程:

1.实现Cloneable接口:同样,为了使用clone()方法,需要确保类实现了Cloneable接口。

class MyClass implements Cloneable {// 类的定义
}

2.重写clone()方法:在重写的clone()方法中,除了调用super.clone()来复制对象本身之外,还需要递归地复制所有引用的对象。

class MyClass implements Cloneable {private AnotherClass anotherObject;public MyClass(AnotherClass anotherObject) {this.anotherObject = anotherObject;}// Getter and setter 方法@Overridepublic Object clone() throws CloneNotSupportedException {MyClass clonedObject = (MyClass) super.clone();// 对引用类型的成员变量进行深度复制clonedObject.anotherObject = (AnotherClass) anotherObject.clone();return clonedObject;}
}

3.在引用类型的类中同样实现深拷贝:如果类中有成员变量是引用类型,那么需要在该引用类型的类中同样实现深拷贝。

class AnotherClass implements Cloneable {// 类的定义@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}

4.调用clone()方法:现在可以调用clone()方法来获取深拷贝的对象。

public class DeepCopyExample {public static void main(String[] args) {AnotherClass anotherObject = new AnotherClass();MyClass original = new MyClass(anotherObject);MyClass deepCopy = null;try {deepCopy = (MyClass) original.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}

        这就是实现Java中深拷贝的四步实现流程,当然,现在让我们使用一个完整的案例,来实现一下Java中的深拷贝:    

class Person implements Cloneable {private String name;private Address address;public Person(String name, Address address) {this.name = name;this.address = address;}// Getter and setter 方法@Overrideprotected Object clone() throws CloneNotSupportedException {Person clonedPerson = (Person) super.clone();clonedPerson.address = (Address) this.address.clone();return clonedPerson;}
}class Address implements Cloneable {private String city;public Address(String city) {this.city = city;}// Getter and setter 方法@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}public class DeepCopyExample {public static void main(String[] args) {Address address = new Address("New York");Person person1 = new Person("Alice", address);try {Person person2 = (Person) person1.clone();// 输出: falseSystem.out.println(person1.getAddress() == person2.getAddress()); } catch (CloneNotSupportedException e) {e.printStackTrace();}}
}

让我解释一下代码的主要部分:

  1. Person 类和 Address 类都实现了 Cloneable 接口,这是为了表明它们可以被克隆。

  2. Person 类中,有一个私有字段 address,类型为 AddressPerson 类的构造函数用于初始化这个字段。

  3. Person 类的 clone() 方法首先调用了 super.clone(),这会复制 Person 对象本身。然后,它对 address 字段进行了深拷贝,即创建了一个新的 Address 对象,并将其赋值给 clonedPersonaddress 字段。

  4. Address 类中的 clone() 方法也是调用了 super.clone(),实现了浅拷贝,因为 Address 类只有一个字段,且该字段为不可变类型。

  5. main() 方法中,首先创建了一个 Address 对象和一个 Person 对象。然后,通过调用 clone() 方法,创建了一个新的 Person 对象 person2,其中包含了新的 Address 对象。

  6. 最后,通过比较 person1person2 的地址字段,可以看到它们不相同,这表明在克隆过程中进行了深拷贝。

这样我们就大致的了解了在Java中如何去实现对象的深拷贝了。

4.深浅拷贝的作用

        了解完了Java中的深浅拷贝之后,那么其有什么用呢?

浅拷贝的作用:

  1. 节省内存空间:浅拷贝只复制对象本身,不会复制对象引用的内容,因此在某些情况下可以节省内存空间。

  2. 提高对象创建速度:由于浅拷贝只复制对象本身,因此复制过程相对较快。

  3. 适用于不包含引用类型成员变量的对象:如果对象中的成员变量都是基本数据类型或者不需要被复制的对象,那么浅拷贝是一个简单有效的复制方式。

深拷贝的作用:

  1. 确保对象的独立性:深拷贝会递归地复制对象及其引用的所有对象,从而确保复制后的对象与原始对象完全独立,对复制对象的修改不会影响原始对象。

  2. 数据安全性:在多线程环境下,深拷贝可以确保对象的数据安全性,因为每个线程都可以操作独立的对象,而不会相互影响。

  3. 避免对象共享的副作用:在某些情况下,对象的共享可能会导致意外的副作用,深拷贝可以避免这种情况的发生,保证数据的一致性和可靠性。

  4. 适用于包含引用类型成员变量的对象:如果对象中包含了引用类型的成员变量,并且需要复制所有引用的对象,那么深拷贝是更合适的选择。

        总的来说,浅拷贝适用于简单对象的复制,可以提高性能和节省内存空间,而深拷贝则适用于需要确保对象独立性和数据安全性的情况,尤其是当对象包含引用类型成员变量时。


以上就是本篇文章的全部内容了~~~

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

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

相关文章

springboot undertow 文件上传文件过大异常

io.undertow.server.RequestTooBigException: UT000020 Connection terminated as request was larger than xxxx 修改yaml文件中关于undertow的配置项 server:undertow:# HTTP POST请求最大的大小# 默认0,无限制max-http-post-size: ${SERVER_UNDERTOW_MAX_HTTP_…

小白教程--- kali(po解)WIFI密码 (图文教程)

kali学得好,牢饭少不了!!! 原理: 模拟WiFi的已连接设备,强制让其下线重连,获取其握手包,使用密码字典(宝丽)婆洁。 环境(准备工作)&a…

跨域、JSONP、CORS、Spring、Spring Security解决方案

概述 JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。跨域是浏览器(如Chrome浏览器基于JS V8引擎,可以简单理解为JS解释器)的一种同源安全策略,是浏览器单方面限制脚本的跨域访问。因此,仅有…

【Java面试】十六、并发篇:线程基础

文章目录 1、进程和线程的区别2、并行和并发的区别3、创建线程的四种方式3.1 Runnable和Callable创建线程的区别3.2 线程的run和start 4、线程的所有状态与生命周期5、新建T1、T2、T3,如何保证线程的执行顺序6、notify和notifyAll方法有什么区别7、wait方法和sleep方…

Flutter Image源码分析

本文用于记录分析Imge图片加载流程源码分析学习笔记 切入点是Image.network,加载网络图片 构造方法会创建NetworkImage,加载图片的实现类,父类是ImageProvider 加载本地图片等等都是类似 下面进入_ImageState类 void resolveStreamForKey(ImageConfiguration configurat…

【云原生】基于windows环境搭建Docker

目录 一、Docker Desktop搭建 二、前置准备 2.1开启 Hyper-V 2.2 Hyper-V选项看不到问题解决 2.3 开启或升级wsl 三、安装过程 3.1 下载安装包 3.2 安装 Docker Desktop 3.2.1 Docker 图标一直处于starting状态问题解决 3.3 配置仓库与镜像 3.4 docker功能测试 四、…

C++中的一些困惑(长期更新中)

C中的一些困惑 文章目录 C中的一些困惑1. using std::具体命名与using namespace std;2. 【int \*p[10] 】与 【int (\*p)[10]】3. main()函数可带参,参从何来?4. constexpr函数的返回值可不为常量,那这时constexpr关键字作用是什么&#xff…

CTF Show MISC做题笔记

MISCX 30 题目压缩包为misc2.rar,其中包含三个文件:misc1.zip, flag.txt, hint.txt。其中后两个文件是加密的。 先解压出misc1.zip, 发现其中包含两个文件:misc.png和music.doc。其中后面文件是加密的。 解压出misc.png,发现图片尾部有消息:flag{flag…

一个简单的消息队列

目录 原理 实现代码 示例 原理 消息队列是一个先进先出栈,每次都处理第一项,处理完了过后会删除这个消息,这是一个简单的消息队列图: 实现代码 首先消息队列需要一个队列,我们用Python里的列表: self.…

Shell脚本学习_内置命令

目录 1.内置命令介绍: 2.Shell内置命令:alias设置别名 3.Shell内置命令:echo输出字符串 4.Shell内置命令:read读取控制台输入 5.Shell内置命令:exit退出 6.Shell内置命令:declare设置变量 1.内置命令…

【计算机毕业设计】283基于微信小程序校园订餐

🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板&#xff…

用python编撰一个电脑清理程序

自制一个电脑清理程序,有啥用呢?在电脑不装有清理软件的时候,可以解决自己电脑内存不足的情况。 1、设想需要删除指定文件夹中的临时文件和缓存文件。以下是代码。 import os import shutil def clean_folder(folder_path): for root,…

【备战蓝桥杯】蓝桥杯省一笔记:算法模板笔记(Java)

蓝桥杯 0、快读快写模板1、回文判定2、前缀和3、差分4、二分查找5、快速幂6、判断素数7、gcd&lcm8、进制转换9、位运算10、字符串常用API11、n的所有质因子12、n的质因子个数13、n的约数个数14、n阶乘的约数个数15、n的约数和16、阶乘 & 双阶乘17、自定义升序降序18、动…

Java----抽象类和接口

欢迎大家来这次博客-----抽象类和接口。 1.抽象类 1.1 抽象类概念 在Java中我们都是通过类来描述对象,但反过来并不是所有的类都是用来描述对象的。当一个类中没有足够的信息来描述一个具体对象,我们就将该类称为抽象类。 如上图中的Shape类&#xff…

Wireshark自定义Lua插件

背景: 常见的抓包工具有tcpdump和wireshark,二者可基于网卡进行抓包:tcpdump用于Linux环境抓包,而wireshark用于windows环境。抓包后需借助包分析工具对数据进行解析,将不可读的二进制数转换为可读的数据结构。 wires…

SwiftUI五视图动画和转场

代码下载 使用SwiftUI可以把视图状态的改变转成动画过程,SwiftUI会处理所有复杂的动画细节。在这篇中,会给跟踪用户徒步的图表视图添加动画,使用animation(_:)修改器给一个视图添加动画效果非常容易。 下载起步项目并跟着本篇教程一步步实践…

单元测试覆盖率

什么是单元测试覆盖率 关于其定义,先来看一下维基百科上的一段描述: 代码覆盖(Code coverage)是软件测试中的一种度量,描述程序中源代码被测试的比例和程度,所得比例称为代码覆盖率。 简单来理解&#xff…

【Redis学习笔记05】Jedis客户端(中)

Jedis客户端 1. 命令 1.1 String类型 1.1.1 常见命令 SET命令 语法:SET key value [EX seconds | PX milliseconds] [NX|XX] 说明:将string类型的value值设置到指定key中,如果之前该key存在,则会覆盖原先的值,原先…

短剧看剧系统投流版系统搭建,前端uni-app

目录 前言: 一、短剧看剧系统常规款短剧系统和投流版的区别? 二、后端体系 1.管理端: 2.代理投流端 三、功能区别 总结: 前言: 23年上半年共上新微短剧481部,相较于2022年全年上新的454部&#xff0…

C语言王国——数据的内存管理

目录 一、引言 二、整形在内存中的存储 2.1 进制之间的转换 2.1.1 整形的二进制 2.1.2 十进制和二进制 2.1.3 十进制和八进制的转换 2.1.4 十六进制和十进制的转换 2.2 原码,反码,和补码 三、大、小端字节序 3.1 大小端的定义 3.2 为什么会有大…