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的使用步骤以及注意事项:
- 实现Cloneable标记接口,重写Object类中的clone方法
- 捕获处理或抛出使用clone方法带来的异常
- 将克隆猴返回的Object类型的对象向下转型为克隆类型
- 如果克隆对象的内部还有引用类型,则需要对内部的引用类型也按照上述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个层次,密码技术 是信息安全中研究的关键点。 2、握手协议 用于客户机与服务器建立起安全连接之前交换一系列信息的安全信道。 3、仅设立防火墙系统,而没有 安全策略 ,防火墙就形同虚设。 4、应用代理防火墙 …...
linux挂载远程目录
服务端操作 # 1、安装NFS程序 yum -y install nfs* rpcbind,在centos6以前自带的yum源中为portmap。 使用yum安装nfs时会下载依赖,因此只要下载nfs即可,无需再下载rpcbind. # 2、查看是否安装了nfs与rpcbind rpm -qa | grep nfs rpm -qa | grep rpc…...
ChatGPT—初识
ChatGPT初识 由于ChatGPT 注册相关的文章被平台限制了,所以有注册相关的问题可以私聊,或者可以代注册 Chat GPT是一款基于GPT模型的对话型AI模型,能够模拟真实的对话风格和行为方式,让人与AI的交互变得更加自然顺畅。下面将从Chat…...
【ArcGIS Pro二次开发】(18):地理处理工具类【Geoprocessing】补遗
ArcGIS Pro SDK 3.0中的Geoprocessing类是用于执行地理处理工具的核心类。地理处理工具是用于执行空间分析、数据转换、数据管理等任务的工具集,包括常见的空间分析工具、栅格处理工具、矢量处理工具、地图制图工具等。 之前有简单记录了下Geoprocessing工具的用法…...
国产芯片方案——红外测温体温计方案
红外测温体温计采用了热电堆式,利用塞贝克效应,将收集到的红外线光信号转化为电信号,再经过放大等处理,按内部的算法校正后再显示屏幕上输出具体温度值,能快速准确地测量人体体温。红外测温体温计广泛应用于医疗卫生、…...
【花雕学编程】代码泄露之后:深度剖析Claude开源对开发者生态的冲击与机遇
导语:2026年3月31日,Anthropic 旗下 Claude Code CLI 客户端源码意外泄露,1906个源文件、51.2万行TypeScript代码被开发者备份至 GitHub 仓库 instructkr/claude-code,标注为“仅供研究”。这场看似偶然的打包失误,并非…...
别再死记公式!一个Buck电路实例带你吃透‘小信号建模’到底在干什么
从Buck电路实战理解小信号建模:为什么工程师需要这个"数学翻译器"? 第一次接触小信号建模时,我和大多数电力电子初学者一样困惑——明明电路已经能用状态方程描述,为什么还要大费周章地推导那些看似复杂的传递函数&…...
别再用subprocess了!Mojo原生FFI直连Python C API的5种安全模式,含CPython 3.11+PyPy兼容性矩阵表
第一章:Mojo 与 Python 混合编程案例 生产环境部署Mojo 作为新兴的系统级编程语言,原生兼容 Python 生态,支持在关键性能路径中无缝调用 Mojo 编译模块,同时复用 Python 的成熟工具链与部署基础设施。在生产环境中,典型…...
OpenClaw技能开发入门:为Qwen3-14b_int4_awq扩展自定义功能
OpenClaw技能开发入门:为Qwen3-14b_int4_awq扩展自定义功能 1. 为什么需要自定义技能? 去年冬天,我花了整整两周时间手动整理公司项目的技术文档。每天重复着复制、粘贴、格式调整的机械操作,直到偶然发现OpenClaw这个开源自动化…...
Graphormer开源模型多场景落地:高校科研、药企CADD、新材料研发实操路径
Graphormer开源模型多场景落地:高校科研、药企CADD、新材料研发实操路径 1. 项目概述 Graphormer是一种基于纯Transformer架构的图神经网络模型,专门为分子图(原子-键结构)的全局结构建模与属性预测而设计。该模型在OGB、PCQM4M…...
StructBERT语义分析工具实测:一键判断句子相似度,支持GPU加速
StructBERT语义分析工具实测:一键判断句子相似度,支持GPU加速 1. 工具核心价值 StructBERT语义分析工具是一款专为中文文本设计的本地化语义相似度计算解决方案。不同于传统的关键词匹配方法,该工具基于阿里巴巴开源的StructBERT-Large模型…...
Java集合判空全攻略:从原生方法到Apache Commons工具类对比
Java集合判空全攻略:从原生方法到Apache Commons工具类对比 在Java开发中,集合判空是最基础却又最容易出错的环节之一。一个看似简单的判空操作,背后可能隐藏着NPE风险、性能损耗甚至逻辑漏洞。本文将深入剖析Java原生判空方法与Apache Commo…...
AI在测试中的应用:从测试用例生成到缺陷预测
随着软件开发流程向敏捷与DevOps的持续演进,软件测试面临着迭代周期缩短、系统复杂度飙升的双重压力。传统的测试方法,高度依赖人工经验与重复劳动,在效率、覆盖率和预测性上逐渐显现瓶颈。人工智能技术的引入,正从辅助工具演变为…...
SEO的发展趋势会是什么样的
SEO的发展趋势会是什么样的 在互联网的快速发展中,搜索引擎优化(SEO)技术一直是网站排名和流量增长的关键。随着技术的不断进步,SEO的发展趋势也在不断演变。未来SEO的发展趋势会是什么样的呢?本文将从多个角度探讨这…...
SSD用久了为啥会变慢?深入NAND Flash的‘写放大’与‘磨损均衡’,教你看懂SMART数据避坑
SSD性能下降的真相:从写放大到磨损均衡的深度解析 你是否遇到过这样的困扰——新买的SSD速度飞快,但用了一段时间后,系统响应明显变慢,开机时间延长,文件传输速度大不如前?这种现象并非偶然,而是…...
