当前位置: 首页 > news >正文

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

🌐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()方法重写内容:

 @Overrideprotected 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.mrgr.cn/news/34334.html

相关文章:

  • 生产模式打包
  • Spring Boot中的自动装配机制
  • Vue Cli的配置中configureWebpack和chainWebpack的主要作用及区别是什么?
  • 【安全通信】告别信息泄露:搭建你的开源视频聊天系统briefing
  • spring中entity的作用
  • CSS3 用户界面
  • 我设置了路由器自动切换ip,这会让我的账号登录地址经常改变吗
  • 奔驰「进退」两难
  • Webpack 常见配置项
  • apply、call和bind的作用和区别
  • 装饰器模式
  • Vue使用Vue Router路由:开发单页应用
  • 【网络协议栈】传输层的意义 和 UDP协议结构的解析(内含逻辑图解通俗易懂)
  • yolo自动化项目实例解析(四)ui页面整理1 (1.85)
  • kafka负载均衡迁移(通过kafka eagle)
  • 独立站崛起:2024全球商家共谋增长新蓝图
  • Kibana中突然看不到日志ElasticSearch突然采集不到日志问题解决分析
  • Linux——虚拟机和Windows间的文件传输方式
  • 【运维监控】influxdb 2.0 + grafana 11 监控jmeter 5.6.3 性能指标(1)
  • 9.23-部署项目
  • 基于深度学习的竞争性对抗学习
  • 场景题面试题——第一篇
  • freemobus阅读笔记
  • 比亚迪技术面试(测试、测开)
  • 公测两次延期、被网易拉黑,乙游《米修斯之印》能“活”下来吗?
  • python对文件的写入和追加