| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> 这样的问题代码,我实习的时候都写不出来! -> 正文阅读 |
|
[Java知识库]这样的问题代码,我实习的时候都写不出来! |
△Hollis, 一个对Coding有着独特追求的人△ 这是Hollis的第?365?篇原创分享 作者 l Hollis 来源 l Hollis(ID:hollischuang) 本文的内容是最近我刚刚遇到的一个问题,问题代码是我自己写的,也是我自己写单元测试的时候发现的,也是我自己修复的,修复完之后,我反思了一下:这样的问题代码,我实习的时候都写不出来。 可是为什么我就写出来了呢?其实还是因为有些知识没那么扎实了~就容易被忽略了,于是我在团队群里面强调了一下这个问题: 所以,本文主要是关于BeanUtils工具的属性拷贝以及深拷贝、浅拷贝等问题的。好了开始正文,介绍下问题代码是什么,为什么有问题,又符合修改? 在日常开发中,我们经常需要给对象进行赋值,通常会调用其set/get方法,有些时候,如果我们要转换的两个对象之间属性大致相同,会考虑使用属性拷贝工具进行。 如我们经常在代码中会对一个数据结构封装成DO、SDO、DTO、VO等,而这些Bean中的大部分属性都是一样的,所以使用属性拷贝类工具可以帮助我们节省大量的set和get操作。 市面上有很多类似的工具类,比较常用的有 1、Spring BeanUtils 2、Cglib BeanCopier 3、Apache BeanUtils 4、Apache PropertyUtils 5、Dozer 6、MapStucts 这里面我比较建议大家使用的是MapStructs,我在《丢弃掉那些BeanUtils工具类吧,MapStruct真香!!!》中介绍过原因。这里就不再赘述了。 最近我们有个新项目,要创建一个新的应用,因为我自己分析过这些工具的效率,也去看过他们的实现原理,比较下来之后,我觉得MapStruct是最适合我们的,于是就在代码中引入了这个框架。 另外,因为Spring的BeanUtils用起来也比较方便,所以,代码中对于需要beanCopy的地方主要在使用这两个框架。 我们一般是这样的,如果是DO和DTO/Entity之间的转换,我们统一使用MapStruct,因为他可以指定单独的Mapper,可以自定义一些策略。 如果是同对象之间的拷贝(如用一个DO创建一个新的DO),或者完全不相关的两个对象转换,则使用Spring的BeanUtils。 刚开始都没什么问题,但是后面我在写单测的时候,发现了一个问题。 问题 先来看看我们是在什么地方用的Spring的BeanUtils 我们的业务逻辑中,需要对订单信息进行修改,在更改时,不仅要更新订单的上面的属性信息,还需要创建一条变更流水。 而变更流水中同时记录了变更前和变更后的数据,所以就有了以下代码:
大致逻辑是这样的,因为创建订单变更流水的时候,需要一个改变前的订单和改变后的订单。所以我们想到了要new一个新的订单模型,然后操作新的订单模型,避免对旧的有影响。 但是,就是这个BeanUtils.copyProperties的过程其实是有问题的。 因为BeanUtils在进行属性copy的时候,本质上是浅拷贝,而不是深拷贝。 浅拷贝?深拷贝? 什么是浅拷贝和深拷贝?来看下概念。 1、浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。 2、深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。 我们举个实际例子,来看下为啥我说BeanUtils.copyProperties的过程是浅拷贝。 先来定义两个类:
然后写一段测试代码:
以上代码输出结果为:true 即,我们BeanUtils.copyProperties拷贝出来的newUser中的address对象和原来的user中的address对象是同一个对象。 可以尝试着修改下newUser中的address对象:
输出结果:
可以发现,原来的对象也受到了修改的影响。 这就是所谓的浅拷贝! 如何进行深拷贝 发现问题之后,我们就要想办法解决,那么如何实现深拷贝呢? 1、实现Cloneable接口,重写clone() 在Object类中定义了一个clone方法,这个方法其实在不重写的情况下,其实也是浅拷贝的。 如果想要实现深拷贝,就需要重写clone方法,而想要重写clone方法,就必须实现Cloneable,否则会报CloneNotSupportedException异常。 将上述代码修改下,重写clone方法:
之后,在执行一下上面的测试代码,就可以发现,这时候newUser中的address对象就是一个新的对象了。 这种方式就能实现深拷贝,但是问题是如果我们在User中有很多个对象,那么clone方法就写的很长,而且如果后面有修改,在User中新增属性,这个地方也要改。 那么,有没有什么办法可以不需要修改,一劳永逸呢? 2、序列化实现深拷贝 我们可以借助序列化来实现深拷贝。先把对象序列化成流,再从流中反序列化成对象,这样就一定是新的对象了。 序列化的方式有很多,比如我们可以使用各种JSON工具,把对象序列化成JSON字符串,然后再从字符串中反序列化成对象。 如使用fastjson实现:
也可实现深拷贝。 除此之外,还可以使用Apache Commons Lang中提供的SerializationUtils工具实现。 我们需要修改下上面的User和Address类,使他们实现Serializable接口,否则是无法进行序列化的。
然后在需要拷贝的时候:
同样,也可以实现深拷贝啦~! 总结 当我们使用各类BeanUtils的时候,一定要注意是浅拷贝还是深拷贝,浅拷贝的结果就是两个对象中的引用对象都是同一个地址,只要发生改变,都会有影响。 想要实现深拷贝,有很多种办法,其中比较常用的就是实现Cloneable接口重写clone方法,还有使用序列化+反序列化创建新对象。 好了,以上就是今天的全部内容了。 就在我编辑这篇文章的时候,公司通知杭州员工在家办公了,杭州的台风来了,希望所有人都能安安全全的! ?
最近有很多人问,有没有读者交流群,想知道怎么加入。 最近我创建了一些群,大家可以加入。交流群都是免费的,只需要大家加入之后不要随便发广告,多多交流技术就好了。 目前创建了多个交流群,全国交流群、北上广杭深等各地区交流群、面试交流群、资源共享群等。 有兴趣入群的同学,可长按扫描下方二维码,一定要备注:全国 Or 城市 Or 面试 Or 资源,根据格式备注,可更快被通过且邀请进群。 ▲长按扫描
如果你喜欢本文, 请长按二维码,关注?Hollis. 转发至朋友圈,是对我最大的支持。 点个?在看? 喜欢是一种感觉 在看是一种支持 ↘↘↘ |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年4日历 | -2025/4/12 3:19:45- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |