当前位置: 首页 > news >正文

Java中的深拷贝和浅拷贝

目录

🍎引出拷贝

🍎浅拷贝

🍎深拷贝

🍎总结


引出拷贝

现在有一个学生类和书包类,在学生类中有引用类型的书包变量:

class SchoolBag {private String brand;   //书包的品牌private int size;   //书包的尺寸//getter、setter略public SchoolBag(String brand, int size) {this.brand = brand;this.size = size;}@Overridepublic String toString() {return "SchoolBag{" +"brand='" + brand + '\'' +", size=" + size +'}';}
}class Student {private String name;private int age;private SchoolBag schoolBag;public Student(String name, int age, SchoolBag schoolBag) {this.name = name;this.age = age;this.schoolBag = schoolBag;}//getter、setter略@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", schoolBag=" + schoolBag +'}';}
}

创建一个学生对象,现在想在内存中拷贝一份和这个学生对象指向的对象相同的对象,那么应该怎么去做呢?

可能我们会像下边这样写:

...
Student stu1 = new Student("张华",18,new SchoolBag("小米",4));
//直接创建一个新的引用指向这个stu1指向的对象
Student stu2 = stu1;

这显然是错误的,这从内存的分布上就可以知道,stu1与stu2虽然是两个不同的引用变量,但是指向的是堆内存中的同一块空间或者说指向的是同一个对象,并没有真正实现克隆的效果。它们两个引用和引用对象在内存中的关系如图

那么要怎么实现对象内存的拷贝呢?


浅拷贝

 我们可以通过使用Java中的clone方法,该clone方法是Object类中定义的,因此我们只需要在自己的类中重写这个方法,就可以实现对象的拷贝。但是clone方法的使用,并不只是单纯的重写就可以实现效果的,下面我们依次来看:

1.待克隆对象所在类重写clone方法

class Student {private String name;private int age;private SchoolBag schoolBag;public Student(String name, int age, SchoolBag schoolBag) {this.name = name;this.age = age;this.schoolBag = schoolBag;}//getter、setter略@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", schoolBag=" + schoolBag +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

调用重写的clone方法,克隆对象,会发现,报错了:

哦😂,原来还得让待克隆对象所在类实现Cloneable接口,但是当我们查看这个接口的内容时,却发现这是一个空接口:

为什么是一个空接口呢?

这里可以记作它是一个标记接口,实现Cloneable接口的类被标记为可以被clone的类。

现在我们让Student类实现Cloneable接口再试试看能不能克隆成功。

2.实现Cloneable接口

class Student implements Cloneable{private String name;private int age;private SchoolBag schoolBag;public Student(String name, int age, SchoolBag schoolBag) {this.name = name;this.age = age;this.schoolBag = schoolBag;}//getter、setter略@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", schoolBag=" + schoolBag +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

现在再次尝试clone,嗯...又报错了......

原来时异常,我们把异常抛给JVM一下:

 啊,还报错...

原来是clone方法的返回类型时Object:

 我们进行向下转型,然后进行克隆输出会发现克隆成功了

 现在克隆出来的对象以及原型在内存中因该是这样分布的:

当我们修改stu1对象中的schoolbag成员变量后再进行对象的输出,会发现stuClone对象的schoolbag中的值也发生了变化,这是为什么?????

 这是因为clone出来的stuClone对象和stu1对象的引用成员变量schoolbag指向的还是同一片内存空间,二者还是共享这一片内存空间。
 

因此我们可以得到何为浅拷贝?

当引用变量指向的对象中还包含引用变量时,如果只对当前的外部的引用变量进行clone,克隆出来对象的基本数据类型都会被正常拷贝;而内部的引用变量只是将引用的值clone给了另一个clone对象的引用,因此无法在真正意义上实现对象的拷贝,下面我们来解决这种clone后仍存在成员变量有内存共享的问题。


深拷贝

既然clone方法能够实现对对象的拷贝,那么我们在引用变量内部的引用变量身上再使用一次clone方法不就可以解决这种浅拷贝问题了吗?

现在我们进行上述操作,让SchoolBag类重写clone方法并且实现标记接口Cloneable:

class SchoolBag implements Cloneable{private String brand;   //书包的品牌private int size;   //书包的尺寸//getter、setter略public SchoolBag(String brand, int size) {this.brand = brand;this.size = size;}@Overridepublic String toString() {return "SchoolBag{" +"brand='" + brand + '\'' +", size=" + size +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

在对Student对象进行克隆是也对其中的引用成员变量进行克隆,我们只需要在Student类的clone方法中这样写就可以了:

class Student {...@Overrideprotected Object clone() throws CloneNotSupportedException {Student stuClone = (Student)super.clone();SchoolBag schoolbagClone = (SchoolBag) this.schoolBag.clone();stuClone.setSchoolBag(schoolbagClone);return stuClone;}...
}

现在我们在进行stu对象的schoolbag修改后的输出,会发现stu1对象的修改已经不会影响stuClone对象了,二者之间已经没有共享的存储空间了:

 此时这两个对象的内存布局图如下:

 因此我们可以得到何为深拷贝?

深拷贝进行后,进行拷贝的对象与拷贝出来的对象之间没有共享的一片存储空间,二者独立存在,对其中任何一方的修改都不会影响另外一方。


总结

拷贝实现方法clone的使用步骤以及注意事项:

  1. 实现Cloneable标记接口重写Object类中的clone方法
  2. 捕获处理或抛出使用clone方法带来的异常
  3. 将克隆猴返回的Object类型的对象向下转型为克隆类型
  4. 如果克隆对象的内部还有引用类型,则需要对内部的引用类型也按照上述1,2,3步骤在自己的clone方法中进行克隆返回。

相关文章:

Java中的深拷贝和浅拷贝

目录 🍎引出拷贝 🍎浅拷贝 🍎深拷贝 🍎总结 引出拷贝 现在有一个学生类和书包类,在学生类中有引用类型的书包变量: class SchoolBag {private String brand; //书包的品牌private int size; //书…...

大文件上传

上图就是大致的流程一、标题图片上传课程的标题图片Ajax发送请求到后端后端接收到图片使用IO流去保存图片,返回图片的信息对象JS回调函数接收对象通过$("元素id").val(值),方式给页面form表达img标签src属性值,达到上传图片并回显二…...

Python每日一练(20230327)

目录 1. 最大矩形 🌟🌟🌟 2. 反转链表 II 🌟🌟 3. 单词接龙 II 🌟🌟🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/C每日…...

Centos7 升级内核到5.10mellanox 编译安装

升级5.10内核 #uname -r 重启后 进入新的内核 进入新的内核信息 直接查看是看不到gcc版本 5.10需要高版本gcc 才可以进行编译...

冯诺依曼,操作系统以及进程概念

文章目录一.冯诺依曼体系结构二.操作系统(operator system)三.系统调用和库函数四.进程1.进程控制块(PCB)2.查看进程3.系统相关的调用4.fork介绍(并发引入)五.总结一.冯诺依曼体系结构 计算机大体可以说是…...

7.网络爬虫—正则表达式详讲

7.网络爬虫—正则表达式详讲与实战Python 正则表达式re.match() 函数re.search方法re.match与re.search的区别re.compile 函数检索和替换检索:替换:findallre.finditerre.split正则表达式模式常见的字符类正则模式正则表达式模式量词正则表达式举例前言&…...

关于位运算的巧妙性:小乖,你真的明白吗?

一.位运算的概念什么是位运算?程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。位运算就是直接操作二进制数,那么有哪些种类的位运算呢?常见的运算符有与(&)、或(|)、异或(^)、…...

【Android车载系列】第5章 AOSP开发环境配置

1 硬件支持 建议空闲内存16G以上,同时硬盘400G以上 内存不够可以使用 Linux 的交换分区2 VMware Workstation安装 https://download3.vmware.com/software/wkst/file/VMware-workstation-full-16.1.1-17801498.exe2.1 Ubuntu镜像 http://mirrors.aliyun.com/ubun…...

个人时间管理网站—Git项目管理

🌟所属专栏:献给榕榕🐔作者简介:rchjr——五带信管菜只因一枚😮前言:该专栏系为女友准备的,里面会不定时发一些讨好她的技术作品,感兴趣的小伙伴可以关注一下~👉文章简介…...

2023最新ChatGPT整理的40道Java高级面试题

2023 年最火的就是 ChatGPT 了,很多同事使用他完成一些代码上的智能提示,也有人使用它发了财《「用ChatGPT年入百万!」各博主发布生财之道,网友:答辩搬运工》、《“躺着就能赚大钱”?ChatGPT火了,有人早就动起坏脑筋》等。 最近我也使用 ChatGPT 写技术文章了,比如:《…...

单机分布式一体化是什么?真的是数据库的未来吗,OceanBase或将开启新的里程碑

一. 数据 我们先说说数据这个东西,这段时间的ChatGPT在全世界的爆火说明了一件事,数据是有用的,并且大量的数据如果有一个合适的LLM大规模语言模型训练之后,可以很高程度的完成很多意想不到的事情。 我们大多数的时候的注意力只…...

100天精通Python丨基础知识篇 —— 03、Python基础知识扫盲(第一个Python程序,13个小知识点)

文章目录🐜 1、Python 初体验Pycharm 第一个程序交互式编程第一个程序🐞 2、Python 引号🐔 3、Python 注释🦅 4、Python 保留字符🐯 5、Python 行和缩进🐨 6、Python 空行🐹 7、Python 输出&…...

springboot逍遥大药房管理系统

084-springboot逍遥大药房管理系统演示录像开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包&a…...

ZYNQ中的GPIO与AXI GPIO

GPIO GPIO—一种外设,对器件进行观测和控制MIO—将来自PS外设和静态存储器接口的访问多路复用到PS引脚上处理器控制外设的方法—通过一组寄存器包括状态寄存器和控制寄存器,这些寄存器都是有地址的,通过这些寄存器的读写进行外设的控制sessi…...

接口导入功能

1.接口api export function import(param) { return fetch({ url: XXX.import, method: POST, headers: { Content-Type: multipart/form-data; }, data: param }) } 2.页面vue 和 js逻辑 <el-button :loading"disable&qu…...

网络安全知识点总结 期末总结

1、信息安全从总体上可以分成5个层次&#xff0c;密码技术 是信息安全中研究的关键点。 2、握手协议 用于客户机与服务器建立起安全连接之前交换一系列信息的安全信道。 3、仅设立防火墙系统&#xff0c;而没有 安全策略 &#xff0c;防火墙就形同虚设。 4、应用代理防火墙 …...

linux挂载远程目录

服务端操作 # 1、安装NFS程序 yum -y install nfs* rpcbind,在centos6以前自带的yum源中为portmap。 使用yum安装nfs时会下载依赖&#xff0c;因此只要下载nfs即可&#xff0c;无需再下载rpcbind. # 2、查看是否安装了nfs与rpcbind rpm -qa | grep nfs rpm -qa | grep rpc…...

ChatGPT—初识

ChatGPT初识 由于ChatGPT 注册相关的文章被平台限制了&#xff0c;所以有注册相关的问题可以私聊&#xff0c;或者可以代注册 Chat GPT是一款基于GPT模型的对话型AI模型&#xff0c;能够模拟真实的对话风格和行为方式&#xff0c;让人与AI的交互变得更加自然顺畅。下面将从Chat…...

【ArcGIS Pro二次开发】(18):地理处理工具类【Geoprocessing】补遗

ArcGIS Pro SDK 3.0中的Geoprocessing类是用于执行地理处理工具的核心类。地理处理工具是用于执行空间分析、数据转换、数据管理等任务的工具集&#xff0c;包括常见的空间分析工具、栅格处理工具、矢量处理工具、地图制图工具等。 之前有简单记录了下Geoprocessing工具的用法…...

国产芯片方案——红外测温体温计方案

红外测温体温计采用了热电堆式&#xff0c;利用塞贝克效应&#xff0c;将收集到的红外线光信号转化为电信号&#xff0c;再经过放大等处理&#xff0c;按内部的算法校正后再显示屏幕上输出具体温度值&#xff0c;能快速准确地测量人体体温。红外测温体温计广泛应用于医疗卫生、…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

智能职业发展系统:AI驱动的职业规划平台技术解析

智能职业发展系统&#xff1a;AI驱动的职业规划平台技术解析 引言&#xff1a;数字时代的职业革命 在当今瞬息万变的就业市场中&#xff0c;传统的职业规划方法已无法满足个人和企业的需求。据统计&#xff0c;全球每年有超过2亿人面临职业转型困境&#xff0c;而企业也因此遭…...

拟合问题处理

在机器学习中&#xff0c;核心任务通常围绕模型训练和性能提升展开&#xff0c;但你提到的 “优化训练数据解决过拟合” 和 “提升泛化性能解决欠拟合” 需要结合更准确的概念进行梳理。以下是对机器学习核心任务的系统复习和修正&#xff1a; 一、机器学习的核心任务框架 机…...