深克隆与浅克隆的区别与实现
在软件开发中,克隆对象是一个常见需求。克隆的方式主要有两种:深克隆(Deep Clone)和浅克隆(Shallow Clone)。了解它们的区别及其实现方法,对于编写高效、安全的代码非常重要。
深克隆与浅克隆的区别
浅克隆(Shallow Clone)
浅克隆会复制原型对象的基本数据类型的字段(如int, float等),而对于引用类型的字段(如对象、数组等),只会复制其引用地址。也就是说,原型对象和克隆对象会共享引用类型的字段。
深克隆(Deep Clone)
深克隆不仅复制原型对象的基本数据类型字段,还会递归复制引用类型的字段。这样,原型对象和克隆对象在内存中是完全独立的,不会共享任何引用类型的字段。
如何实现深克隆?
深克隆的实现方式有多种,下面介绍三种常见的方法:
- 所有对象都实现克隆方法
- 通过构造方法实现深克隆
- 使用 JDK 自带的字节流实现深克隆
所有对象都实现克隆方法
这种方式要求所有引用类型的对象都实现Cloneable
接口,并重写clone
方法。例如:
public class CloneExample {public static void main(String[] args) throws CloneNotSupportedException {// 创建被赋值对象Address address = new Address(001, "北京");People p1 = new People(1, "Java", address);// 克隆 p1 对象People p2 = p1.clone();// 修改原型对象p1.getAddress().setCity("上海");// 输出 p1 和 p2 地址信息System.out.println("p1:" + p1.getAddress().getCity() + " p2:" + p2.getAddress().getCity());}static class People implements Cloneable {private Integer id;private String name;private Address address;@Overrideprotected People clone() throws CloneNotSupportedException {People people = (People) super.clone();people.setAddress(this.address.clone()); // 引用类型克隆赋值return people;}// getter 和 setter 方法public People(Integer id, String name, Address address) {this.id = id;this.name = name;this.address = address;}}static class Address implements Cloneable {private Integer id;private String city;@Overrideprotected Address clone() throws CloneNotSupportedException {return (Address) super.clone();}// getter 和 setter 方法public Address(Integer id, String city) {this.id = id;this.city = city;}}
}
通过构造方法实现深克隆
《Effective Java》中推荐使用构造器来实现深克隆。构造器的参数为基本数据类型或字符串类型时直接赋值,如果是对象类型,则需要重新创建一个新的对象。
public class SecondExample {public static void main(String[] args) {// 创建对象Address address = new Address(001, "北京");People p1 = new People(1, "Java", address);// 调用构造函数克隆对象People p2 = new People(p1.getId(), p1.getName(), new Address(p1.getAddress().getId(), p1.getAddress().getCity()));// 修改原型对象p1.getAddress().setCity("上海");// 输出 p1 和 p2 地址信息System.out.println("p1:" + p1.getAddress().getCity() + " p2:" + p2.getAddress().getCity());}static class People {private Integer id;private String name;private Address address;// getter 和 setter 方法public People(Integer id, String name, Address address) {this.id = id;this.name = name;this.address = address;}}static class Address {private Integer id;private String city;// getter 和 setter 方法public Address(Integer id, String city) {this.id = id;this.city = city;}}
}
使用 JDK 自带的字节流实现深克隆
通过字节流实现深克隆的方式是将原型对象写入到内存中的字节流,然后再从这个字节流中读出信息,生成一个新对象。这个新对象与原型对象在内存地址上是完全独立的。
import java.io.*;public class ThirdExample {public static void main(String[] args) {// 创建对象Address address = new Address(001, "北京");People p1 = new People(1, "Java", address);// 通过字节流实现克隆People p2 = (People) StreamClone.clone(p1);// 修改原型对象p1.getAddress().setCity("上海");// 输出 p1 和 p2 地址信息System.out.println("p1:" + p1.getAddress().getCity() + " p2:" + p2.getAddress().getCity());}static class StreamClone {public static <T extends Serializable> T clone(People obj) {T cloneObj = null;try {// 写入字节流ByteArrayOutputStream bo = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bo);oos.writeObject(obj);oos.close();// 分配内存, 写入原始对象, 生成新对象ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());ObjectInputStream oi = new ObjectInputStream(bi);// 返回生成的新对象cloneObj = (T) oi.readObject();oi.close();} catch (Exception e) {e.printStackTrace();}return cloneObj;}}static class People implements Serializable {private Integer id;private String name;private Address address;// getter 和 setter 方法public People(Integer id, String name, Address address) {this.id = id;this.name = name;this.address = address;}}static class Address implements Serializable {private Integer id;private String city;// getter 和 setter 方法public Address(Integer id, String city) {this.id = id;this.city = city;}}
}
需要注意的是,由于通过字节流序列化实现的深克隆,每个对象必须实现Serializable
接口,否则会抛出异常。
总结
深克隆和浅克隆在对象复制上的区别主要在于是否复制引用类型的对象。浅克隆仅复制对象本身,而深克隆会递归复制所有引用类型的对象。根据需求的不同,可以选择实现Cloneable
接口、使用构造器或者通过字节流进行深克隆。了解这些实现方法,可以帮助我们在开发过程中更好地管理对象的复制和内存的使用。
相关文章:
深克隆与浅克隆的区别与实现
在软件开发中,克隆对象是一个常见需求。克隆的方式主要有两种:深克隆(Deep Clone)和浅克隆(Shallow Clone)。了解它们的区别及其实现方法,对于编写高效、安全的代码非常重要。 深克隆与浅克隆的…...

【学习笔记】无人机系统(UAS)的连接、识别和跟踪(六)-无人机直接C2通信
目录 引言 5.4 直接C2通信 5.4.1 概述 5.4.2 A2X直接C2通信服务的授权策略 5.4.3 USS使用A2X直接C2通信服务的C2授权程序 5.4.4 直接C2通信建立程序 引言 3GPP TS 23.256 技术规范,主要定义了3GPP系统对无人机(UAV)的连接性、身份识别…...

认识和安装R的扩展包,什么是模糊搜索安装,工作目录和空间的区别与设置
R语言以其强大的功能和灵活的扩展性,成为了无数数据分析师和研究者的首选工具。R的丰富功能和海量扩展包直接相关,但如何高效管理这些扩展包,进而充分发挥R的强大潜力?本文将为您揭示这些问题的答案。 一、R的扩展包 R的包(packages)是由R函数、数据和预编译代码组成的一…...
解决STM32开启定时器时立即进入一次中断程序问题
转自 解决STM32开启定时器时立即进入一次中断程序问题_stm32f407定时器初始化自动进入一次-CSDN博客 配置STM32定时器时,定时器中断使能、定时器使能、清除更新中断标志位,三者不同顺序程序执行时有不同效果,具体如下: TIM_Clea…...

Unity UGUI 之EventSystem
本文仅作学习笔记与交流,不作任何商业用途 本文包括但不限于unity官方手册,唐老狮,麦扣教程知识,引用会标记,如有不足还请斧正 1.EventSystem是什么? 有需要请查看手册:Unity - 手册࿱…...

USB转多路UART - USB 基础
一、 前言 断断续续做了不少USB相关开发,但是没有系统去了解过,遇到问题就很被动了。做这个USB转UART的项目就是,于是专门花了一天的时间学习USB及CDC相关,到写这文章时估计也忘得差不多了,趁项目收尾阶段记录一下&am…...
接近50个实用编程相关学习资源网站
Date: 2024.07.17 09:45:10 author: lijianzhan 编程语言以及编程相关工具等实用性官方文档网站 C语言文档:https://learn.microsoft.com/zh-cn/cpp/c-languageMicrosoft C、C和汇编程序文档:https://learn.microsoft.com/zh-cn/cppJAVA官方文档&#…...
在数据操作中使用SELECT子句
目录 一、INSERT 语句中使用 SELECT子句 二、UPDATE 语句中使用 SELECT子句 三、DELETE 语句中使用 SELECT子句 一、INSERT 语句中使用 SELECT子句 在 INSERT 语句中使用 SELECT子句,可以将一个或多个表或视图中的数据添加到另外一个表中。使用 SELECT 子句还可以…...

Golang | Leetcode Golang题解之第274题H指数
题目: 题解: func hIndex(citations []int) int {// 答案最多只能到数组长度left,right:0,len(citations)var mid intfor left<right{// 1 防止死循环mid(leftright1)>>1cnt:0for _,v:range citations{if v>mid{cnt}}if cnt>mid{// 要找…...

区块链技术在智能家居中的创新应用探索
随着物联网技术的发展和智能家居市场的蓬勃发展,区块链技术作为一种去中心化的数据管理和安全保障技术,正在逐渐引入智能家居领域,并为其带来了新的创新应用。本文将探讨区块链技术在智能家居中的具体应用场景、优势以及未来发展方向。 智能家…...

无需业务改造,一套数据库满足 OLTP 和 OLAP,GaiaDB 发布并行查询能力
在企业中通常存在两类数据处理场景,一类是在线事务处理场景(OLTP),例如交易系统,另一类是在线分析处理场景(OLAP),例如业务报表。 OLTP 数据库擅长处理数据的增、删、改,…...
PHP 表单验证:邮件和URL
PHP 表单验证:邮件和URL 在Web开发中,表单验证是一个至关重要的环节,它确保了用户输入的数据的有效性和安全性。特别是在处理邮件地址和URL时,准确的验证尤为重要。本文将详细介绍如何使用PHP来验证表单中的邮件地址和URL。 邮件…...
前端八股文 路由的懒加载
为什么会有 路由的懒加载 在现代单页应用(SPA)的开发中,路由懒加载是一种提升应用性能的关键技术。通过按需加载组件,而非在应用启动时一次性加载所有模块,可以显著减少初次加载时间和资源消耗。本文旨在深入探讨前端…...

HarmonyOS Web组件(二)
1. HarmonyOS Web组件 官方文档 1.1. 混合开发的背景和好处 混合开发(Hybrid Development)是一种结合原生应用和Web应用的开发模式,旨在同时利用两者的优势。随着移动应用需求的多样化和复杂化,单一的开发方式往往难以满足所有…...
HarmonyOS应用开发者高级认证,Next版本发布后最新题库 - 单选题序号2
基础认证题库请移步:HarmonyOS应用开发者基础认证题库 注:有读者反馈,题库的代码块比较多,打开文章时会卡死。所以笔者将题库拆分,单选题20个为一组,多选题10个为一组,题库目录如下,…...

基于python深度学习遥感影像地物分类与目标识别、分割实践技术应用
目录 专题一、深度学习发展与机器学习 专题二、深度卷积网络基本原理 专题三、TensorFlow与Keras介绍与入门 专题四、PyTorch介绍与入门 专题五、卷积神经网络实践与遥感图像场景分类 专题六、深度学习与遥感图像检测 专题七、遥感图像检测案例 专题八、深度学习与遥感…...

叶再豪降龙精英课程总结
文章目录 1.思维认知1.1 稻盛和夫成功公式1.2 龙头主升模式1.3 龙头主升-两种路径1.4 股市新手的炒股思路1.5 龙头案例1.6 降龙心法 2.情绪周期2.1 情绪周期2.1 情绪演绎周期2.2 情绪的四个部分2.2.1 指数的情绪周期2.2.3 热点情绪周期2.2.4 热点情绪演绎周期2.2.5 大热点支线2…...
算法 - 查找算法(顺序、折半、红黑树、AVL树、B+树、散列)
查找 顺序查找 查找算法原理: 顺序查找是一种简单的查找方法,从数组的第一个元素开始,依次比较每个元素,直到找到目标元素或者数组结束为止。 实现步骤: 从数组的第一个元素开始。逐一比较数组中的元素与目标值。如…...

TCP与UDP网络编程
网络通信协议 java.net 包中提供了两种常见的网络协议的支持: UDP:用户数据报协议(User Datagram Protocol)TCP:传输控制协议(Transmission Control Protocol) TCP协议与UDP协议 TCP协议 TCP协议进行通信的两个应用进程:客户端、服务端 …...

媲美Midjourney-v6,Kolors最新文生图模型部署
Kolors模型是由快手团队开发的大型文本到图像生成模型,专门用于将文本描述转换成高质量的图像。 Kolors模型支持中英文双语输入,生成效果与Midjourney-v6相媲美,能够处理长达256个字符的文本输入,具备生成中英文文字的能力。 Ko…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...