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

Clonable 接口 深拷贝与浅拷贝(超详细!!!代码附注释带图)

Clonable又叫克隆接口,我们在使用Clonable之前,先来了解一下什么叫深拷贝,什么叫浅拷贝

目录

1.浅拷贝

2.深拷贝

3.Clonable接口


1.浅拷贝

定义:

浅拷贝拷贝就是拷贝指向对象的指针,意思就是说:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,浅拷贝只是一种简单的拷贝,让几个对象公用一个内存.

这里我们用一张图配合代码来理解

首先我们先写一段伪代码

String a = new String();
String b = a;

在上述代码中,a和b指向的是同一块地址空间,当a中的内容发生改变的时候,b也会随之改变

就像这个图一样,当对a中的数据进行操作的时候,b也会随之改变,我们称这种拷贝叫做浅拷贝

2.深拷贝

定义:

深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响

 还是一样,先看代码

String a = new String("abcdef");
        String b = new String();
        b += a;
        System.out.println(a);
        System.out.println(b);

这里面就是给a和b同时开了两个空间,然后把a中的数值拷贝到b中去,这种当a中的值发生改变的时候,不会影响到b,就叫做深拷贝

这张图中就比较好理解深拷贝了

知道了深浅拷贝之后,我们再去认识Clonable接口

3.Clonable接口

Object 类中存在一个 clone 方法 , 调用这个方法可以创建一个对象的 " 拷贝 ". 但是要想合法调用 clone 方法 , 必须要 先实现 Clonable 接口 , 否则就会抛出 CloneNotSupportedException 异常 .
下面我们一起看看这个接口
//这里实现这个接口的作用是表明这个类可以被克隆,实际上这个接口里面什么都没有
class Student implements Cloneable{
    public String name;
    public int Id;
    public String major;
    public Student(String name,int id,String major){
        this.name = name;
        this.Id = id;
        this.major = major;
    }
    @Override
//这里重写的是Objec类中的clone方法
//这里可以用try catch
    public Object clone() throws CloneNotSupportedException {
        Student o = null;
//这里的super其实是再调用Object中的clone方法
        o = (Student) super.clone();
        return o;
    }

}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {

        Student s1 = new Student("张三",123,"生物制药");
        Student s2 = (Student)s1.clone();
        System.out.println("s1 " + s1.name + s1.Id + s1.major);
        s1.Id = 5;
        System.out.println("s2 " + s2.name + s2.Id + s2.major);
    }
}

 其实这个clone接口做的事情,就是在内存中克隆一份,传给s2

借助图来理解

 这里再s1调用clone方法之后,便会克隆出来一个0X777并且返回给s2

这样就做到深拷贝了吗?

不一定哦,我们再看下面的代码

class Student implements Cloneable{
    public String name;
    public int Id;
    public String major;
    public func A;
    public Student(String name,int id,String major,func A){
        this.name = name;
        this.Id = id;
        this.major = major;
        this.A = A;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        Student o = null;
        o = (Student) super.clone();
        return o;
    }

}
//这里加了个类,让Student里面有个引用变量

class func{
    int x = 10;
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {

        Student s1 = new Student("张三",123,"生物制药",new func());
        Student s2 = (Student) s1.clone();
        System.out.println("s1 " + s1.name + s1.Id + s1.major + s1.A.x);
//这里是对s1进行的修改
        s1.A.x = 20;
        System.out.println("s2 " + s2.name + s2.Id + s2.major + s2.A.x);
    }
}

在上述代码中,加了一个func类,从而让Student有了引用变量,最后修改s1中的A的x

上述代码的执行结果是

 

最后修改s1中的A的x,导致s2中的A的x发生了改变,这种就不符合我们的预期了,成了浅拷贝了

为什么会这样呢?

一张图就能解释

这里可以看到,s1中的func和s2中的func是一样的,都是一块地址,所以指向同一块空间,所以当s1中的func中的数据改变,s2中的数据也会随之改变

那我们怎么做才能再让他进行深拷贝呢?

看代码

 


class Student implements Cloneable{
    public String name;
    public int Id;
    public String major;
    public func A;
    public Student(String name,int id,String major,func A){
        this.name = name;
        this.Id = id;
        this.major = major;
        this.A = A;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
//这里先克隆出一个student,但是克隆完之后,func仍然指向的是同一个地址
        Student tmp =(Student) super.clone();
//这里对func进行克隆,然后把克隆出来的结果复制给tmp.A
        tmp.A = (func)this.A.clone();
//最后再进行返回,就能得到一个全新的深拷贝对象了
        return tmp;
    }

}
//因为上面的clone需要调用this.a.clone,所以这个func也必须实现克隆,并且重写克隆方法
class func implements Cloneable{
    int x = 10;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {

        Student s1 = new Student("张三",123,"生物制药",new func());
        Student s2 = (Student) s1.clone();
        System.out.println("s1 " + s1.name + s1.Id + s1.major + s1.A.x);
        s1.A.x = 20;
        System.out.println("s2 " + s2.name + s2.Id + s2.major + s2.A.x);
    }
}

代码中有详细解析

运行结果为

所以,这样我们就做到了深拷贝,但是问题又来了,如果func中又有一个引用类型呢?

所以所谓的深拷贝,浅拷贝,需要根据业务需求进行操作,并没有绝对的深拷贝 

相关文章:

  • Centos7.4重启提示gurb2/i386-pc/normal.mod not found
  • 开学季·东莞理工学院
  • SpringMVC拦截器的基本使用
  • s20.基于 Kubernetes v1.25.0(kubeadm) 和 Docker 部署高可用集群(二)
  • ciscn_2019_es_2【BUUCTF】
  • 计算机毕业设计springboot+vue基本微信小程序的驾校宝典系统-驾照考试系统
  • 开源IM项目OpenIM单聊及万人群压测报告
  • 了解 Dockerfile VOLUME 指令
  • 用这个衍生方法增加维度,风控模型特征太少也不怕
  • 2022年9月2日:面向初学者的 web 开发--使用 JavaScript 制定决策
  • 【明年找到好工作】:面试题打卡第四天
  • vmpalyer虚拟机的使用教程,小白也能看懂
  • buu(rce命令执行)
  • 【Python】-- 元组、字符串常用方法、序列切片
  • 【Pytorch】多维矩阵的加法
  • [译]如何构建服务器端web组件,为何要构建?
  • Android 控件背景颜色处理
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • flask接收请求并推入栈
  • Java 内存分配及垃圾回收机制初探
  • JavaScript的使用你知道几种?(上)
  • log4j2输出到kafka
  • PAT A1050
  • Synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比...
  • 猴子数据域名防封接口降低小说被封的风险
  • 聚簇索引和非聚簇索引
  • 前端相关框架总和
  • 提醒我喝水chrome插件开发指南
  • 云大使推广中的常见热门问题
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • 第二十章:异步和文件I/O.(二十三)
  • ​学习一下,什么是预包装食品?​
  • #ifdef 的技巧用法
  • #控制台大学课堂点名问题_课堂随机点名
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • #我与Java虚拟机的故事#连载11: JVM学习之路
  • (02)vite环境变量配置
  • (a /b)*c的值
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (过滤器)Filter和(监听器)listener
  • (三)docker:Dockerfile构建容器运行jar包
  • (三)elasticsearch 源码之启动流程分析
  • (三)uboot源码分析
  • (学习日记)2024.01.09
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .locked1、locked勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .Mobi域名介绍
  • .NET gRPC 和RESTful简单对比
  • .NET NPOI导出Excel详解
  • .net图片验证码生成、点击刷新及验证输入是否正确
  • .net中的Queue和Stack
  • @column注解_MyBatis注解开发 -MyBatis(15)
  • [ vulhub漏洞复现篇 ] Hadoop-yarn-RPC 未授权访问漏洞复现