深入理解Java中的clone对象

目录

1. 为什么要使用clone

2. new和clone的区别

3. 复制对象和复制引用的区别

4.浅克隆和深克隆

5. 注意事项


1. 为什么要使用clone

在实际编程过程中,我们常常遇到这种情况:有一个对象 A,需要一个和 A 完全相同新对象 B,并且此后对 B 任何改动都不会影响到 A 中的值,也就是说,A 与 B 是两个独立的对象,但 B 的初始值是由 A 对象确定的。在 Java 语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需
求虽然有很多途径,但实现 clone()方法是其中最简单,也是最高效的手段。

2. new和clone的区别

new 

  • 通过调用类的构造函数来创建新对象实例,这意味着可以定义对象的初始化状态
  • 根据对象类型明确分配相应大小的内存空间
  • 需要显式初始化,通常在构造函数中完成
  • 涉及内存分配、构造函数调用和初始化,可能在复杂对象中相对较慢
  • 不依赖已有对象,每次创建都是全新的实例
  • 无需实现任何特殊接口
  • 用于全新创建对象实例,无历史状态需求
  • 生成完全独立的对象,互不影响

clone

  • 不会调用构造函数,而是复制已有对象的属性,适用于快速创建一个具有相似状态的对象
  • 分配内存给新对象,并且大小与原对象相同
  • 继承了原对象的初始化状态,不需要再次初始化
  • 通常认为较快,因为避免了构造函数调用和逐步初始化,但具体取决于JVM实现和场景
  • 必须有一个原对象作为克隆的基础
  • 需要实现Cloneable接口并重写clone()方法,否则会抛出CloneNotSupportedException异常
  • 用于基于现有对象快速生成一个副本,例如保存当前状态进行操作或比较
  • 虽然生成的是一个新对象,但如果涉及到引用类型字段,则需要深拷贝处理,否则可能存在依赖

        new操作符的本意是分配内存。程序执行到 new 操作符时,首先去看 new 操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。clone 在第一步是和 new 相似的,都是分配内存,调用 clone 方法时,分配的内存和原对象(即调用 clone 方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后,clone 方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部,new主要用于创建全新的对象实例,而clone则用于基于已有对象快速生成副本。选择使用哪一种方式取决于具体的编程需求和场景。理解它们的不同之处能够帮助更好地掌握Java中的对象创建和管理技巧。

3. 复制对象和复制引用的区别

class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}...//此处省略set、get、toString方法
}public class Main {public static void main(String[] args) {Person person1 = new Person("Alice", 30);Person person2 = person1; // 复制引用Person person3 = (Person)person1.clone(); // 复制对象System.out.println("Original Person: " + person1);System.out.println("Reference Copy: " + person2);System.out.println("Object Copy: " + person3);person1.setName("Bob");person1.setAge(40);System.out.println("After modification:");System.out.println("Original Person: " + person1);System.out.println("Reference Copy: " + person2); // 修改了person1,person2也会受到影响System.out.println("Object Copy: " + person3); // person3不受person1的修改影响}
}

在这个例子中,我们创建了一个Person类,并实例化了三个对象:person1person2person3。其中,person2是通过复制引用的方式创建的,而person3是通过复制对象的方式创建的。

当我们修改person1的属性时,可以看到以下结果:

  • person2person1的引用,因此它们的属性值相同,并且对其中一个对象的修改会影响另一个对象。
  • person3是通过复制person1的属性创建的新对象,因此它的属性值与person1相同,但对person3的修改不会影响person1

复制引用 

复制对象

4.浅克隆和深克隆

        在Java编程中,clone()方法提供了一种创建对象副本的方式。通过实现Cloneable接口并重写clone()方法,我们可以轻松地创建一个对象的完整复制品。然而,关于使用clone()方法,存在许多细节和注意事项,尤其是在处理浅克隆(shallow copy)与深克隆(deep copy)时。本篇博客将深入探讨这一主题。

        在Java中,所有对象都继承自Object类,其中包含一个clone()方法,但该方法是受保护的(protected),意味着我们不能直接在非子类外部调用它。为了使得clone()方法可用,我们需要实现Cloneable接口,该接口是一个标记接口(marker interface),本身不包含任何方法。

实现Cloneable接口后,我们可以通过重写clone()方法来提供自定义的复制逻辑。如果不重写clone()方法,对象将获得一个默认的浅复制行为,这意味着基本字段将被复制,但引用类型字段将只复制引用,而不是引用的对象

浅克隆

        浅复制是指在复制对象时,只复制基本类型的字段和引用,但不复制引用的对象。这意味着,如果原对象中的引用类型字段被修改,复制的对象也会受到影响。

class ShallowCopyExample implements Cloneable {int intValue; // 基本类型字段String stringValue; // 引用类型字段public ShallowCopyExample clone() throws CloneNotSupportedException {return (ShallowCopyExample) super.clone();}
}

深克隆

        相对地,深复制会复制所有字段,包括引用的对象。这就需要我们自定义clone()方法以实现所需行为。

class DeepCopyExample implements Cloneable {int intValue; // 基本类型字段String stringValue; // 引用类型字段DeepCopyExample innerObject; // 引用的对象public DeepCopyExample clone() throws CloneNotSupportedException {DeepCopyExample deepCopy = (DeepCopyExample) super.clone();deepCopy.innerObject = new DeepCopyExample(); // 对内部对象进行深复制deepCopy.innerObject.intValue = this.innerObject.intValue;deepCopy.innerObject.stringValue = this.innerObject.stringValue;return deepCopy;}
}

        这段代码展示了一个名为DeepCopyExample的Java类,该类实现了Cloneable接口。这个类有三个字段:一个基本类型(int)字段intValue,一个引用类型(String)字段stringValue,以及一个引用的对象字段innerObject

        在这个类中,我们重写了clone()方法,以便实现深拷贝。深拷贝意味着当我们复制一个对象时,不仅复制对象的值,还会复制对象所引用的其他对象。这样,原始对象和克隆对象之间的所有引用都是独立的。

        在clone()方法中,首先调用super.clone()来创建一个新的DeepCopyExample对象。然后,我们为innerObject字段创建一个新的DeepCopyExample对象,并将原始对象的innerObject字段的值复制到新对象的相应字段中。最后,返回新创建的深拷贝对象。

        需要注意的是,如果innerObject字段中的DeepCopyExample对象也包含其他引用类型的字段,那么这些字段也需要进行深拷贝。在这个示例中,我们只处理了intValuestringValue字段的深拷贝

 

 

5. 注意事项

  • Cloneable接口的存在是为了标记对象可以被克隆。如果我们忘记实现这个接口,clone()方法会抛出CloneNotSupportedException异常。
  • 对于含有引用类型字段的对象,默认的clone()方法不会创建新的对象实例来复制这些字段,而是复制它们的引用,这可能导致意外的副作用。
  • 在实现深复制时,需要特别关注循环引用的情况,即一个对象直接或间接引用了自己。不妥善处理这种情况会导致无限递归和栈溢出错误。
  • 当对象层次结构很复杂时,手动实现深复制可能会很繁琐,此时可以考虑使用序列化或其他第三方库来实现深复制。

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

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

相关文章

ModuleNotFoundError: No module named ‘keras.layers.core‘怎么解决

问题 ModuleNotFoundError: No module named keras.layers.core,如图所示: 如何解决 将from keras.layers.core import Dense,Activation改为from tensorflow.keras.layers import Dense,Activation,如图所示: 顺利运行&#xf…

IOS Siri和快捷指令打开app

使用场景 需要Siri打开应用或者在自定义快捷指令打开应用,并携带内容进入应用。 1.创建Intents文件 1.1 依次点开File->New->File 1.2 搜索intent关键字找到 SiriKit Intent Definition File文件 1.3 找到刚才创建的Intent文件,点击然后New Inte…

【JS逆向学习】快乐学堂登陆接口(自定义DES加密、ddddocr验证码识别)

逆向目标 网址:https://www.91118.com/Passport/Account/Login接口:https://www.91118.com/passport/Account/LoginPost参数: passr 逆向过程 输入手机号、密码、验证码 点击登陆,多试几次,然后观察并比较不通请求…

MMO 地图传送,UI系统框架设计

地图传送 创建传送点 建碰撞器触发 //位置归零 建一个传送门cube放到要传送的位置(这个teleporter1是传出的区域 这是从另一张地图传入时的传送门 创建一个脚本TeleporterObject给每个传送cube都绑上脚本 通过脚本,让传送门在编辑器下面还能绘制出来 …

GIT | git提交注释自动添加信息头

GIT | git提交注释自动添加信息头 时间:2024年9月6日10:20:11 文章目录 GIT | git提交注释自动添加信息头1.操作2.commit-msg文件 1.操作 2.commit-msg文件 #!/bin/sh # # An example hook script to check the commit log message. # Called by "git commit&q…

基于SpringBoot+Vue+MySQL的流浪猫狗宠物救助救援网站管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 在当今社会,随着宠物数量的激增及人们关爱动物意识的提升,流浪猫狗问题日益严峻。为解决这一问题,构建一套高效、便捷的流浪猫狗宠物救助救援网站管理系统显得尤为重要。本系统基于SpringBoot…

CSP-CCF★★★201812-2小明放学★★★

目录 一、问题描述 二、解答 (1)注意: (2)80分版: (3)100分版: 三、总结 一、问题描述 二、解答 (1)注意: 题目的n小于等于10的…

m3u8网页视频文件爬取与视频合成

文章目录 m3u8网页视频文件爬取与视频合成下载m3u8文件下载m3u8文件列表所对应的ts文件下载ffmpeg m3u8网页视频文件爬取与视频合成 我们经常在网络上找到的自己想要的视频素材却无法下载,并且打开控制台一看视频是通过分割成一份份的.ts文件发送过来的。 下载m3u8…

零信任安全:重新思考数字世界的访问

目录 ​编辑 网络安全形势的演变 数字安全的变化 引入零信任安全 零信任的当今意义 了解零信任原则 零信任架构的核心概念 实施微分段 持续验证:积极主动的立场 与传统安全模型的对比 在现代企业中实施零信任 零信任实施基础知识 多重身份验证 (MFA) 的…

c++(继承、模板进阶)

一、模板进阶 1、非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。 非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中…

非监督式机器学习:群集

聚类分析是一种非监督式机器学习形式,在此形式下,基于观察值的数据值或特征的相似性,将观察值分组到群集中。 这种就是非监督式机器学习,因为它不使用先前已知的标签值来训练模型。 在聚类分析模型中,标签是群集&#…

帧缓冲 framebuffer

一、基本概念 framebuffer: 帧缓存、帧缓存(显示设备) Linux内核为显示提供的一套应用程序接口。(驱动内核支持) 分辨率: 像素点 显示屏:800 * 600(横向有800个像素点,纵向有60…

DAY73

作业 pro文件&#xff1a; QT texttospeech 头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPushButton> //按钮类 #include <QLabel> //标签类 #include <QLineEdit> //行编译器类 #include…

阿里中间件——diamond

一、前言 最近工作不忙闲来无事&#xff0c;仔细分析了公司整个项目架构&#xff0c;发现用到了很多阿里巴巴集团开源的框架&#xff0c;今天要介绍的是中间件diamond. 二、diamond学习笔记 1、diamond简介 diamond是一个管理持久配置&#xff08;持久配置是指配置数据会持久化…

【Datawhale X 李宏毅苹果书 AI夏令营】《深度学习详解》Task3 打卡

文章目录 前言学习目标一、优化策略二、模型偏差三、优化问题三、过拟合增加训练集给模型一些限制 四、交叉验证五、不匹配总结 前言 本文是【Datawhale X 李宏毅苹果书 AI夏令营】的Task3学习笔记打卡。 学习目标 李宏毅老师对应视频课程&#xff1a;https://www.bilibili.…

QDY421F-16P-25液氨不锈钢液动紧急切断阀

一、产品概述 QDY421F-16P-25液氨不锈钢液动紧急切断阀&#xff0c;采用先进的液动驱动技术&#xff0c;结合高质量的不锈钢材质&#xff0c;专为满足液氨等腐蚀性介质的紧急切断需求而设计。该阀门的工作压力可达16MPa&#xff0c;适用于DN25&#xff08;即25毫米&#xff09;…

系统架构师考试学习笔记第四篇——架构设计实践知识(18)面向服务架构设计理论与实践

本章考点&#xff1a; 第18课时主要学习面向服务架构设计理论与实践。根据考试大纲&#xff0c;本课时知识点会涉及单选题型&#xff08;约占2~5分&#xff09;和案例题&#xff08;25分&#xff09;&#xff0c;本课时内容偏重于方法的掌握和应用&#xff0c;根据以往全国计算…

时序预测|基于小龙虾优化高斯过程GPR数据回归预测Matlab程序COA-GPR 多特征输入单输出 附赠基础GPR

时序预测|基于小龙虾优化高斯过程GPR数据回归预测Matlab程序COA-GPR 多特征输入单输出 附赠基础GPR 文章目录 一、基本原理二、实验结果三、核心代码四、代码获取五、总结 时序预测|基于小龙虾优化高斯过程GPR数据回归预测Matlab程序COA-GPR 多特征输入单输出 附赠基础GPR 一、…

mysql高级sql

文章目录 一&#xff0c;查询1.按关键字排序1.1按关键字排序操作(1)按分数排序查询&#xff08;不加asc默认为升序&#xff09;(2)按分数降序查询&#xff08;DESC&#xff09;(3)使用where进行条件查询(4)使用ORDER BY语句对多个字段排序 1.2使用区间判断查询&#xff08;and/…

如何通过内网穿透实现Pycharm远程服务器编译项目与服务器代码同步

文章目录 前言一、前期准备1. 检查IDE版本是否支持2. 服务器需要开通SSH服务 二、Pycharm本地链接服务器测试1. 配置服务器python解释器 三、使用内网穿透实现异地链接服务器开发1. 服务器安装Cpolar2. 创建远程连接公网地址 四、使用固定TCP地址远程开发 前言 本文主要介绍如…