Java拷贝之深拷贝与浅拷贝

2023-11-06

Java中的拷贝分为引用拷贝和对象拷贝

1、引用拷贝(浅拷贝)

引用拷贝:只会生成一个新的对象引用地址,但两个对峙最终指向的还是同一个对象
代码示例:

// 定义的测试对象
@Data
public class User {
    private String name;
    private String age;
    private String address;
    public User(String name, String age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
}

// 测试类
@Test
void testCopy() {
    User userA = new User("john", "23", "西斗门路");
    System.out.println(userA);
    User userB = userA;
    userB.setName("matt");
    System.out.println("userA:" + userA);
    System.out.println("userB:" + userB);
}

/** 执行结果
 * User(name=john, age=23, address=西斗门路)
 * userA:User(name=matt, age=23, address=西斗门路)
 * userB:User(name=matt, age=23, address=西斗门路)
 */

可以看到,在修改userB的属性后,userA的属性也一同被修改了,说明此时二者的引用对象都是同一个。

2、对象拷贝(深拷贝/浅拷贝)

对象拷贝:会重新生成一个对象,新生成的对象和之前的对象没有任何关联。
对象拷贝中,区分为两类:对象浅拷贝、对象深拷贝

2.1、对象浅拷贝

在克隆操作中国,只复制对象本身以及对象内部的基本数据类的属性,而不会复制对象内部的引用类型的属性。
浅拷贝仅仅创建了一个新的对象,该对象与原对象的引用类型属性还是指向同一地址,当应用类型发生修改时,二者都会变更。
在浅拷贝中,新对象和原始对象指向同一块内存区域,因此对其中一个对象进行修改可能会影响到另一个对象。
想要实现对象浅拷贝,可以通过实现Cloneable接口重写其clone方法
代码示例:

// 定义的测试对象
@Data
public class User implements Cloneable{
    private String name;
    private String age;
    private String address;
    private Father father;
    public User(String name, String age, String address, Father father) {
        this.name = name;
        this.age = age;
        this.address = address;
        this.father = father;
    }
    public User copyUser() throws CloneNotSupportedException {
        return (User) clone();
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
@Data
public class Father {
    private String name;
    public Father(String name) {
        this.name = name;
    }
}

@Test
void testCopy() {
    Father fatherA = new Father("William");
    User userA = new User("john", "23", "西斗门路", fatherA);
    System.out.println(userA);
    User userB = null;
    try {
        userB = userA.copyUser();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    userB.setName("matt");
    userB.getFather().setName("Billion");
    System.out.println("userA:" + userA);
    System.out.println("userB:" + userB);
}

/**
 * 执行结果
 * User(name=john, age=23, address=西斗门路, father=Father(name=William))
 * userA:User(name=john, age=23, address=西斗门路, father=Father(name=Billion))
 * userB:User(name=matt, age=23, address=西斗门路, father=Father(name=Billion))
 */

2.2、对象深拷贝

  • 深拷贝指的是在复制对象的过程中,除了复制对象及其基本类型的属性时,还要递归的复制对象内部的引用类型的属性。
  • 深拷贝会创建一个完全独立的对象, 新对象和原对象不会产生任何关联,各自的属性值修改互不影响

实现有两个方案:
1、实现Serializable接口,通过序列化方式进行克隆(下面示例使用方式)
2、重写Cloneable的clone方法,即对原对象的引用类型的属性二次调用clone方法进行克隆
代码示例:

// 创建对象,注意都需要实现Serializable接口
@Data
public class User implements Serializable {
    private String name;
    private String age;
    private String address;
    private Father father;
    public User(String name, String age, String address, Father father) {
        this.name = name;
        this.age = age;
        this.address = address;
        this.father = father;
    }
}

@Data
public class Father implements Serializable {
    private String name;
    public Father(String name) {
        this.name = name;
    }
}

// 通过序列化实现深拷贝的方法
public static <T extends Serializable> T deepCopy(T object) {
    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
        // 序列化对象
        objectOutputStream.writeObject(object);
        objectOutputStream.flush();
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
             ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)) {
            // 反序列化对象,实现深拷贝
            return (T) objectInputStream.readObject();
        }
    } catch (IOException | ClassNotFoundException e) {
        // 处理n异常
        e.printStackTrace();
    }
    // 返回null,表示深拷贝失败
    return null;
}

// 测试
@Test
void testCopy() {
    Father fatherA = new Father("William");
    User userA = new User("john", "23", "西斗门路", fatherA);
    System.out.println(userA);
    User userB = deepCopy(userA);
    userB.setName("matt");
    userB.getFather().setName("Billion");
    System.out.println("userA:" + userA);
    System.out.println("userB:" + userB);
}

/**
 * 执行结果
 * User(name=john, age=23, address=西斗门路, father=Father(name=William))
 * userA:User(name=john, age=23, address=西斗门路, father=Father(name=William))
 * userB:User(name=matt, age=23, address=西斗门路, father=Father(name=Billion))
 */

附带知识:使用try-with-resources:在Java 7及更高版本中,可以使用try-with-resources语句来自动关闭实现了Closeable接口的资源。在这段代码中,ByteArrayOutputStream、ObjectOutputStream、ByteArrayInputStream和ObjectInputStream都实现了Closeable接口,因此可以使用try-with-resources来自动关闭这些资源,而无需手动调用close方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java拷贝之深拷贝与浅拷贝 的相关文章

随机推荐

  • set常用方法及遍历方式

    set常用方法 Set 接口继承 Collection 接口 而且它不允许集合中存在重复项 所有原始方法都是现成的 没有引入新方法 具体的 Set 实现类依赖添加的对象的 equals 方法来检查等同性 public int size 返回
  • Qt之QGraphicsView进阶篇

    前言 上一章节介绍了 QGraphicsView 中的基础内容 具体请参考 Qt之QGraphicsView入门篇 这一章节我们来具体了解一下 GraphicsView 框架中有哪些特性 缩放与旋转 QGraphicsView 通过 QGr
  • 查看windows mstsc远程登陆日志(client ip)

    最近有个需求 要看一下windows MSTSC的登陆日志 测试环境 win10 参考 https social technet microsoft com Forums windows en US efabde54 be5e 4be2 b
  • [C6064]警告以及解决方法

    对于函数scanf s 当出现C6064这个警告的时候 预示着缺少一个整形参量 该参量的功能是表明输入字符的长度 可以使用sizeof strlen 等函数 例子如下 scanf s s book title sizeof book tit
  • 【Matlab智能算法】极限学习机-遗传算法(ELM-GA)函数极值寻优——非线性函数求极值

    往期博客 Matlab BP神经网络遗传算法 BP GA 函数极值寻优 非线性函数求极值 Matlab GRNN神经网络遗传算法 GRNN GA 函数极值寻优 非线性函数求极值 Matlab RBF神经网络遗传算法 RBF GA 函数极值寻
  • elementUI表格编辑状态下,如何获取修改过的行

    表格再编辑状态 需要获取到哪一行数据被修改了 面对这样一个场景 我们可以结合element的row click事件以及动态的监听 watch来实现 tableList 代表表格的数据 rowIds代表需要记录的id数组的集合 isUpdat
  • 外设驱动库开发笔记22:ADXL345三轴数字加速度计驱动

    移动设备的广泛应用增加对移动过程中各种参数的检测需求 ADXL345三轴数字加速度计可以用来检测加速度 进而测量倾斜角度等 在这一篇中 我们将讨论ADXL345三轴数字加速度计驱动程序的设计与实现 1 功能概述 ADXL345是一款小而薄的
  • EPSANet学习笔记

    EPSANet学习笔记 EPSANet An Efficient Pyramid Squeeze Attention Block on Convolutional Neural Network Abstract 最近 研究表明 在深度卷积神
  • 常用的前端排序

    冒泡排序 前一个与后一个作比较 如果前一个大于后一个 两者交换位置 对每一对相邻元素做第一步的操作 针对所有的元素重复以上的步骤 除了最后一个 let arr 1 3 4 5 8 6 4 1 3 2 for var i 0 i
  • 等保2.0四级安全要求

    第四级安全保护能力 应能够在统一安全策略下防护免受来自国家级别的 敌对组织的 拥有丰富资源的威胁源发起的恶意攻击 严重的自然灾害 以及其他相当危害程度的威胁所造成的资源损害 能够及时发现 监测发现攻击行为和安全事件 在自身遭到损害后 能够迅
  • 模板元编程-模板特例化

    在模板元编程里面 有一种情况是模板特例化 就是你定义了一个模板类 并且专门为它写了一些特例 如下 定义了一个模板类Vec template
  • Python 实战:用 Scrapyd 打造个人化的爬虫部署管理控制台

    小册介绍 Scrapyd 是业内最优秀的爬虫框架之一 Scrapy 官方出品的部署管理平台 有了它 你就可以通过 API 向指定的爬虫发起指令 并且可以通过 Web 页面来查看爬虫的运行记录与状态等信息 将爬虫部署到服务器是大部分爬虫工程师
  • Lua反编译

    前言 这个知识点的突破还是来自于一道解密题 题干只有一个bin文件 然后就要输入答案 用记事本打开 发现是一串乱码 但是仔细一瞧就能捕捉到LuaR print fact这几个关键词 在浏览器上探索了许久23333 然后就能锁定它应该是个Lu
  • Python计算机视觉第七章 图像搜索

    文章目录 7 1基于内容的图像检索 从文本挖掘中获取灵感 矢量空间模型 7 2视觉单词 创建词汇 7 3图像索引 7 3 1建立数据库 7 3 2添加图像 7 4在数据库中搜索图像 7 4 1利用索引获取候选图像 7 4 2用一幅图像进行查
  • 如何在styles.xml中使用控件自定义属性

    开发过程中 对于通用控件的属性我们习惯在styles xml中抽取出来 然后在用到的地方通过 style style Your Style 引入 可以简化代码 在Material Design风格的app里面大量使用到CarView 但是C
  • VS中新建Qt项目工程后显示无法打开源文件“QtWidgets/QApplication”的解决方案

    1 环境 VS2015 Qt 5 6 2 现象描述 在vs中新建工程后一般都会显示无法打开源文件 QtWidgets QApplication 就像这样 3 原因 新建Qt项目时VC 包含目录没有自动包含Qt所需要的头文件路径 需要手动添加
  • VSCode一些小问题及解决方式

    01 插件安装目录改换 使用Installer安装VSCode 插件的默认安装目录为 C User
  • win10 安装SVN时提示2503的错误代码

    最近给别人装了一个win10 装完之后装sql 装vs 在装svn客户端时出现了以下错误 在网上找了好久 都说是权限不足 然后 就试了个中给权限的方法 1 在svn 安装包上 右键属性 设置完全控制 没解决 2 通过win R 命令提示符
  • 7、Mac iTerm2 + Zsh 打造舒适终端

    iTerm2 Zsh 打造舒适终端 最终效果图 一 准备工作 git xcode 本文默认各位同学已经安装了git环境和xcode command line tools 遇到提示找不到git命令或需要安装command line tool的
  • Java拷贝之深拷贝与浅拷贝

    Java中的拷贝分为引用拷贝和对象拷贝 1 引用拷贝 浅拷贝 引用拷贝 只会生成一个新的对象引用地址 但两个对峙最终指向的还是同一个对象 代码示例 定义的测试对象 Data public class User private String