当前位置:职场发展 > 我实习的时候连这么有问题的代码都写不出来!

我实习的时候连这么有问题的代码都写不出来!

  • 发布:2023-10-05 21:07

这篇文章的内容是关于我最近刚刚遇到的一个问题。问题代码是我自己写的。我在编写单元测试时发现了这一点。我也自己修好了。修复后反思:我实习的时候连这么有问题的代码都写不出来。 这篇文章的内容是关于我最近刚刚遇到的一个问题。问题代码是我自己写的。我在编写单元测试时发现了这一点。我也自己修好了。修复后我反思了这个问题:实习期间连代码都不会写。 但我为什么要写下来呢?其实就是因为有些知识不是那么扎实,很容易被忽视,所以我在团队群里强调了这个问题: 因此,本文主要讨论BeanUtils工具的属性复制、深复制、浅复制等问题。好吧,我们先从正文开始吧。我们先介绍一下问题代码是什么,为什么会出现问题,是否适合修改? 在日常开发中,我们经常需要给对象赋值,通常会调用它们的set/get方法。有时候,如果我们要转换的两个对象的属性大致相同,我们就会考虑使用属性复制工具。 例如,我们经常在代码中将一个数据结构封装成DO、SDO、DTO、VO等,而这些bean中的大部分属性都是相同的,所以使用属性复制工具可以帮助我们节省大量的设置和操作。开始操作。 市面上类似的工具有很多,比较常用的有 1. Spring BeanUtils 2.Cglib BeanCopier 3.Apache BeanUtils 4. Apache PropertyUtils 5.推土机 6. 地图结构 这里我推荐大家使用MapStructs。我在《丢弃掉那些BeanUtils工具类吧,MapStruct真香!!!》介绍过原因。这里我就不详细说了。 最近我们有一个新的项目要创建一个新的应用程序,因为我自己分析了这些工具的效率,也看到了它们的实现原理。经过比较之后,我觉得MapStruct是最适合我们的,所以我在代码中编写了这个框架,在. 另外,由于Spring的BeanUtils使用起来比较方便,所以这两个框架主要用在需要beanCopy的代码中。 我们一般使用MapStruct来进行DO和DTO/Entity之间的转换,因为它可以指定一个单独的Mapper并自定义一些策略。 如果是同一个对象之间的复制(比如用一个DO创建一个新的DO),或者是两个完全不相关的对象的转换,就使用Spring的BeanUtils。一开始没有问题,但是后来在写单个测试的时候,发现了一个问题。 问题 我们先来看看我们在哪里使用Spring的BeanUtils 在我们的业务逻辑中,我们需要修改订单信息。进行变更时,我们不仅需要更新订单的上述属性信息,还需要创建变更管道。 更改管道记录更改前和更改后的数据,因此可用以下代码: //从数据库查询当前订单并锁定 OrderDetail orderDetail = orderDetailDao.queryForLock(); //复制一个新的订单模型 OrderDetail newOrderDetail = new OrderDetail(); BeanUtils.copyProperties(orderDetail, newOrderDetail); //是的新订单模型执行修改后的逻辑操作 newOrderDetail.update(); //使用修改前的订单模型和修改后的订单模型组装订单更改流程 OrderDetailStream orderDetailStream = new OrderDetailStream(); orderDetailStream.create(orderDetail, newOrderDetail); 大致逻辑是这样的,因为创建订单变更管道时,需要一个变更前的订单和一个变更后的订单。于是我们想到创建一个新的订单模型,然后运行新的订单模型,避免影响旧的订单模型。 然而这个BeanUtils.copyProperties过程其实是有问题的。 因为BeanUtils复制属性时,本质上是浅复制,而不是深复制。 浅拷贝?深拷贝? 什么是浅拷贝和深拷贝?让我们看一下这些概念。 1.浅拷贝:按值传输基本数据类型,以及像按引用传输一样复制引用数据类型。这是一个浅拷贝。 2.深拷贝:按值传输基本数据类型,并为引用数据类型创建一个新对象并复制其内容。这是一个深拷贝。 我们通过一个实际的例子来看看为什么我说BeanUtils.copyProperties的过程是浅拷贝。 首先定义两个类:public class Address { private String 省;私人字符串城市;私有字符串区域; // 省略构造函数和 setter/getter } class User { private String name;私有字符串密码;私人地址; //省略构造函数和setter/getter } 然后编写测试代码: 用户 user = new User("Hollis", "holliscuan"); user.setAddress(new Address("浙江", "杭州", "滨江"));用户 newUser = 新用户(); BeanUtils.copyProperties(用户, newUser); System.out.println(user.getAddress() == newUser.getAddress()); 上述代码的输出结果为:true 即BeanUtils.copyProperties复制的newUser中的地址对象与原用户中的地址对象是同一个对象。 您可以尝试修改newUser中的地址对象: newUser.getAddress().setCity("上海"); System.out.println(JSON.toJSONString(用户)); System.out.println(JSON.toJSONString(newUser)); 输出结果:{"地址":{"地区":"滨江","城市":"上海","省份":"浙江"},"姓名":"霍利斯","密码":"霍利斯"} {"地址":{"地区":"滨江","城市":"上海","省份":"浙江"},"名称":"霍利斯","密码":"霍利斯创"} 可以发现,原来的对象也受到了修改的影响。 这就是所谓的浅拷贝! 如何进行深拷贝 发现问题之后,我们就要想办法解决,那么如何实现深拷贝呢? 1.实现Cloneable接口,重写clone() Object 类中定义了克隆方法。这个方法实际上是一个浅拷贝,没有重写它。 如果要实现深拷贝,就需要重写clone方法,而如果要重写clone方法,就必须实现Cloneable,否则会报CloneNotSupportedException。 修改上面的代码,重写clone方法:公共类地址实现Cloneable{私有字符串省;私人字符串城市;私有字符串区域; // 省略构造函数和 setter/getter @Override public Object clone() throws CloneNotSupportedException {       return super.clone(); 。 { 私有字符串名称;私有字符串密码;私人地址地址; //省略构造函数和setter/getter @Override protected Object clone() throws CloneNotSupportedException { User user = ( User)super.clone(); user.setAddress((地址)address.clone());返回用户; 之后执行上面的测试代码,可以发现此时newUser中的address对象是一个新的对象。 这个方法可以实现深拷贝,但是问题是如果我们User中的对象很多的话,clone方法就会很长,而且如果后面有修改,User中增加了新的属性,这个地方也需要改。 那么,有没有什么方法可以一劳永逸,无需修改呢? 2.序列化实现深拷贝 我们可以借助序列化来实现深拷贝。先把对象序列化成流,然后再从流中反序列化成对象,所以一定是一个新对象。 序列化的方法有很多种。例如,我们可以使用各种JSON工具将对象序列化为JSON字符串,然后从字符串反序列化为对象。 如果使用fastjson实现:User newUser = JSON.parseObject(JSON.toJSONString(user), User.class); 深度复制也是可能的。 此外,您还可以使用Apache Commons Lang中提供的SerializationUtils工具。 我们需要修改上面的User和Address类,让它们实现Serialized接口,否则无法序列化。 类用户实现可序列化类地址实现可序列化 然后当你需要复制时: 用户 newUser = (用户) SerializationUtils.clone(用户); 同样的,也可以实现深拷贝~! 总结 我们在使用各种BeanUtil的时候,一定要注意是浅拷贝还是深拷贝。浅拷贝的结果是两个对象中的引用对象具有相同的地址。只要有变化,就会受到影响。 实现深拷贝的方式有很多种,其中比较常用的是实现Cloneable接口并重写clone方法,以及使用序列化+反序列化的方式创建新对象。 好了,今天就这些了。

相关文章

最新资讯

热门推荐