Java 中使用 Gson 实现深度克隆 #什么是深克隆与浅克隆?#clone方法为什么不能直接通过某个对象实例在外部类调用?

news2024/11/15 17:25:48

🌐Gson的jar包提供到本文上方,欢迎自取🌐


前言

🌐在 Java 编程中,克隆对象是一项常见的需求,特别是在处理不可变对象、避免引用传递时,深度克隆显得尤为重要。虽然 Java 提供了 clone() 方法,但由于它的限制(如 Cloneable 接口的复杂性),实际项目中并不常用。为此,许多开发者选择使用第三方工具来实现深度克隆,其中,Gson 是一个非常流行且简洁的解决方案。

本文将介绍什么是深度克隆与浅克隆、clone()方法调用的细节问题和如何通过 Gson 实现对象的深度克隆,并分析其优缺点。


什么是深度克隆?浅克隆?

对象克隆指的是把A对象的属性值完全拷贝给B对象,也叫对象拷贝/对象复制

但是object默认的是浅克隆

不管对象内部的属性是基本数据类型还是引用数据类型,都完全拷贝过来

基本数据类型拷贝过来的是具体的数据,引用数据类型拷贝过来的是地址值。

🔴注意:浅克隆会导致引用数据类型的地址多个对象共用的情况,如果一个对象对其地址所记录的值进行了修改,另外一个对象里面的的值也会跟着一起变动,因为两个对象的地址值相同,指向相同的具体数值。

🌐clone()的问题与方法实现——以User类为例

❓问题:

在User类中重写clone()方法,不能直接在main下创建对象直接调用,至于为什么不能这样调用在寻找资料和ai后都得不到很好地理解,把原因归结与protected修饰符不能直接通过某个对象实例在外部类调用,如下图:

直接调用会出现'clone()' has protected access in 'java.lang.Object'报错

但是我换了别的方法试了一下别的方法来测试得到不一样的结果

我先在Animal类中定义了一个protected修饰的eat方法

然后在另外一个包创建一个Dog类,继承Animal,而且不对eat方法作重写

最后我们到测试类的main方法下创建一个Dog对象,并调用继承父类用protected修饰的eat()方法

最后运行发现,没有任何报错而且成功调用了继承父类用protected修饰的eat()方法

得到运行结果:


🔷思考与结论:

同样是父类其他包的子类,同样是父类用protected修饰的方法,为什么不能在main方法中被子类对象实例调用呢?

为了解决这个疑惑,我找到了clone()的源代码,发现有一个native关键字,可能是native的原因导致上述问题,如下图:

而且通过学习我们得知,想要实现JavaBean类调用clone()方法还需要实现一个Cloneable接口。

Cloneable 是一个标记接口(没有方法),它告诉 JVM 某个类是可以被克隆的

经过上述分析,个人没有找到protected的影响,但是找到了两个可能的原因就是:

  • 实现克隆调用需要实现Cloneable接口
  • 受到native关键字的影响

🔷实现:
  1. 在JavaBean类中重写Object中的clone方法
  2. 让JavaBean类实现Cloneable接口
  3. 创建对象并调用clone方法(clone返回的是Object对象,需要强制转换)

1.在JavaBean类中重写Object中的clone方法:

2.让JavaBean类实现Cloneable接口:

3.创建对象并调用clone方法(注意接收对象的时候要强转):

先点击向方法签名添加异常

这样,一个浅克隆就实现了

但是我们上面提及,浅克隆会导致引用数据类型的地址多个对象共用的情况,如果一个对象对其地址所记录的值进行了修改

代码示例:

 public static void main(String[] args) throws CloneNotSupportedException {
        //定义一个数组并赋值
        int[] arr = {1,2,3,4,5};
        //新建一个User对象
        User u1 = new User(001,"qiao","123456","kimi",arr);

        User u2 = new User();
        u2 = (User) u1.clone();

        System.out.println(u1);
        System.out.println(u2);

        int[] data = u1.getData();
        data[0] = 100;

        System.out.println(u1);//User{id=1, username='qiao', password='123456', path='kimi', data=[100, 2, 3, 4, 5]}
        System.out.println(u2);//User{id=1, username='qiao', password='123456', path='kimi', data=[100, 2, 3, 4, 5]}
    }
黑马程序员的这张图片可以帮助我们很好地理解浅克隆

而深度克隆指的是不仅复制对象本身,还复制该对象所有引用的对象,确保克隆对象与原对象之间没有共享的引用。与之相对的浅克隆,只会复制对象本身,而不会递归复制其引用的对象。

🌐通过clone()方法实现深克隆

我们需要修改一下JavaBean类中的clone()方法重写内容:

 @Override
    protected Object clone() throws CloneNotSupportedException {
        int[] data = this.data;
        int[] newData = new int[data.length];
        for (int i = 0; i < data.length; i++) {
            newData[i] = data[i];
        }
        User u = (User)super.clone();
        u.data = newData;
        return u;
    }

但是这样操作有点麻烦,所以我们会使用一个第三方工具Gson 


Gson 简介

Gson 是 Google 提供的一个用于将 Java 对象与 JSON 互相转换的库。它具有以下特点:

  1. 支持简单和复杂的对象转换。

  2. 无需配置即可处理大部分对象。

  3. 能够处理泛型类。

  4. 性能高效、使用简单。

尽管 Gson 主要用于 JSON 序列化与反序列化,但它的这一特性可以巧妙地用于对象的深度克隆。通过将对象转换为 JSON 字符串,再从 JSON 字符串中反序列化回对象,我们可以实现深度克隆。


🌐使用 Gson 实现深度克隆

使用之前先在项目目录下创建lib包,将gson的jar包放在lib包下:

然后右键该jar包,点击黄色箭头处完成导入:

接下来实现对象的深度克隆非常简单,只需以下两步:

  1. 将对象序列化为 JSON 字符串(toJson)

  2. 从 JSON 字符串反序列化为一个新的对象(fromJson)

示例代码

import com.google.gson.Gson;

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        int[] arr = {1,2,3,4,5};
        User u1 = new User(001,"zhangsan","123456","gril",arr);

        System.out.println(u1);

        //第三方深克隆工具
        Gson gson = new Gson();
        String json = gson.toJson(u1);
        User u2 = gson.fromJson(json, User.class);

        System.out.println(u1);//User{id=1, username='zhangsan', password='123456', path='gril', data=[1, 2, 3, 4, 5]}
        System.out.println(u2);//User{id=1, username='zhangsan', password='123456', path='gril', data=[1, 2, 3, 4, 5]}

        int[] data = u1.getData();
        data[0] = 100;

        System.out.println(u1);//User{id=1, username='zhangsan', password='123456', path='gril', data=[100, 2, 3, 4, 5]}
        System.out.println(u2);//User{id=1, username='zhangsan', password='123456', path='gril', data=[1, 2, 3, 4, 5]}
}

运行结果

原对象:User{id=1, username='zhangsan', password='123456', path='gril', data=[1, 2, 3, 4, 5]}
克隆对象:User{id=1, username='zhangsan', password='123456', path='gril', data=[1, 2, 3, 4, 5]}
修改后的原对象:User{id=1, username='zhangsan', password='123456', path='gril', data=[100, 2, 3, 4, 5]}
克隆对象不受影响:User{id=1, username='zhangsan', password='123456', path='gril', data=[1, 2, 3, 4, 5]}

通过输出结果可以看出,原对象和克隆对象之间没有共享引用,修改克隆对象的地址不会影响原对象,说明我们成功实现了深度克隆。


Gson 深度克隆的优缺点

🌐优点

  1. 实现简单:使用 Gson 实现深度克隆的代码非常简洁,仅需两步即可完成。

  2. 适用于复杂对象:Gson 可以处理包含嵌套对象和集合的复杂 Java 对象。

  3. 无需额外实现接口:不同于 clone() 方法,使用 Gson 不需要让类实现 Cloneable 接口,也不需要自己处理克隆逻辑。

🌐缺点

  1. 性能开销:由于 Gson 需要将对象转换为 JSON 字符串,再从 JSON 字符串中解析出对象,这个过程相较于原生的克隆操作稍慢,特别是在处理大对象时性能损耗较为明显。

  2. 不适用于所有场景:Gson 无法处理带有瞬态字段(transient)或不希望被序列化的对象,这些字段会在克隆过程中丢失。

  3. 依赖 JSON 表示:对象的所有字段都必须能够被正确转换为 JSON 格式,对于某些复杂类型或带有循环引用的对象,Gson 可能无法处理。


总结

在 Java 中,深度克隆是一个常见需求,而 Gson 提供了一种简单高效的解决方案。虽然它的主要用途是序列化与反序列化,但它也能够通过这种方式轻松实现深度克隆。对于大部分场景,Gson 是一个非常不错的选择。不过,如果你的对象过于复杂或者对性能要求较高,可能需要考虑其他克隆工具。

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

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

相关文章

【线程安全】如何区分线程安全还是线程不安全? 一文解释清楚线程安全问题,解释什么是锁重用、锁竞争、分段锁

线程安全问题 是一个重难点&#xff0c;编程就是这样&#xff0c;有的时候自己无论如何苦思冥想也弄不明白&#xff0c;但如果有人指点一二就能豁然开朗&#xff0c;希望本文可以给各位同学带来帮助 本文作者&#xff1a; csdn 孟秋与你 文章目录 如何判断一个类是否线程安全是…

抖音截流神器发布:不限量评论采集,实时推送,提升运营效率

在短视频风靡的今天&#xff0c;抖音成为品牌营销的新战场。如何在海量内容中脱颖而出&#xff0c;提升运营效率成为关键。本文将揭秘一款革命性的抖音运营工具&#xff0c;它不仅支持不限量评论采集&#xff0c;还实现了实时推送功能&#xff0c;助力运营者精准把握用户反馈&a…

解决事务提交延迟问题:Spring中的事务绑定事件监听机制解析

目录 一、背景二、事务绑定事件介绍三、事务绑定事件原理四、结语 一、背景 实际工作中碰到一个场景&#xff0c;现存系统有10w张卡需要进行换卡&#xff0c;简单来说就是为用户生成一张新卡&#xff0c;批量换卡申请需要进行审核&#xff0c;审核通过后异步进行处理。 为什么…

C++中string的使用

文章目录 string类对象的常见构造string类对象的容量操作size() / length()&#xff1a;返回字符串的长度&#xff08;字符数&#xff09;。capacity()&#xff1a;返回当前字符串分配的容量&#xff08;即在重新分配内存前可以保存的字符数&#xff09;。检查是否为空&#xf…

大数据可视化-三元图

三元图是一种用于表示三种变量之间关系的可视化工具&#xff0c;常用于化学、材料科学和地质学等领域。它的特点是将三个变量的比例关系在一个等边三角形中展示&#xff0c;使得每个点的位置代表三个变量的相对比例。 1. 结构 三个角分别表示三个变量的最大值&#xff08;通常…

Centos7.9 使用 Kubeadm 自动化部署 K8S 集群(一个脚本)

文章目录 一、环境准备1、硬件准备&#xff08;虚拟主机&#xff09;2、操作系统版本3、硬件配置4、网络 二、注意点1、主机命名格式2、网络插件 flannel 镜像拉取2.1、主机生成公私钥2.2、为啥有 Github 还用 Gitee2.3、将主机公钥添加到 Gitee2.3.1、复制主机上的公钥2.3.2、…

【C++篇】走进C++标准模板库:STL的奥秘与编程效率提升之道

文章目录 C STL 初探&#xff1a;打开标准模板库的大门前言第一章: 什么是STL&#xff1f;1.1 标准模板库简介1.2 STL的历史背景1.3 STL的组成 第二章: STL的版本与演进2.1 不同的STL版本2.2 STL的影响与重要性 第三章: 为什么学习 STL&#xff1f;3.1 从手动编写到标准化解决方…

FortiGate 防火墙 DNS 地址转换(DNS Translation)

简介 本例介绍 FortiGate 防火墙 DNS 地址转换&#xff08;DNS Translation&#xff09;配置方法。 一、 网络结构 网络结构如下图&#xff0c;PC1 连接在 FG60B 的 Internal 接口&#xff0c;FG60B 的 Wan1 接口连接 FG80CM 的 DMZ 接口&#xff0c;Wan1 接口开启 DNS 服务…

无人机之工作温度篇

无人机的工作温度是一个相对复杂的问题&#xff0c;因为它受到多种因素的影响&#xff0c;包括无人机的类型&#xff08;如民用、军用&#xff09;、设计规格、应用场景以及环境条件等。以下是对无人机工作温度范围的详细解析&#xff1a; 一、正常工作温度范围 一般来说&…

LeetcodeTop100 刷题总结(二)

LeetCode 热题 100&#xff1a;https://leetcode.cn/studyplan/top-100-liked/ 文章目录 八、二叉树94. 二叉树的中序遍历&#xff08;递归与非递归&#xff09;补充&#xff1a;144. 二叉树的前序遍历&#xff08;递归与非递归&#xff09;补充&#xff1a;145. 二叉树的后序遍…

RK3568驱动指南|第十六篇 SPI-第190章 配置模式下寄存器的配置

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

JavaEE---Spring之小练习

实现一个验证码项目 后端代码 后端代码测试 整体代码测试 输入正确的验证码 输入错误的验证码

【C++掌中宝】缺省参数的全面解析

文章目录 前言1. 什么是缺省参数&#xff1f;2. 缺省参数的分类2.1 全缺省【备胎是如何使用的&#x1f605;】2.1.1 疑难细究 2.2 半缺省2.2.1 错误用法示范2.2.2 正确用法示范2.2.3&#x1f525;实参缺省与形参缺省的混合辨析&#x1f525; 3. 缺省参数的规则和限制4. 规定必须…

Gartner发布2024年中国基础设施战略技术成熟度曲线

Gartner于近日首次发布2024年中国基础设施战略技术成熟度曲线&#xff0c;该曲线收录的21项技术主要覆盖四大领域&#xff0c;分别是&#xff1a;自主可控计划、AI 影响、运营效率以及基础设施现代化。 Gartner研究总监张吟铃表示&#xff1a;“中国市场与全球市场虽然使用的技…

[Redis][预备知识]详细讲解

目录 1.命令1.最核心的两个命令1.SET2.GET3.说明 2.基本全局命令0.前言1.KEYS2.EXISTS3.DEL4.EXPIRE5.TTL6.TYPE 2.数据结构和内部编码3.单线程架构1.单线程模型2.单线程还效率高&#xff1f;(重点)3.注意 1.命令 1.最核心的两个命令 1.SET 语法&#xff1a;SET key value功…

jmeter发送邮件:详细步骤与功能配置指南?

jmeter发送邮件如何设置&#xff1f;怎么配置JMeter以发送邮件&#xff1f; Apache JMeter作为一款强大的性能测试工具&#xff0c;不仅可以用于测试Web应用的性能&#xff0c;还可以通过插件扩展其功能&#xff0c;实现诸如发送邮件等操作。AokSend将详细介绍如何使用JMeter发…

核心复现—计及需求响应的区域综合能源系统双层优化调度策略

目录 一、主要内容&#xff1a; 二、摘要介绍&#xff1a; 三、综合能源系统结构&#xff1a; 四、实际仿真运行结果&#xff1a; 五、代码数据下载&#xff1a; 一、主要内容&#xff1a; 在模型构建部分&#xff1a;建立了一个综合能源系统双层优化调度模型&#xff0c;…

openeuler22.03 LTS 源码编译安装fastdfs-6.06

openeuler22.03 LTS 源码编译安装fastdfs-6.06 1、环境 名称版本备注openeuler22.03 LTSfastdfs6.06libfastcommon1.0.43libfastcommon是一个开源的C语言库&#xff0c;用于实现高性能的分布式文件系统和分布式存储系统 2、准备安装包 fastdfs 官网&#xff1a; https://git…

《概率论与数理统计》学渣笔记

文章目录 1 随机事件和概率1.1 古典概型求概率随机分配问题简单随机抽样问题 1.2 几何概型求概率1.3 重要公式求概率 2 一维随机变量及其分布2.1 随机变量及其分布函数的定义离散型随机变量及其概率分布&#xff08;概率分布&#xff09;连续型随机变量及其概率分布&#xff08…

【machine learning-12-多元线性回归】

线性回归-多特征 多特征线性回归多特征表示更简单的多元线性回归表示方法 之前节的线性回归为简化都是用的单特征&#xff0c;但现实中我们的预测因素很复杂不可能只有一个特征&#xff0c;下面总结多特征线性回归 多特征 之前总是用房价举例&#xff0c;预测房价和房屋面积的…