java 深拷贝 浅拷贝 详解

在 Java 中,深拷贝浅拷贝是对象拷贝(复制)时的两个重要概念,它们决定了拷贝后的对象与原对象之间的关联性。以下是深拷贝和浅拷贝的详解,包括定义、实现方式及其区别。


1. 概念解释

1.1 浅拷贝(Shallow Copy)

浅拷贝是对对象的一种表层复制

  • 基本数据类型的字段会复制其值。
  • 引用数据类型的字段会复制其引用地址(即引用同一个对象)。

拷贝后的对象与原对象共享引用类型的成员。

特性

  • 拷贝对象与原对象的引用类型字段指向同一个内存地址。
  • 修改引用类型的内容会影响到原对象。

1.2 深拷贝(Deep Copy)

深拷贝是对对象的一种完全复制

  • 基本数据类型的字段会复制其值。
  • 引用数据类型的字段会递归拷贝新对象,即创建新的内存空间,拷贝后不共享引用。

拷贝后的对象与原对象完全独立。

特性

  • 拷贝对象与原对象互不影响,任何修改只会影响对应对象。

2. 示例代码

2.1 浅拷贝示例

使用 Object.clone() 方法实现浅拷贝:

class Person implements Cloneable {String name;Address address;Person(String name, Address address) {this.name = name;this.address = address;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone(); // 调用 Object 的 clone 方法}
}class Address {String city;Address(String city) {this.city = city;}
}public class ShallowCopyExample {public static void main(String[] args) throws CloneNotSupportedException {Address address = new Address("New York");Person person1 = new Person("John", address);// 浅拷贝Person person2 = (Person) person1.clone();// 修改拷贝对象的引用类型字段person2.address.city = "Los Angeles";// 原对象的引用类型字段也被修改System.out.println(person1.address.city); // 输出:Los Angeles}
}

2.2 深拷贝示例

手动实现深拷贝:

class Person implements Cloneable {String name;Address address;Person(String name, Address address) {this.name = name;this.address = address;}@Overrideprotected Object clone() throws CloneNotSupportedException {// 创建一个浅拷贝Person clonedPerson = (Person) super.clone();// 手动深拷贝引用类型字段clonedPerson.address = new Address(this.address.city);return clonedPerson;}
}class Address {String city;Address(String city) {this.city = city;}
}public class DeepCopyExample {public static void main(String[] args) throws CloneNotSupportedException {Address address = new Address("New York");Person person1 = new Person("John", address);// 深拷贝Person person2 = (Person) person1.clone();// 修改拷贝对象的引用类型字段person2.address.city = "Los Angeles";// 原对象的引用类型字段未被修改System.out.println(person1.address.city); // 输出:New York}
}

3. 区别对比

特性浅拷贝深拷贝
拷贝方式仅复制对象的基本类型字段和引用类型字段的引用地址。复制对象的所有字段,包括引用类型字段所指向的对象。
内存分配原对象和拷贝对象共享引用类型字段的内存地址。原对象和拷贝对象完全独立,占用不同的内存空间。
修改影响修改拷贝对象的引用类型字段会影响原对象。修改拷贝对象不会影响原对象。
实现复杂度简单,可以直接使用 Object.clone() 方法。较复杂,需要手动实现递归拷贝。
适用场景引用类型字段无需独立,或对性能要求高的场景。引用类型字段需要独立且完全隔离的场景。

4. 实现深拷贝的常用方式

4.1 使用 clone() 方法

  • 递归实现拷贝每个引用类型字段。
  • 需要确保所有类都实现 Cloneable 接口,并重写 clone() 方法。

4.2 使用序列化

通过将对象序列化为字节流再反序列化来实现深拷贝:

import java.io.*;class Person implements Serializable {String name;Address address;Person(String name, Address address) {this.name = name;this.address = address;}
}class Address implements Serializable {String city;Address(String city) {this.city = city;}
}public class DeepCopyWithSerialization {public static void main(String[] args) throws IOException, ClassNotFoundException {Address address = new Address("New York");Person person1 = new Person("John", address);// 深拷贝Person person2 = deepCopy(person1);// 修改拷贝对象的引用类型字段person2.address.city = "Los Angeles";// 原对象的引用类型字段未被修改System.out.println(person1.address.city); // 输出:New York}// 序列化深拷贝方法private static <T> T deepCopy(T object) throws IOException, ClassNotFoundException {ByteArrayOutputStream byteOut = new ByteArrayOutputStream();ObjectOutputStream out = new ObjectOutputStream(byteOut);out.writeObject(object);ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());ObjectInputStream in = new ObjectInputStream(byteIn);return (T) in.readObject();}
}

4.3 使用第三方库

一些第三方库(如 Apache Commons Lang 的 SerializationUtils)可以简化深拷贝的实现。


5. 注意事项

  1. Cloneable 接口的限制

    • 默认的 Object.clone() 方法只能实现浅拷贝,需要手动实现深拷贝。
  2. 性能考虑

    • 深拷贝通常比浅拷贝耗时更多,尤其是引用类型字段复杂时。
  3. 引用循环问题

    • 如果对象内部存在循环引用(即对象引用自身或其他对象),需要特别处理,避免递归拷贝时发生栈溢出。
  4. 使用场景选择

    • 如果引用类型字段无需独立修改,浅拷贝通常已足够。
    • 如果引用类型字段需要独立,且修改后不影响原对象,使用深拷贝。

6. 总结

浅拷贝深拷贝
仅复制值类型字段和引用地址复制值类型字段,并递归复制引用类型字段所指向的对象
性能较高,但存在引用共享的问题性能较低,但保证拷贝对象独立
适用于引用类型字段无需独立修改的场景适用于引用类型字段需要独立且完全隔离的场景

在实际应用中,应根据具体需求选择合适的拷贝方式。浅拷贝适用于简单的场景,而深拷贝更适合复杂对象需要完全隔离的场景。

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

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

相关文章

【MySQL系列】深入理解MySQL中的存储、排序字符集

前言 在创建数据库时&#xff0c;我们经常会需要填写数据库的所用字符集、排序规则&#xff0c;字符集和排序规则是两个非常重要的概念&#xff0c;它们决定了数据库如何存储和比较字符串数据。在 MySQL 中&#xff0c;常用的存储字符集有 utf8、utf8mb4&#xff0c;而排序字符…

tcp 超时计时器

在 TCP&#xff08;传输控制协议&#xff09;中有以下四种重要的计时器&#xff1a; 重传计时器&#xff08;Retransmission Timer&#xff09; 作用&#xff1a;用于处理数据包丢失的情况。当发送方发送一个数据段后&#xff0c;就会启动重传计时器。如果在计时器超时之前没有…

Docker部署ES7.9.3单节点

Elasticsearch&#xff08;简称ES&#xff09;是一个分布式、可扩展、实时的搜索与数据分析引擎&#xff01; Elasticsearch位于Elastic Stack核心&#xff0c;为所有类型的数据提供近乎实时的搜索和分析。无论是结构化或非结构化文本、数字数据还是地理空间数据&#xff0c;El…

ChromeDriver驱动下载地址更新(保持最新最全)

说明&#xff1a; ChromeDriver 是 Selenium WebDriver 用于控制 Chrome 的独立可执行文件。 为了方便下载使用&#xff0c;本文保持ChromeDriver的最新版本更新&#xff0c;并提供115.0.5763.0-133.0.6841.0版本的下载地址&#xff1a; 所有版本和下载地址&#xff1a; &am…

CSS:高级寄巧

精灵图 为什么需要精灵图呢&#xff1f; 一个网页中往往会应用很多小背景图作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁地接收和发送 请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度。 因此&#xff0c;为了有…

AutosarMCAL开发——基于EB DsAdc驱动

目录 一、旋转变压器与DsAdc原理1.常见电机角度反馈方式2.可变磁阻旋变工作原理3.使用TC3XX EDSADC进行旋变软解码 二、EB配置1.载波输出2.通道配置3.调制器4.滤波链路5.整流6.积分 三、Mcal接口应用1.AUtosar标准API接口2.应用步骤 四、总结 一、旋转变压器与DsAdc原理 1.常见…

web应用安全和信息泄露预防

文章目录 1&#xff1a;spring actuator导致的信息泄露1.1、Endpoint配置启用检测1.2、信息泄露复现1.3、防御 2&#xff1a;服务端口的合理使用3&#xff1a;弱口令&#xff08;密码&#xff09;管理4&#xff1a;服务端攻击4.1、短信业务&#xff0c;文件上传等资源型接口1、…

C语言:链表

链表是一种常见的线性数据结构&#xff0c;其中每个元素&#xff08;称为节点&#xff09;包含两部分&#xff1a;数据和指向下一个节点的指针。链表的主要优点是插入和删除操作的时间复杂度较低&#xff0c;但随机访问的效率不如数组。 1. 链表的基本概念 节点&#xff08;N…

webpack配置

4-3vue-loader测试_哔哩哔哩_bilibili 一.新建文件夹vue_todo&#xff0c;vscode打开 二.ctrl打开终端&#xff0c;输入npm init -y&#xff0c;快速生成一个默认的package.json文件 之后左边出现项目初始化文件package.json 三.接下来需要webpack完成打包&#xff0c;所以安装…

字节跳动辞退103人

大家好&#xff0c;我是程序员面试刷题平台的鸭鸭&#xff01; 在前阵子实习生破坏大模型训练事件之后&#xff0c;字节又上了一次热搜。 鸭鸭吃完瓜&#xff0c;只能说&#xff0c;社会险恶啊同学们&#xff01; 5 号&#xff0c;字节跳动内部发布了年内第四份《企业纪律与职…

大型语言模型综述 A Survey of Large Language Models

文章源自 2303.18223 (arxiv.org) 如有侵权&#xff0c;请通知下线 这是一篇关于大语言模型&#xff08;LLMs&#xff09;的综述论文&#xff0c;主要介绍了 LLMs 的发展历程、技术架构、训练方法、应用领域以及面临的挑战等方面&#xff0c;具体内容如下&#xff1a; 摘要…

服务器作业4

[rootlocalhost day04]# vim 10.sh [rootlocalhost day04]# cat 10.sh #通过shell脚本分析部署nginx网络服务 #1.接收用户部署的服务名称 read -p "服务名称:(nginx)" server if [ $server ! nginx ];then echo "输入的不是nginx,脚本退出" exit 1…

Linux基础(二十)——程序管理与 SELinux 初探

程序管理与 SELinux 初探 1. 程序和进程2.程序调用流程3. 一个bash中的多任务工作管理4.进程管理4.1 查询进程4.2 进程的执行顺序 5.系统资源的观察6. /proc/* 代表的意义7.SELinux 1. 程序和进程 2.程序调用流程 程序与进程之间的关系&#xff1a; 从上图可以看出&#xff0…

vue3 路由写法及传参方式 !超详细

Vue Router 是 Vue.js 官方的路由管理器。它主要用于单页面应用程序&#xff08;SPA, Single Page Application&#xff09;中&#xff0c;帮助解决页面导航、组件复用等问题。 基本的使用 1.router配置文件代码 创建一个ts文件,用来写路由器 // 创建一个路由器,并暴露出去 …

MATLAB绘制正四面体、正六面体

MATLAB绘制正四面体、正六面体 clc;close all;clear all;warning off;% clear all rand(seed, 100); randn(seed, 100); format long g;% 正四面体&#xff08;Tetrahedron&#xff09; % 顶点坐标&#xff08;正四面体的顶点位于一个正方体的对角线上&#xff0c;并经过适当缩…

一文了解 inductive bias(归纳偏好)

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 归纳偏好&#xff08;Inductive Bias&#xff09;是机器学习中的一个非常基础但又非常重要的概念。为了更好地理解它&#xff0c;我们先从 “归纳” 和 “偏好” 这两个词开始讲解。 什么是归纳&#x…

leetcode844:比较含退格的字符串

题干 题目分析 两个字符串要进行比较&#xff0c;#代表着回车&#xff0c;也就是删除之前的字符。 若按照遍历的惯例&#xff0c;选择从前到后遍历&#xff0c;但这样没法判断&#xff0c;因为#之前被删除的部分是不需要相同的。 因此考虑到#的含义&#xff0c;我们应该选择从…

【Python爬虫实战】从入门到精通:全面解析IP代理池的原理与实战应用

&#x1f308;个人主页&#xff1a;易辰君-CSDN博客 &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html ​ 目录 前言 一、IP代理池 &#xff08;一&#xff09;基本概念 &#xff08;二&#xff09;主要功能 &#xff08;三…