深入了解:浅拷贝深拷贝

news2025/1/8 5:49:55

浅拷贝和深拷贝

在java中 理解深拷贝和浅拷贝的概念对于处理对象复制时 保持数据的一致性和独立性至关重要  这两种拷贝方式主要区别在于它们如何处理对象内部的引用类型成员变量

在学习浅拷贝和深拷贝前 我们要区分 对象引用 和 对象本身的区别

对象引用:在Java中 对象引用是一个指向对象内存地址的变量  它本身不是对象  而是对象在内存中的位置的标识

对象本身:对象是在内存中分配的一块区域  存储了对象的实际数据和状态

在接下来的示例中 我们详细讲解

浅拷贝

浅拷贝会创建一个对象  这个新对象有着原始对象属性值的一份精确拷贝  

如果属性是基本数据类型 拷贝的就是值本身

如果这个属性是引用类型  拷贝的就是内存地址(此时便共享空间)

如果原始对象的引用类型属性被修改  浅拷贝出来的对象中的对应属性也会受到影响

赋值和浅拷贝的区别

赋值操作是将一个对象的引用(即对象的内存地址)赋值给另一个对象 这意味着两个变量现在都指向了内存的同一个对象

总结来说 浅拷贝在处理基本数据类型字段时 会为新对象分配新的内存空间来存储这些字段的副本  而对于引用类型字段  浅拷贝只是复制了引用的值(即内存地址)  而没有复制引用所指向的对象本身

在java中 实现浅拷贝的一个常用方法是使用Object类的clone方法(注意 这个方法是protected的  所以我们需要继承该类并重写clone方法 并改为public  或者使用实现了Cloneable接口的类)  但是 默认情况下 clone方法实现的是浅拷贝

示例1:

class Address {
    private String city;
    public Address(String city) {
        this.city = city;
    }
    public String getCity() {
        return city;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Person implements Cloneable {
    private Address address;

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

    public Address getAddress() {
        return address;
    }

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

public class ShallowCopy {
    public static void main(String[] args) {
        try {
            Person person1 = new Person(new Address("乌镇"));
            Person person1Copy = person1.clone();
            // true
            System.out.println(person1.getAddress() == person1Copy.getAddress());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

可能我们会思考 为什么person1和person1Copy 不能共享空间呢?

person1和person1Copy是两个不同的Person对象实例  它们不共享Person对象的内存空间

person1和person1Copy的address字段都指向同一个Address对象  这是因为浅拷贝只复制了引用   而没有复制引用的对象本身

注意:由于Address类中的city字段是String类型的  而String是不可变的  所以在这个特定的例子中  Address类的clone方法实际上并没有做太多有用的事情(除了复制引用之外)  然而 如果Address类包含其他可变字段或复杂的对象引用  那么实现深拷贝就变得重要了

示例2:

在Person类中  age和name是基本数据类型(尽管String在Java中是特殊的  但在这里它作为引用传递 但其不可变性使得它表现得像值类型)  而m是一个Money类型的对象引用

person1和person2是两个不同的Person对象实例  它们有不同的内存地址

但是 它们的m字段都指向同一个Money对象实例(浅拷贝)

深拷贝

深拷贝会创建一个新对象  并且递归地复制对象中的所有属性  包括对象中的引用类型属性  这意味着  深拷贝会创建一个全新的对象  以及对象中引用的其他对象的全新拷贝  因此原始对象和拷贝对象之间是完全独立的  互不影响

实现深拷贝的一个常用方法是通过序列化和反序列化  这种方式虽然简单  但要求对象及其所有引用的对象的必须实现Serializable接口  另一个方法是手动编写深拷贝的代码  为每个引用类型属性创建一个新的实例 并复制其内容

示例1:

class Address implements Cloneable{
    private String city;

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

    public String getCity() {
        return city;
    }

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

class Person implements Cloneable {
    private Address address;

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

    public Address getAddress() {
        return address;
    }

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

public class Test {
    public static void main(String[] args) {
        try {
            Address address1 = new Address("白城");
            Person person1 = new Person(address1);
            Person person1Copy=(Person) person1.clone();
            System.out.println(person1.getAddress()==person1Copy.getAddress());//false
            System.out.println(person1.getAddress().getCity().equals(person1Copy.getAddress().getCity()));//true
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

在这段代码中 Person类包含了一个Address类型的成员变量address 当我们调用Person类的clone方法时 首先会调用Object类的clone方法来创建一个Person对象的浅拷贝  即新对象的address成员变量会引用与原始对象相同的Address对象  

但是 随后在Person类的clone方法中 我们通过this.address.clone( )创建了Address对象的一个新实例 并将其复制给新对象的address成员变量  这样就确保了Person对象中的Address成员变量也被复制了 从而实现了深拷贝

示例2:

在这里  当clone方法调用时 person2指向tmp所指向的对象 当clone方法结束  tmp被回收  此时只有person2指向原先tmp所指的对象

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

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

相关文章

线上考试系统---虚拟化技术部署

部署前端服务器 1.将资源上传到服务器 scp -r dist/ root192.168.1.50:~2.在服务器上创建基础容器 [rootdocker0 ~]# docker run -it --name c0 centos:latest /bin/bash [rootc1092b8c9ce5 /]# [rootdocker0 ~]# [rootdocker0 ~]# docker attach c0 3.在容器中修改yum源 …

Using Managed Identity to Access Azure OpenAI Service

题意:使用托管身份访问 Azure OpenAI 服务 问题背景: Im working with Azure OpenAI service, and want to test its Managed Identity support. According to the docs it supports Managed Identity authentication, for example - using a VM. 我正…

鸿蒙界面开发(四):支付宝首页开发实战

利用了层叠布局和一个scroll滚动组件。 包括底部导航、顶部导航、主体的滚动窗口。 主体的滚动窗口又包括一个快捷导航栏和一个导航窗口、以及主体部分 import text from ohos.graphics.text;Entry Component struct Index {State message: string Hello World;//支付宝首页b…

全网最最最详细的企业级WEB应用服务器TOMCAT

目录 一:tomcat的介绍 Tomcat请求过程 实验部分: 1:安装 Tomcat 1.1:安装java环境 1.2:安装并启动tomcat 1.3: 查看端口 1.4:浏览器访问以下试试 2:tomcat的文件结构和组成 3&…

高性能web服务器4——Nginx反向代理A

Nginx 反向代理 Nginx 的反向代理功能是其最强大的特性之一,它允许 Nginx 作为中间层来接收客户端的请求,并将这些请求转发到后端服务器。这种架构不仅可以提高安全性,还可以实现负载均衡、缓存和内容过滤等功能。本文将详细介绍 Nginx 反向…

【neo4j】neo4j-Desktop安装

Neo4j是一个高性能的图数据库,它使用图形结构来存储和处理数据。它是一个开源的、完全事务的数据库,专门设计用于大规模的图形数据。Neo4j使用一种名为Cypher的查询语言来处理图形数据,使用户能够方便地进行复杂的图形查询和分析。 Neo4j的主…

AIGC 大模型:实践与未来

一、AIGC 大模型的概念与发展 (一)AIGC 大模型的定义与特点 AIGC(AI Generated Content)大模型是一种基于大规模数据训练的人工智能模型,具备强大的内容生成能力和多模态交互能力。它能够理解和处理多种类型的信息&a…

找不到工作四处闲逛github,我发现了一个找工作的神器

前言 到底是谁把我那不值钱的班给上了,博主找不到工作,天天游荡。 终于在github上发现一个找工作的神器,那就是:get_gobs 该项目支持定时投递,自动编写打招呼语,妈妈,再也不用担心我打招呼的…

简易STL实现 | List的实现

基于双向链表的数据结构 1、list的特性 双向链表:允许在序列的两端和中间 执行高效的插入和删除操作 不支持随机访问:要访问list中的元素,必须通过迭代器进行 动态内存管理: list的内部实现使用节点,每个节点都包含…

Java 入门指南:Java IO 模型

UNIX I/O 模型 根据冯.诺依曼结构,计算机结构分为 5 大部分:运算器、控制器、存储器、输入设备、输出设备。 输入设备(比如键盘)和输出设备(比如显示器)都属于外部设备。网卡、硬盘这种既可以属于输入设备…

日期反向格式化之前导零

1.问题描述 2.问题分析 为什么用yyyy年MM月dd日会报错,原因是:"前导零" 2.1前导零 前导零指的是在单个数字前面添加一个零以确保数字位数相同的过程。在日期格式化中,前导零常用于确保月份或日期总是显示为两位数字。 例…

mac苹果电脑配置Docker最新国内源

如图: 具体配置如下: {"builder": {"gc": {"defaultKeepStorage": "20GB","enabled": true}},"experimental": false,"registry-mirrors": ["https://docker.anyhub.us.kg", &…

单线程,多线程,异步,同步详解

关于异步与多线程,笔者在刚接触的时候一直存在诸多疑惑,甚至一度以为这俩概念是用来描述同一种技术在不同场景下的应用,进而导致对很多与它们相关的概念都一知半解,代码中的async/await关键词也是莫名其妙地在用。 但是在不断地接…

【解析几何笔记】8.向量的投影与内积

8. 向量的投影与内积 复习前面的知识:,若BCE三点共线,则 A E ⃗ ( 1 − s ) A B ⃗ s A C ⃗ , ( B , C , E ) μ ⇒ s μ 1 μ , 1 − s 1 1 μ \vec{AE}(1-s)\vec{AB}s\vec{AC},(B,C,E)\mu\Rightarrow s\frac{\mu}{1\mu},1-s\frac…

【案例59】WebSphere类加载跟踪开启方法

问题现象 WAS加载代码时,模块开发怀疑是WebSphere本身加载某个类的代码出现了问题。但不知道怎么排查。故寻求帮助。 问题分析 WebSphere本身是提供相关类加载跟踪的方法的。 解决方案 经过排查资料。如果实际诊断中,能够明确断定是某个类的加载出了…

MySQL集群技术详解

目录 一、MySQL在服务器中的部署方法 1.1 编译安装MySQL 1.2 部署MySQL 二、MySQL主从复制 2.1 配置master 2.2 配置slave 2.3 添加slave2 测试: 2.4 延迟复制 2.5 慢查询日志 2.6 MySQL的并行复制 2.7 MySQL主从复制原理剖析 2.8 架构缺陷 三、MySQL…

猫咪掉毛严重,新手铲屎官不知如何处理?推荐使用宠物空气净化器

把小猫接回来一起生活没几天,我就感觉好日子就到头了...猫咪掉毛怎么这么严重啊,我都不敢怎么撸它,一撸满天都是毛,轻轻一搓就是一大团。而且想到还要清理就很头疼,每天都要很多的时间搞卫生。尝试过把它的毛剪短&…

【时时三省】(C语言基础)指针进阶3

山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 一级指针的传参 示例: 这就是一个一级指针传参 思考:当一个函数的参数部分为一级指针的时候,函数能接受什么参数? 二级指针的传参 二级指针示例: pa是一级指针 p…

K8S 1.31 新功能: 跨核分发CPU

​在Kubernetes的最新版本1.31中,一个超酷的新功能,叫做CPUManager的静态策略,里面有个选项叫做distribute-cpus-across-cores。虽然这个功能现在还在测试阶段,也就是alpha版,而且默认是藏起来的,但它的目的…

Backtrader 实现和理解海龟交易法

Backtrader 实现和理解海龟交易法 1. 海龟交易的理解 (1)资金管理 海龟将总资金分为N个交易单位,每个单位即称为头寸,划分的标准主要是参考标的的波动性。 波动性用一个指标量化即真实波动幅度均值(ATR)…