“深入探讨Java中的对象拷贝:浅拷贝与深拷贝的差异与应用“

        前言:在Java编程中,深拷贝(Deep Copy)与浅拷贝(Shallow Copy)是两个非常重要的概念。它们涉及到对象在内存中的复制方式,对于理解对象的引用、内存管理以及数据安全都至关重要。


✨✨✨这里是秋刀鱼不做梦的BLOG

✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客

先让我们看一下本文的大致内容:

目录

1.深拷贝与浅拷贝的概念

(1)浅拷贝

(2)深拷贝

2.浅拷贝的实现

3.深拷贝的实现

4.深浅拷贝的作用

浅拷贝的作用:

深拷贝的作用:


1.深拷贝与浅拷贝的概念

        ——在了解Java中是如何实现对象的深浅拷贝之前,我们需要先了解一下什么是深拷贝、浅拷贝:

(1)浅拷贝

        在浅拷贝中,只复制对象本身,而不复制对象引用的内容。这意味着,如果对象中包含了引用类型的成员变量,那么这些成员变量的引用将会被复制,但是它们仍然指向相同的内存地址。因此,对于引用类型成员变量的修改会影响到原始对象和拷贝对象。

(2)深拷贝

        与浅拷贝不同,深拷贝会递归地复制对象及其所有引用的对象,直到所有对象都被复制到一个新的内存地址上。这样,原始对象和拷贝对象完全独立,彼此的修改不会相互影响。

        嗯嗯嗯......感觉看了和没看没什么区别,还是不太能理解到底什么是Java中的深浅拷贝,那么我们使用一个生活中的案例来解释一下:

浅拷贝的情景:

        ——如果你选择了浅拷贝,那么你会简单地把整个礼物篮进行复制,然后送给你的朋友。在这种情况下,你的朋友会得到一个看起来一模一样的礼物篮。然而,当你的朋友拆开礼物篮,他们发现里面的食品和饰品并没有改变,他们是和你的礼物篮里的相同的食品和饰品。

深拷贝的情景:

        ——相比之下,如果你选择了深拷贝,那么你会仔细地把礼物篮里的每一样东西都复制一份,然后把这些复制品装进一个新的礼物篮里,送给你的朋友。在这种情况下,你的朋友得到的是一个全新的礼物篮,里面的食品和饰品和你的礼物篮里的完全一样。但是,现在他们拥有的是独立于你的礼物篮的新的食品和饰品。

不知道上面的生活案例有没有使你更好的理解Java中的深浅拷贝,如果还是没有,那么直接往下看即可!

大致的了解了什么是Java中的深浅拷贝之后,那么我们又该如何使用代码去实现它们呢?

2.浅拷贝的实现

        在Java中,实现浅拷贝通常使用clone()方法。该方法会创建一个新对象,并将原始对象的所有字段值复制到新对象中。但是需要注意的是,对于引用类型的成员变量,仍然是浅拷贝,即复制的是引用而不是对象本身。

        下面是在Java中实现浅拷贝的详细步骤:

1.实现Cloneable接口:

class MyClass implements Cloneable {
    // 类的定义
}

 2.重写clone()方法并调用super.clone()

class MyClass implements Cloneable {
    // 类的定义

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

3.在使用时捕获CloneNotSupportedException异常:

try {
    MyClass copy = (MyClass) original.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

4.强制转换:由于clone()方法返回的是Object类型,因此在使用时需要进行类型转换。

try {
    MyClass copy = (MyClass) original.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

       

         这就是实现Java中浅拷贝的四步实现流程,相信你仔细的读完上边的代码之后,对Java中浅拷贝的实现流程已经有了初步的理解了,现在让我们使用一个完整的案例,来实现一下Java中的浅拷贝:    

class Person implements Cloneable {
    private String name;
    private Address address;

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    // Getter and setter 方法

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

class Address {
    private String city;
    
    public Address(String city) {
        this.city = city;
    }

    // Getter and setter 方法
}

public class ShallowCopyExample {
    public static void main(String[] args) {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", address);

        try {
            Person person2 = (Person) person1.clone();
            // 输出: true
            System.out.println(person1.getAddress() == person2.getAddress()); 
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

以上代码演示了一个浅拷贝的例子。让我来解释一下它的执行过程和输出:

  1. 首先,我们定义了两个类:PersonAddressPerson类有一个name属性和一个address属性,而Address类只有一个city属性。

  2. ShallowCopyExample类的main方法中,我们创建了一个Address对象,表示Alice的地址是"New York"。然后,我们创建了一个Person对象person1,传入了名字"Alice"和上面创建的Address对象。

  3. 接着,我们调用person1.clone()方法进行浅拷贝。由于Person类实现了Cloneable接口并重写了clone()方法,因此它支持克隆操作。在clone()方法内部,我们调用了super.clone()来复制Person对象本身,但是对于address属性,只是复制了其引用,而没有对Address对象进行深度复制。

  4. 输出语句System.out.println(person1.getAddress() == person2.getAddress());比较了person1person2address属性是否是同一个对象。由于浅拷贝只是复制了引用,所以person1person2address属性指向的是同一个Address对象,因此输出结果为true

这样我们就大致的了解了在Java中如何去实现对象的浅拷贝了。

3.深拷贝的实现

        在Java中实现深拷贝相对于浅拷贝来说更为复杂,因为需要确保对象及其引用的所有对象都被复制到新的内存地址上。

        下面是在Java中实现深拷贝的详细流程:

1.实现Cloneable接口:同样,为了使用clone()方法,需要确保类实现了Cloneable接口。

class MyClass implements Cloneable {
    // 类的定义
}

2.重写clone()方法:在重写的clone()方法中,除了调用super.clone()来复制对象本身之外,还需要递归地复制所有引用的对象。

class MyClass implements Cloneable {
    private AnotherClass anotherObject;

    public MyClass(AnotherClass anotherObject) {
        this.anotherObject = anotherObject;
    }

    // Getter and setter 方法

    @Override
    public Object clone() throws CloneNotSupportedException {
        MyClass clonedObject = (MyClass) super.clone();
        // 对引用类型的成员变量进行深度复制
        clonedObject.anotherObject = (AnotherClass) anotherObject.clone();
        return clonedObject;
    }
}

3.在引用类型的类中同样实现深拷贝:如果类中有成员变量是引用类型,那么需要在该引用类型的类中同样实现深拷贝。

class AnotherClass implements Cloneable {
    // 类的定义

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

4.调用clone()方法:现在可以调用clone()方法来获取深拷贝的对象。

public class DeepCopyExample {
    public static void main(String[] args) {
        AnotherClass anotherObject = new AnotherClass();
        MyClass original = new MyClass(anotherObject);
        MyClass deepCopy = null;

        try {
            deepCopy = (MyClass) original.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

        这就是实现Java中深拷贝的四步实现流程,当然,现在让我们使用一个完整的案例,来实现一下Java中的深拷贝:    

class Person implements Cloneable {
    private String name;
    private Address address;

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    // Getter and setter 方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person clonedPerson = (Person) super.clone();
        clonedPerson.address = (Address) this.address.clone();
        return clonedPerson;
    }
}

class Address implements Cloneable {
    private String city;
    
    public Address(String city) {
        this.city = city;
    }

    // Getter and setter 方法

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

public class DeepCopyExample {
    public static void main(String[] args) {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", address);

        try {
            Person person2 = (Person) person1.clone();
            // 输出: false
            System.out.println(person1.getAddress() == person2.getAddress()); 
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

让我解释一下代码的主要部分:

  1. Person 类和 Address 类都实现了 Cloneable 接口,这是为了表明它们可以被克隆。

  2. Person 类中,有一个私有字段 address,类型为 AddressPerson 类的构造函数用于初始化这个字段。

  3. Person 类的 clone() 方法首先调用了 super.clone(),这会复制 Person 对象本身。然后,它对 address 字段进行了深拷贝,即创建了一个新的 Address 对象,并将其赋值给 clonedPersonaddress 字段。

  4. Address 类中的 clone() 方法也是调用了 super.clone(),实现了浅拷贝,因为 Address 类只有一个字段,且该字段为不可变类型。

  5. main() 方法中,首先创建了一个 Address 对象和一个 Person 对象。然后,通过调用 clone() 方法,创建了一个新的 Person 对象 person2,其中包含了新的 Address 对象。

  6. 最后,通过比较 person1person2 的地址字段,可以看到它们不相同,这表明在克隆过程中进行了深拷贝。

这样我们就大致的了解了在Java中如何去实现对象的深拷贝了。

4.深浅拷贝的作用

        了解完了Java中的深浅拷贝之后,那么其有什么用呢?

浅拷贝的作用:

  1. 节省内存空间:浅拷贝只复制对象本身,不会复制对象引用的内容,因此在某些情况下可以节省内存空间。

  2. 提高对象创建速度:由于浅拷贝只复制对象本身,因此复制过程相对较快。

  3. 适用于不包含引用类型成员变量的对象:如果对象中的成员变量都是基本数据类型或者不需要被复制的对象,那么浅拷贝是一个简单有效的复制方式。

深拷贝的作用:

  1. 确保对象的独立性:深拷贝会递归地复制对象及其引用的所有对象,从而确保复制后的对象与原始对象完全独立,对复制对象的修改不会影响原始对象。

  2. 数据安全性:在多线程环境下,深拷贝可以确保对象的数据安全性,因为每个线程都可以操作独立的对象,而不会相互影响。

  3. 避免对象共享的副作用:在某些情况下,对象的共享可能会导致意外的副作用,深拷贝可以避免这种情况的发生,保证数据的一致性和可靠性。

  4. 适用于包含引用类型成员变量的对象:如果对象中包含了引用类型的成员变量,并且需要复制所有引用的对象,那么深拷贝是更合适的选择。

        总的来说,浅拷贝适用于简单对象的复制,可以提高性能和节省内存空间,而深拷贝则适用于需要确保对象独立性和数据安全性的情况,尤其是当对象包含引用类型成员变量时。


以上就是本篇文章的全部内容了~~~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

秒解-今年高考数学压轴题,你不知道有多爽!附带:计算机程序验证结果

同步的公众号文章在此,今年高考数学-压轴题 原来可以秒解啊!附带:计算机程序验证结果没错,其实高考数学-压轴题其实可以秒解的呀~https://mp.weixin.qq.com/s/4M50qP9MFwJOS9OpeyxvSg 没错,其实新课标I数学-压轴题其实…

里卡提方程(Riccati Equation)例子

里卡提方程(Riccati Equation) 里卡提方程(Riccati Equation)在人形机器人控制中有重要的应用,特别是在最优控制和估计问题中。里卡提方程主要用于求解线性二次型调节器(LQR, Linear Quadratic Regulator)和卡尔曼滤波器(Kalman Filter)。这些方法有助于提高机器人控…

RISCV中CLINT和PLIC解析

中断这个东西理论上属于CPU核心的东西。一般来说并不需要重新设计。实际的实现中是比较繁琐的,此处只介绍原理。ARM基本上会用NVIC(Nested Vectored Interrupt Controller) 的东西,RISC-V目前实现了一个比较简单的东西(有人称之为简洁高效&am…

Dubbo 3.x源码(20)—Dubbo服务引用源码(3)

基于Dubbo 3.1,详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了调用createProxy方法,根据服务引用参数map创建服务接口代理引用对象的整体流程,我们知道会调用createInvokerForRemote方法创建远程引用Invoker,这是Dubbo …

QT系列教程(10) QTextEdit学习

简介 QTextEdit是文本编辑器,支持富文本功能。接下来我们创建一个Qt Application 应用,然后在ui中添加一个QTextEdit插件。 运行程序后,可以在QTextEdit中输入任何文字也包括富文本。 文本块 我们在MainWindow的ui文件中添加了textedit插件…

算法刷题【二分法】

题目: 注意题目中说明了数据时非递减的,那么这样就存在二分性,能够实现logn的复杂度。二分法每次只能取寻找特定的某一个值,所以我们要分别求左端点和有端点。 分析第一组用例得到结果如下: 成功找到左端点8 由此可知&#xff0…

Autosar Dem配置-Condition(TRC)的使用-基于ETAS软件

文章目录 前言Dem配置DemEnableConditionDemEnableConditionIdDemEnableConditionStatus DemEnableConditionGroupDemEventParameter 接口配置代码实现总结 前言 在车辆工作状态下,每个DTC检测可能都需要一个前提条件,否则如果任何条件下都可以进行DTC检…

C# WPF入门学习主线篇(十七)—— UniformGrid布局容器

C# WPF入门学习主线篇(十七)—— UniformGrid布局容器 欢迎来到C# WPF入门学习系列的第十七篇。在前几篇文章中,我们已经探讨了 Canvas、StackPanel、WrapPanel、DockPanel 和 Grid 布局容器及其使用方法。本篇博客将介绍另一种非常实用且简单…

coap-emqx:使用libcoap与emqx通信

# emqx开启CoAP网关 请参考【https://blog.csdn.net/chenhz2284/article/details/139562749?spm1001.2014.3001.5502】 # 写一个emqx的客户端程序&#xff0c;不断地往topic【server/1】发消息 【pom.xml】 <dependency><groupId>org.springframework.boot<…

一分钟学习数据安全—自主管理身份SSI加密技术

上篇介绍了SSI的架构。架构之后&#xff0c;我们要了解一下SSI发展的驱动力&#xff1a;加密技术。现代数字通信离不开数学和计算机科学&#xff0c;加密技术也源于此。加密技术使区块链和分布式账本得以实现&#xff0c;也使SSI成为可能。 以下我们就概览一下SSI基础架构中涉及…

Python实现半双工的实时通信SSE(Server-Sent Events)

Python实现半双工的实时通信SSE&#xff08;Server-Sent Events&#xff09; 1 简介 实现实时通信一般有WebSocket、Socket.IO和SSE&#xff08;Server-Sent Events&#xff09;三种方法。WebSocket和Socket.IO是全双工的实时双向通信技术&#xff0c;适合用于聊天和会话等&a…

微信小程序学习笔记(1)

文章目录 一、文件作用app.json&#xff1a;project.config.json:sitemap.json页面中.json 二、项目首页三、语法**WXML**和**HTML**WXSS 和CSS的区别小程序中.js文件的分类 一、文件作用 app.json&#xff1a; 当前小程序的全局配置&#xff0c;包括所有页面路径、窗口外观、…

kv视频如何转码mp4格式,kv转换mp4最简单方法

在数字化时代&#xff0c;视频格式转换成为了一项日常需求。有时候我们需要把kv格式转换为MP4格式。下面将详细介绍kv转MP4的方法 方法一、 1、使用 "小白兔视频格式在线转换网站" 2、地址发给"小白兔视频格式在线转换网站"的客服&#xff0c;客服下载即可…

基于小波脊线的一维时间序列信号分解方法(MATLAB R2018A)

信号分解技术是把一个复杂信号分解为若干含有时频信息的简单信号&#xff0c;研可通过分解后的简单信号来读取和分析复杂信号的有效特征。因此&#xff0c;信号分解技术对分析结果的影响是不言而喻的。 傅里叶分解是早期常用的信号分解方法&#xff0c;最初被用于分析热过程&a…

树状数组的基础

树状数组1 树状数组可以解决什么问题呢&#xff1f; 可以解决大部分区间上面的修改以及查询的问题&#xff0c;例如1.单点修改&#xff0c;单点查询&#xff0c;2.区间修改&#xff0c;单点查询&#xff0c;3.区间查询&#xff0c;区间修改&#xff0c;换言之&#xff0c;线段…

分享一个用python写的本地WIFI密码查看器

本章教程&#xff0c;主要分享一个本地wifi密码查看器&#xff0c;用python实现的&#xff0c;感兴趣的可以试一试。 具体代码 import subprocess # 导入 subprocess 模块&#xff0c;用于执行系统命令 import tkinter as tk # 导入 tkinter 模块&#xff0c;用于创建图形用…

达梦8 开启物理逻辑日志对系统的影响

物理逻辑日志&#xff0c;是按照特定的格式存储的服务器的逻辑操作&#xff0c;专门用于 DBMS_LOGMNR 包挖掘获取数据库系统的历史执行语句。当开启记录物理逻辑日志的功能时&#xff0c;这部分日志内 容会被存储在重做日志文件中。 要开启物理逻辑日志的功能&#xff0c;需要…

如何将AndroidStudio和IDEA的包名改为分层级目录

新版UIAndroidStudio 1、点击项目目录右上角如图所示的三个点点。 2、然后依次取消Hide empty middle package &#xff0c;Flatten package的勾选 3、注意&#xff1a;一定要先取消hide的勾选&#xff0c;不然目录不会完全分级&#xff08;做错了可以反过来重新设置&#x…

VS2019专业版 C#和MFC安装

1. VS2019专业版下载地址 https://learn.microsoft.com/en-us/visualstudio/releases/2019/history 2.安装 C# 部分 MFC部分

利用SuperGlue算法实现跨尺度金字塔特征点的高效匹配(含py代码)

在计算机视觉领域&#xff0c;特征点匹配是一个基础而关键的任务&#xff0c;广泛应用于图像拼接、三维重建、目标跟踪等方向。传统的特征点匹配方法通常基于相同尺度下提取的特征进行匹配&#xff0c;然而在实际场景中&#xff0c;由于成像距离、分辨率等因素的差异&#xff0…