Cloneable接口(浅拷贝和深拷贝的区别)

news2024/10/3 19:00:18

在这里插入图片描述

前言

Object类中存在这一个clone方法,调用这个方法可以创建一个对象的“拷贝”。但是想要合法调用clone方法,必须要先实现Clonable接口,否则就会抛出CloneNotSupportedException异常。

1 Cloneable接口

//Cloneable接口声明
public interface Cloneable {
	
}

我们发现Cloneable接口中没有任何定义字段和方法,也就是说Cloneable接口是个空接口,既然是一个空接口那么实现这个空接口的意思是什么呢?

Cloneable接口作为一个空接口,它的作用是用来标记当前实现类,表示当前类是可以被克隆的,也就是我们将Cloneable接口当作一个标记接口。

1.1 clone()

前面我们提到Object类中存在一个clone()方法,调用这个方法可以创建一个对象的“拷贝“,我们通过clone()方法将下文定义类克隆,观察出现的现象来学习Cloneable接口。

//Person.java
public class Person {
	//字段
	public String name;
	public int age;

	//方法
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public void print() {
		System.out.println("我是" + this.name);
	}

	//Object类中的clone()方法被protected修饰只能在同个包或子类中使用
	//想在外界使用就必须重写
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

//Main.java
public class Main {
	//当方法中抛出受查异常,借助throws将异常抛给方法的调用者来处理
	public static void main(String[] args) throws CloneNotSupportedException {
		//实例一个Person对象
		Person person1 = new person("张三",18);
	} 
}
  • 当我们调用clone()克隆出张三的拷贝会出现什么现象?
Person person2 = (Person)person1.clone();

在这里插入图片描述

当我们在编译器运行时,编译器向我们抛出了受查异常CloneNotSupportedException,这说明当前类不支持克隆,也就是说当前类中没有实现Cloneable接口这个作为克隆标记的接口,当我们在Person类中实现这个接口后:
在这里插入图片描述

当程序运行时,我们就会将person1引用的对象中的内容拷贝一份到person2所引用的对象中去,堆栈关系图如下:
在这里插入图片描述

也就是说当我们要利用Object类中的clone()方法去克隆一个对象时,我们需要进行以下步骤:

  1. 重写父类Object中的克隆方法
  2. 注意对克隆返回对象进行类型转换,clone()方法默认放回Object
  3. 处理异常CloneNotSupportedException
  4. 实现Cloneable接口,标记当前接口可克隆

2. 深拷贝和浅拷贝

我们将上文提到的Person类进行修改,以修改后的person类进行深拷贝和浅拷贝的讲解

//Money.java
public class Money {
	public double m = 1999.9;
}

//Person.java
public class Person implements Cloneable{
	public String name;
	public Money money;

	//方法
	public Person(String name) {
		this.name = name;
		this.money = new Money(); 
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

2.1 浅拷贝

  • #浅拷贝: 仅复制对象本身及其引用的子对象的引用,而不复制这些子对象本身。结果是新对象和原对象共享子对象。如果子对象被修改,原对象也会受到影响。
Person person1 = new Person("张三");  
Person person2 = (Person)person1.clone();  
System.out.println("通过person1修改前:");  
System.out.println(person1.money.m);  
System.out.println(person2.money.m);  
person1.money.m = 1888.8;  
System.out.println("通过person1修改后:");  
System.out.println(person1.money.m);  
System.out.println(person2.money.m);

执行后:
在这里插入图片描述

也就是我们可以得到通过浅拷贝的堆栈图如下:
在这里插入图片描述

我们的子对象引用了一个对象,也就是说在浅拷贝过程中只实现了子对象引用的拷贝,所以我们通过person1修改的money对象中的m实际上修改的是同一个。

2.2 深拷贝

  • #深拷贝: 递归地复制对象及其所有子对象,创建一个完全独立的副本。新对象与原对象及其子对象完全独立,修改新对象不会影响原对象。
    我们希望深拷贝是实现了所有子对象的拷贝,形成一个完全独立的副本,也就是我们希望得到以下堆栈图:
    在这里插入图片描述

那么如何通过堆栈图所述得到我们想要的深拷贝效果呢?我们可以通过修改重写的clone()方法和实现Money类的Cloneable接口来实现。

//Money.java
public class Money implements Cloneable{  
    public double m = 1999.9;  
  
    @Override  
    protected Object clone() throws CloneNotSupportedException {  
        return super.clone();  
    }  
}
//Person.java
public class Person implements {
	//...
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Person tmp = (Person)super.clone();//(1)
		tmp.money = (Money)this.money.clone();//(2)
		return tmp;
	}
}

//(2)通过将子对象本身拷贝创建出独立的副本

需要注意的是:

  • 对子对象进行拷贝的时候需要相对应的子对象所对应类也实现Cloneable接口

实现深拷贝后再次执行代码:

Person person1 = new Person("张三");  
Person person2 = (Person)person1.clone();  
System.out.println("通过person1修改前:");  
System.out.println(person1.money.m);  
System.out.println(person2.money.m);  
person1.money.m = 1888.8;  
System.out.println("通过person1修改后:");  
System.out.println(person1.money.m);  
System.out.println(person2.money.m);

执行结果:
在这里插入图片描述
深拷贝过程创建一个完全独立的副本,使得新对象与原对象及其子对象完全独立,修改新对象不会影响原对象。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS 7文件系统

从centos7开始,默认的文件系统从ext4变成了XFS。随着虚拟化的应用越来越广泛,作为虚拟化磁盘来源的大文件(单个文件几GB级别)越来越常见。 1.XFS组成部分: XFS文件系统在数据的分布上主要划分为三部分:数据…

QT篇:QT介绍

一.QT概述 Qt 是一个跨平台的应用程序和用户界面框架,用于开发图形用户界面(GUI)应用程序以及命令行工 具。它最初由挪威的 Trolltech (奇趣科技)公司开发,现在由 Qt Company 维护,2020年12月8…

如何在网格中模拟腐烂扩散:如何使用广度优先搜索(BFS)解题

问题描述 你需要在一个二维的网格中处理橘子的腐烂扩散过程,网格中的每个单元格可以有三种状态: 0:表示空格,没有橘子。1:表示一个新鲜的橘子。2:表示一个腐烂的橘子,它可以在 1 分钟内让上下…

模拟算法(1)_替换所有的问号

个人主页:C忠实粉丝 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C忠实粉丝 原创 模拟算法(1)_替换所有的问号 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记,欢迎大家在评论区交流讨论💌 目录 1. …

MHA携手Atlas:打造高效读写分离解决方案,引领数据库性能飞跃

作者简介:我是团团儿,是一名专注于云计算领域的专业创作者,感谢大家的关注 座右铭: 云端筑梦,数据为翼,探索无限可能,引领云计算新纪元 个人主页:团儿.-CSDN博客 目录 前言&#…

npm切换到淘宝镜像

1、输入以下命令后回车,npm切换至淘宝镜像 npm config set registry https://registry.npmmirror.com 2、输入以下命令后回车,检查是否切换成功 npm config get registry 若返回此信息,表示切换成功 3、切换后就可使用淘宝镜像加快npm包的…

C语言 | Leetcode C语言题解之第447题回旋镖的数量

题目: 题解: int cmpfunc(const void *a, const void *b) {return (*(int *)a - *(int *)b); } //计算组合数*2 int every(int count) {if (count 1) {return 0;} else {return count * (count - 1);} } //计算每个锚点能产生的回旋镖总数 int part(in…

【嵌入式系统】第18章 脉宽调试器(PWM)

目录 18.1 结构框图 18.3 功能说明 18.3.4 PWM 信号发生器 18.3.5 死区发生器 18.3.6 中断/ADC 触发选择器 18.3.7 同步方法 18.3.8 故障条件 18.3.9 输出控制块 LES 硬件介绍(12)正交编码接口QEI 19.1 结构框图 19.2 信号描述 19.3 功能说明…

4M-21: An Any-to-Any Vision Model for Tens of Tasks and Modalities论文精度

贡献:在21种高度不同的模态中训练一个统一的模型,并且对比专有模型不会有性能损失做法:将不同模态映射到不同的token空间,并且可以生成不同的模态token【Any-to-any】关键点:如何在不同的模态中应用tokenization进行映…

【MySQL 07】内置函数

目录 1.日期函数 日期函数使用场景: 2.字符串函数 字符串函数使用场景: 3.数学函数 4.控制流函数 1.日期函数 函数示例: 1.在日期的基础上加日期 在该日期下,加上10天。 2.在日期的基础上减去时间 在该日期下减去2天 3.计算两…

【MySQL】服务器管理与配置

MySQL服务器 服务器默认配置 查看服务器默认选项和系统变量 mysqld --verbose --help 查看运行时的系统变量,可以通过like去指定自己要查询的内容 状态变量的查看 系统变量和状态变量的作用域 全局作用域: 对于每个会话都会生效当前会话:只…

通信工程学习:什么是SNMP简单网络管理协议

SNMP:简单网络管理协议 SNMP(Simple Network Management Protocol,简单网络管理协议)是一种用于在计算机网络中管理网络节点(如服务器、工作站、路由器、交换机等)的标准协议。它属于OSI模型的应用层&#…

TIM的PWM模式

定时器的工作流程: 定时器对时钟传来的脉冲次数计数,并且在次数到达范围值时触发中断。如向下计数模式时为0,向上计数为达到自动重装载计时器的值时触发中断。 STM32里面的定时器有多个定时器。 如TIM1、TIM2、TIM3 定时器的输入捕获模式来测量输…

我为什么决定关闭ChatGPT的记忆功能?

你好,我是三桥君 几个月前,ChatGPT宣布即将推出一项名为“记忆功能”的新特性,英文名叫memory。 这个功能听起来相当吸引人,宣传口号是让GPT更加了解用户,仿佛是要为我们每个人量身打造一个专属的AI助手。 在记忆功…

【笔记】平面

一、平面及其方程(3个条件,4种表达) F ( x , y , z ) F(x,y,z) F(x,y,z)为平面方程: 在这个平面上的点满足 F ( x , y , z ) 0 F(x,y,z)0 F(x,y,z)0不在这个平面上的点不满足 F ( x , y , z ) 0 F(x,y,z)0 F(x,y,z)0 归根结底&…

Python 课程23-LibROSA

前言 LibROSA 是一个用于音频分析的 Python 库,特别擅长音乐信号处理和音频特征提取。它提供了广泛的工具来处理音频文件,包括加载、变换、特征提取、可视化等功能。LibROSA 在音乐信息检索(MIR)、机器学习中的音频预处理和音频信…

java发送邮件email实战

1.首先在项目中增加依赖&#xff0c;在pom文件中添加如下坐标 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>2.发邮件工具类如下 package com.example.demo.…

安卓13默认使用大鼠标 与配置分析 andriod13默认使用大鼠标 与配置分析

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.彩蛋1.前言 android13里面的鼠标貌似比以前版本的鼠标小了,有些客户想要把这个鼠标改大。这个功能,android有现成的,就在这里,设置 =》无障碍 =》色彩和动画 =》 大号鼠标指针。 我们通过…

JavaWeb - 8 - 请求响应 分层解耦

请求响应 请求&#xff08;HttpServletRequest&#xff09;&#xff1a;获取请求数据 响应&#xff08;HttpServletResponse&#xff09;&#xff1a;设置响应数据 BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程…

【解决方案】关于 UART 接收数据时丢失数据的解决办法——环形缓冲存储区

文章目录 UART 通信丢失数据的常见原因总结串口&#xff08;UART&#xff09;数据丢失 Bug 的复现引入环形队列解决数据丢失问题总结 在嵌入式系统和物联网&#xff08;IoT&#xff09;设备中&#xff0c;串行通信是一种非常普遍且重要的数据传输方式。无论是通过 UART、RS-232…