Java中clone的浅拷贝和深拷贝区别以及方法详解

news2025/1/11 8:59:01

克隆定义

在 Java 中,克隆是创建原始对象的精确副本的过程。它本质上意味着能够创建一个与原始对象具有相似状态的对象。
复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。

new对象和clone区别

  • 使用new操作符创建一个对象
    new操作符的本意是分配内存。程序执行到new操作符时, 首先去看new操作符后面的类型,根据类型分配内存,再调用构造函数,填充对象的各个域,这一步就叫对象的初始化。初始化完毕后,可以把他的引用(地址)发布到外部,在外部就可以通过引用操纵这个对象。
  • 使用clone方法复制一个对象
    clone在第一步是和new相似的,都是分配内存,调用clone方法时,分配的内存和源对象一样,然后再使用源对象中对应的各个域,填充新对象的域。同样可以可以把这个新对象的引用发布到外部 。

代码:

public class Student implements Cloneable{
        int id;
        String name;
        Student(int i,String n){
            id = i;
            name = n;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException{
            return super.clone();
        }
        
        public static void main(String args[]) throws CloneNotSupportedException {
            Student s1 = new Student(111,"Karan");
            Student s2 =s1;//s2复制s1的数据
            Student s3=(Student) s1.clone();//复制s1的对象

            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s3);
        }

}

运行结果:

Student@1b6d3586
Student@1b6d3586
Student@4554617c

地址相同,那么肯定是同一个对象。s和s1只是引用而已,他们都指向了一个相同的对象Student(111,“Karan”) 。 这种现象叫做“引用的复制”.
s3是s1的复制,栈内存开辟一个新的地址

在这里插入图片描述

浅拷贝

浅拷贝(Shallow Copy of an Object)是 Java 中的“默认实现”。在重写的 clone() 方法中,如果我们没有克隆所有对象类型,下面所有的例子都只是浅拷贝,因为我们没有在 Employee 类的 clone 方法上克隆

代码:

Department 类

public class Department {
    private int DepartId;
    private String DepartName;

    public Department(int departId, String departName) {
        DepartId = departId;
        DepartName = departName;
    }

    public int getDepartId() {
        return DepartId;
    }

    public void setDepartId(int departId) {
        DepartId = departId;
    }

    public String getDepartName() {
        return DepartName;
    }

    public void setDepartName(String departName) {
        DepartName = departName;
    }
}

Employee 类

public class Employee implements Cloneable{
    private int id;
    private String name;
    private Department departemnt;

    public Employee(int id, String name, Department departemnt) {
        this.id = id;
        this.name = name;
        this.departemnt = departemnt;
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Department getDepartemnt() {
        return departemnt;
    }

    public void setDepartemnt(Department departemnt) {
        this.departemnt = departemnt;
    }

测试类:

public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Department dept= new Department(1, "HR");
        Employee original = new Employee(1, "Admin", dept);
        //创建一个原始对象的克隆
        Employee cloned = (Employee) original.clone();
        //如果克隆确实有效,使用员工ID进行验证
        System.out.println(cloned.getId());//1
        //验证JDK的规则
        // 必须为真且对象必须有不同的内存地址
        System.out.println(original != cloned);//true
        //返回如果是同一个类为ture
        System.out.println(original.getClass() == cloned.getClass());//true
        //默认equals方法检查引用,因此它应该是 false,
        System.out.println(original.equals(cloned));//false
        //说明有两个同一个对象的引用

        System.out.println("-------------------------------");
        //修改部门名称
        cloned.getDepartemnt().setDepartName("Finance");
        //查看元数据和clone后的数据是否发生改变,如果两者数据相同,说明引用的是同一个对象
        //如果在数据中clone,对clone的数据修改,元数据也会被修改,说明是浅拷贝
        //lone的数据修改,元数据也会被修改,这不是我们愿意看到的,我们想要对clone数据修改不影响元数据,这就是深拷贝
        System.out.println(original.getDepartemnt().getDepartName());
        System.out.println(cloned.getDepartemnt().getDepartName());
    }
}

运行结果:

1
true
true
false
-------------------------------
Finance
Finance

原理

浅拷贝:直接将源对象中的DepartName的引用值拷贝给新对象的DepartName字段;

在这里插入图片描述

深拷贝

深拷贝(Deep Copying)在大多数情况下,深度克隆或深度复制是所需的行为。在深拷贝中,我们创建了一个独立于原始对象的克隆,并且在克隆对象中进行更改不应影响原始对象。

代码:

Department类

public class Department implements Cloneable {
    private int DepartId;
    private String DepartName;

    public Department(int departId, String departName) {
        DepartId = departId;
        DepartName = departName;
    }
    //Department类添加clone()方法
    @Override
    protected Object clone() throws CloneNotSupportedException{
        return super.clone();
    }

    
    //其余代码相同

Employee 类

public class Employee implements Cloneable{
    private int id;
    private String name;
    private Department departemnt;

    public Employee(int id, String name, Department departemnt) {
        this.id = id;
        this.name = name;
        this.departemnt = departemnt;
    }

   //修改Employee类的clone()方法
   //    @Override
//    protected Object clone() throws CloneNotSupportedException{
//        return super.clone();
//    }
    @Override
    protected Object clone() throws CloneNotSupportedException{
        Employee cloned = (Employee) super.clone();
        cloned.setDepartemnt((Department) cloned.getDepartemnt().clone());
        return cloned;

    }
//其余代码相同

测试类:

public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Department dept= new Department(1, "HR");
        Employee original = new Employee(1, "Admin", dept);
        //创建一个原始对象的克隆
        Employee cloned = (Employee) original.clone();
        //修改部门名称
        cloned.getDepartemnt().setDepartName("Finance");
        //clone数据中部门被修改数据发生改变,但元数据没有发生变化,两者数据是独立的,这就是深拷贝
        System.out.println(original.getDepartemnt().getDepartName()); //HR
        System.out.println(cloned.getDepartemnt().getDepartName()); //Finance
    }
}

运行结果:

HR
Finance

在这里,更改克隆对象的状态不会影响原始对象。
所以深度克隆需要满足以下规则

  • 无需单独复制元数据。
  • 原始类中的所有成员类都应支持克隆,并且上下文中原始类的克隆方法应在所有成员类上调用 super.clone() 。
  • 如果某个成员类不支持克隆,那么在clone方法中,必须创建一个该成员类的新实例,并将其所有属性复制到新的成员类对象中。这个新的成员类对象将被设置在克隆对象中。

原理

在这里插入图片描述

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

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

相关文章

Python批量采集无水印短视频内容

前言 短视频流行起来可不是一年两年了,现在很多年轻人都在玩短视频,有些的单纯就是看看,而有些的就是自己发视频 我每天刷视频,一刷就停不下来,应该还是有蛮多人跟我一样的吧 那有没有想法用自己所学的python知识&a…

Linux管道——进程间通信(匿名管道、命名管道)

文章目录一、进程间通信1.1 进程间通信的概念1.2 进程间通信的目的1.3 进程间通信的本质1.4 进程间通信的分类二、管道2.1 匿名管道① 匿名管道的使用场景② 匿名管道实现通信的原理③ 创建匿名管道 pipe函数④ fork共享管道⑤ 匿名管道的五个特点⑥ 匿名管道的四种特殊情况2.2…

开发模型和测试模型,考点归纳,你都记住了吗?

目录 前言 一、开发模型 1.1、瀑布模型 1.2、螺旋模型 1.3、迭代模型和增量模型 1.4、敏捷模型 敏捷开发最流行的方式——scrum模型 二、测试模型 2.1、V模型 2.2、W模型(双V模型) 前言 对于模型,需要重点掌握特点,缺点&…

模板编程:constexpr +特例化 判断质数

重点&#xff1a; 1.constexpr 函数支持在编译期间完成计算 2.特例化是模板中一种定义 using namespace std;//编译期进行判断 constexpr bool isPrime(unsigned int p) {for (unsigned int d2;d<p/2;d){if (p % d 0){return false;}}return p > 1; }template<int…

python爬虫入门

基础知识 HTTP协议 我们浏览网页的浏览器和手机应用客户端与服务器通信几乎都是基于HTTP协议&#xff0c;而爬虫可以看作是一个另类的客户端&#xff0c;它把自己伪装成浏览器或者手机应用客户端&#xff0c;按照自己的逻辑贪婪的向服务器索取数据&#xff0c;如何向服务器索…

【实操篇】Linux的网络环境及其配置

目录 ●Linux网络环境原理图&#xff08;NAT模式&#xff09; ●虚拟网络编辑器对虚拟网卡ip进行修改&#xff08;VMnet-&#xff09; ●查看网关( VMnet8->WLAN) ●Linux网络ip配置 1.修改配置文件去获取固定ip 2.自动获取ip连接网络 ●Linux网络环境原理图&#xff…

【vue2】计算属性(computed)与侦听器(watch)详解

&#x1f973;博 主&#xff1a;初映CY的前说(前端领域) &#x1f31e;个人信条&#xff1a;想要变成得到&#xff0c;中间还有做到&#xff01; &#x1f918;本文核心&#xff1a;计算属性与侦听属性的用法 目录&#xff08;文末有给大家准备好的Xmind思维导图&#xf…

JAVA单商户商城系统源码,前(vue)后(SpringBoot)端分离,支持多平台(h5,小程序,app)

前言 完整代码下载地址&#xff1a;JAVA单商户商城系统源码 linjiashop 是一个基于Spring Boot和Vue.js的web商城系统 linjiashop 包含了商城的后台管理系统,手机h5&#xff0c;小程序版本 linjiashop 采用web-flash作为底层基础框架搭建&#xff0c;开发过程遇到问题请多阅…

Js中闭包的概念和具体使用

前言闭包在js里面是一个比较抽象的概念,但在面试里,是一个必问的话题,往往面试官希望你列举一些使用闭包的例子或手写一个闭包闭包,简单一句话讲就是能够读取其他函数内部变量的函数,当需要函数内容部的变量被外部的代码所访问时那闭包就非常有用了的,如今,很多框架里面的高级特…

Spring整合Mybatis和Junit

文章目录1 Spring整合Mybatis环境搭建整合步骤使用的注解详解2 Spring整合Junit整合Junit步骤使用的注解详解1 Spring整合Mybatis 大体需要做两件事&#xff0c; 第一件事是:Spring要管理MyBatis中的SqlSessionFactory 第二件事是:Spring要管理Mapper接口的扫描 具体该如何实现…

6.Isaac教程--在 C++ 中开发 Codelet

在 C 中开发 Codelet 本教程的目标是用 C 开发两个小码&#xff1a;第一个实际上是一台“ping”的机器&#xff0c;而第二个侦听并摄取“ping”消息。 对于本教程&#xff0c;不需要外部依赖项或特殊硬件。 文章目录在 C 中开发 Codelet创建新应用程序为应用程序创建一个新目录…

【Linux】Linux的基本指令(一)

文章目录1、ls 指令2、pwd 命令3、cd 指令4、touch 指令5、mkdir 指令6、rmdir和rm 指令7、man 指令8、cp 指令9、mv 指令10、cat11、echo(输出&#xff0c;输入&#xff0c;追加重定向)12、wc13、more14、less1、ls 指令 语法&#xff1a; ls[选项][目录或文件] 功能&#xff…

2. 矩阵(matrix)、数组、列表(list)、数据框(data.frame.....)

课程视频链接&#xff1a;https://www.bilibili.com/video/BV19x411X7C6?p1 本笔记参照该视频&#xff0c;笔记顺序做了些调整【个人感觉逻辑顺畅】&#xff0c;并删掉一些不重要的内容 系列笔记目录【持续更新】&#xff1a;https://blog.csdn.net/weixin_42214698/category_…

电脑总是开机黑屏,开机两次才能成功的解决办法:更新BIOS(七彩虹H410M-T PRO)

参考&#xff1a;七彩虹主板更新BIOS的方法 前段时间电脑出问题了&#xff0c;每当我第一次开机都会黑屏&#xff0c;要强制关机第二次开能开机&#xff0c;导致每次都开机很久很久&#xff0c;心情也不好 有时候开机等他一会&#xff0c;大概两分钟&#xff0c;会报如下错误&a…

C++课程成绩管理与分析系统[2023-01-07]

C课程成绩管理与分析系统[2023-01-07] C实习指导书 编写&#xff1a;潘林 修订&#xff1a;邓吉秋 一、实习目的 学生通过此次实习&#xff0c; 应达到如下要求&#xff1a; 熟练使用一种 C开发环境&#xff0c;包括 IDE 与编译器&#xff1b;掌握 C程序的编写 过程与调试&…

Sentinel 是什么

Sentinel是什么 Sentinel 官网&#xff1a;introduction | Sentinel 随着微服务的流行&#xff0c;服务与服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点&#xff0c;从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 Sentinel 具有以下特征 丰富…

Rancher部署分布式任务调度系统XXL-JOB通过拉取Docker公共镜像方式

通过Rancher部署分布式任务调度系统XXL-JOB我了解到有两种方式&#xff0c;一种是拉取xxl-job的源代码打包通过Jenkins部署&#xff0c;另一种简单的是直接拉取官方镜像&#xff0c;本文主要讲后面这种。 1、打开Docker公共镜像仓库&#xff0c;搜索xxl-job&#xff0c;复制镜…

GNN笔记系列 3

GNN笔记系列 21.Graph1.1Directed Graphs1.2Symmetric Graphs1.3Unweighted Graphs2.Graph Shift Operators(GSO)2.1Degree Matrix2.2Laplacian Matrix2.3Graph Shift Operator SSS3.Graph Signals4.Graph Convolutional Filters5.Time convolutions and graph convolutions6.G…

itertools包介绍——可以不用 但不能不知道——python包推荐系列

背景1 今天在看一个开源包&#xff0c;发现他的requirements.txt里面放着more_itertools包。 这个包的名字还是挺有意思的&#xff0c;在itertools包前面加上了一个more。难道是python自带的包itertools的加强版&#xff1f; 后来查了一下&#xff0c;这个包&#xff0c;果然…

高并发系统设计的15个锦囊

记得很久之前&#xff0c;去面试过字节跳动。被三面的面试官问了一道场景设计题目&#xff1a;如何设计一个高并发系统。当时我回答得比较粗糙&#xff0c;最近回想起来&#xff0c;所以整理了设计高并发系统的15个锦囊&#xff0c;相信大家看完会有帮助的。 如何理解高并发系统…