一切福田,不離方寸,從心而覓,感無不通。

Java中对象拷贝有哪些好用的工具类?

前言

由于在项目中经常需要使用到Java的对象拷贝和属性复制,如DTO、VO和数据库Entity之间的转换,因此本文对需要用到的相关方法、工具类做一个汇总,包括浅拷贝和深拷贝,方便在需要用到时作为参考。

浅拷贝(Shadow Copy)

手动复制

手动new对象,并设置相应字段的值,在字段较少时比较方便。另外就是由于是手动赋值,安全性较高,不容易出错,并且性能最好。

比如有如下一个类:

 

复制的时候只需简单地创建新的对象并赋值:

 

Object类的clone()方法

这个方法需要实现Cloneable接口(浅拷贝)。要实现深拷贝,如果类中的字段类型是可变类型,也需要重写可变类型的clone方法。同样以User类为例:

 

Address类:

 

使用如下:

 

Apache BeanUtils 性能较差

pom文件中引入如下依赖:

 

使用如下:

Apache PropertyUtils

PropertyUtils用法跟BeanUtils相同,这里需要注意的是PropertyUtils不支持类型转换功能。

使用BeanUtils.copyProperties方法时,BeanUtils会调用默认的转换器(Convertor),在八个基本类型间进行转换,不能转换则抛出异常。

使用PropertyUtils.copyProperties方法时,若两同名属性不是同一类型,则直接抛出 java.lang.IllegalArgumentException: argument type mismatch 异常。

我们新建一个UserDto来进行测试:

 

测试如下:

 

若UserDto类中age类型改为Integer,则不会报错,PropertyUtils会自动将int类型转为Integer。

Spring BeanUtils

spring中也有BeanUtils.copyProperties方法,这里需要注意的时入参列表跟apache的BeanUtils.copyProperties方法相反,如下所示:

 

测试如下:

 

另外spring中的BeanUtils.copyProperties方法比apache的性能要好,而且在spring项目中自带该工具类,推荐在spring项目中使用。

Cglib BeanCopier 性能较好

cglib的BeanCopier由于使用到了字节码生成技术,在运行时生成相应的字节码,而不是使用Java的反射,因此性能要比Spring的BeanUtils,Apache的BeanUtils和PropertyUtils要好,推荐使用。

使用时需要在pom文件中引入如下依赖:

 

测试如下:

这里需要注意的是如果有字段类型不同需要手动开启并指定Converter,不然同名字段属性不同不会进行拷贝,如以上例子oldUser中的age(int类型)不会拷贝到dto中的age(Long类型),dto中的age改为Integer类型也不会拷贝。

MapStruct(浅拷贝和深拷贝)性能较好

MapStruct由于是在编译时生成相应的拷贝方法,因此性能很好,理论上拷贝速度是最快的。这里注意运行前需先进行 mvn compile

pom文件中引入相应依赖:

 

创建mapper:

 

使用如下:

mapstruct默认是浅拷贝,如果需要深拷贝,需要在mapper上加注解 @Mapper(mappingControl = DeepClone.class) ,如下所示:

 

但是以上的 DeepClone.class 会导致同名字段在不同类型之间的自动转换失效,如果age从int转换为Long,会编译不通过,提示 Consider to declare/implement a mapping method: "Long map(int value)". 可自定义注解如下:

 

在mapper上加注解 @Mapper(mappingControl = CustomDeepClone.class) ,即可实现深拷贝并保证同名字段在不同类型之间的自动转换生效。

深拷贝(Deep Copy)

Java原生序列化和反序列化

类需要实现Serializable接口,如下所示:

 

将对象序列化为bytes并从bytes反序列化:

Apache SerializationUtils

apache SerializationUtils使用的也是Java原生的序列化和反序列化,来实现对象的深拷贝,因此类也需要实现Serializable接口。

pom文件中引入如下依赖:

 

使用如下:

Json序列化和反序列化

  • Gson

引入Gson依赖:

使用如下:

该方法也支持同名字段不同类型之间的转换,如将age字段在User和UserDto类中分别为int和Long,可拷贝成功。

  • Jackson

引入Jackson依赖:

 

使用如下:

 

Jackson同样支持同名字段不同类型之间的转换,由于Spring项目一般已经依赖了Jackson,推荐使用Jackson来实现对象的深拷贝。

Dozer

引入dozer依赖:

使用如下:

 

总结

以上总结了Java中进行对象属性复制、浅拷贝或深拷贝的各个方法工具类,可供使用时作为参考。至于在项目中具体使用哪个工具类,则需要根据业务情况、项目原先使用的依赖库等进行衡量,权衡性能和使用的方便性、安全性(避免出错)等,来选择合适的工具。文中有何错漏之处欢迎指出,
作者:枫葉也
链接:https://juejin.cn/post/7051166519811637278

from:https://blog.csdn.net/zybb166/article/details/122410945