
简单聊聊对象浅拷贝和深拷贝,真不简单!
一、摘要
上篇文章中,我们有介绍到对象属性复制相关的工具,这些工具所进行的对象拷贝,其实都是浅拷贝模式。
可能有的同学会发出疑问,什么叫浅拷贝?
我们都知道,Java 中的数据类型分为值类型(基本数据类型)和引用类型,值类型包括 byte、short、 int、long、float、double、boolean、char 等简单数据类型,引用类型包括类、接口、数组等复杂类型。
根据数据类型的不同,在进行属性值拷贝的时候,如果是值类型,复制的是属性值,如果是复杂类型,比如对象,复制的内容可能是属性对应的内存引用地址。
因此,在 Java 中对于复杂类型的数据,也分为**浅拷贝(浅克隆)与深拷贝(深克隆)**方式,区别如下:
- 浅拷贝:将原对象或原数组的引用直接赋给新对象或者新数组,新对象只是原对象的一个引用,也就是说不管新对象还是原对象,都是引用同一个对象
- 深拷贝:创建一个新的对象或者数组,将原对象的各项属性的值拷贝过来,是“值”而不是“引用”,两者对象是不一样的
对于概念的解释,可能也很难理解,下面我们简单的通过几个案例向大家介绍!
二、案例实践
2.1、浅拷贝
首先我们新建两个对象,其中User
关联Account
对象,内容如下:
使用Spring BeanUtils
工具进行对象属性复制,操作如下:
输出结果如下:
从结果上可以很明显的得出结论:当修改原始的嵌套对象Account
的属性值时,目标对象的Account
对象对应的值也跟着发生变化。
很显然,这与我们预期想要的对象属性拷贝是想违背的,我们所期待的结果是:原始对象值即使发生变化,目标对象的值也不应该发生变化!
面对这种情况,怎么处理呢?
我们可以把对象Account
单独拉出来,进行一次属性值拷贝,然后再进行封装,比如操作如下:
输出结果如下:
即使Account
对象数据发生变化,也不会改目标对象的数据,与预期结果一致!
现在的情况是User
只有一个嵌套对象Account
,假如像这样的对象有十几个呢,采用以上方式显然不可取。
这个时候深拷贝,该登场了!
2.2、深拷贝
Java 的深拷贝有两种实现方式,第一种是通过将对象序列化到临时文件,然后再通过反序列化方式,从临时文件中读取数据,操作案例如下!
首先所有的类,必须实现Serializable
接口,推荐显式定义序列化 ID。
输出结果:
通过序列化和反序列化的方式,可以实现多层复杂的对象数据拷贝。
因为涉及到需要将数据写入临时磁盘,性能可能会有所下降!
2.3、json 序列化和反序列化
对于对象深度拷贝,还有第二种方式,那就是采用 json 序列化和反序列化相关的技术来实现,同时性能也比将数据写入临时磁盘的方式要好很多,并且不需要显式实现序列化接口。
json 序列化和反序列化的底层思想是,将对象序列化成字符串;然后再将字符串通过反序列化方式成对象。
以jackson
工具库为例,具体使用方式如下!
首先导入相关的jackson
依赖包!
其次,编写统一Json
处理工具类!
最后,在相关的位置引入即可。
输出结果:
与预期一致!
三、小结
本文主要围绕对象的浅拷贝和深拷贝,从使用方面做了一次简单的内容总结。
浅拷贝下,原对象和目标对象,引用都是同一个对象,当被引用的对象数据发生变化时,相关的引用者也会跟着一起变。
深拷贝下,原对象和目标对象数据是两个完全独立的存在,相互直接不受影响。
至于当前对象数据,是应该走浅拷贝还是深拷贝模式好,完全取决于当前业务的需求,没有绝对的好或者不好!
如果当前对象需要深拷贝,推荐采用 json 序列化和反序列化的方式实现,相比通过文件写入的方式进行序列化和反序列化,操作简单且性能高!
本文内容难免有描述不对的地方,欢迎网友留言指出!
文章转载自公众号:Java极客技术
