java对象拷贝效率_使用BeanUitls提高对象拷贝效率

对象拷贝

对象拷贝分为深拷贝和浅拷贝。根据使用场景进行不同选择。在Java中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括int、double、byte、boolean、char等简单数据类型,引用类型包括类、接口、数组等复杂类型。

深度拷贝和浅度拷贝的主要区别在于是否支持引用类型的属性拷贝,本文将探讨目前使用较多的几种对象拷贝的方案,以及其是否支持深拷贝和性能对比。

首先来创建两个测试bean

注:一定要保证有set/get方法,成员变量必须要同名

@Data

public class User1 {

String name;

String password;

String phone;

}

@Data

public class User2 {

String name;

String password;

String phone;

}

1.Spring的BeanUtils(简单易用)

org.springframework.beans.BeanUtils

BeanUtils.copyProperties(源对象,目标对象)

测试方法:

public static void main(String[] args){

User1 user1=new User1();

user1.setName("user1_name");

user1.setPassword("user1_password");

user1.setPhone("user1_phone");

User2 user2=new User2();

BeanUtils.copyProperties(user1,user2);

System.out.println(user2.toString());

}

执行结果:User2(name=user1_name, password=user1_password, phone=user1_phone)

实现原理

BeanUtils部分源码分析

public abstract class BeanUtils {

public static void copyProperties(Object source, Object target) throws BeansException {

copyProperties(source, target, (Class)null, (String[])null);

}

private static void copyProperties(Object source, Object target, @Nullable Class> editable, @Nullable String... ignoreProperties) throws BeansException {

Assert.notNull(source, "Source must not be null");

Assert.notNull(target, "Target must not be null");

Class> actualEditable = target.getClass();

if (editable != null) {

if (!editable.isInstance(target)) {

throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");

}

actualEditable = editable;

}

PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);

List ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;

PropertyDescriptor[] var7 = targetPds;

int var8 = targetPds.length;

for(int var9 = 0; var9 < var8; ++var9) {

PropertyDescriptor targetPd = var7[var9];

Method writeMethod = targetPd.getWriteMethod();

if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {

PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());

if (sourcePd != null) {

Method readMethod = sourcePd.getReadMethod();

if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {

try {

if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {

readMethod.setAccessible(true);

}

Object value = readMethod.invoke(source);

if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {

writeMethod.setAccessible(true);

}

writeMethod.invoke(target, value);

} catch (Throwable var15) {

throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var15);

}

}

}

}

}

}

``````

}

实现的方式很简单,就是对两个对象中相同名字的属性进行简单get/set,仅检查属性的可访问性。可以看到, 成员变量赋值是基于目标对象的成员列表, 并且会跳过ignore的以及在源对象中不存在的, 所以这个方法是安全的, 不会因为两个对象之间的结构差异导致错误

注:必须保证同名的两个成员变量类型相同,同名属性一个是包装类型,一个是非包装类型也是可以的

2.Apache的BeanUtils(拓展性强,相对复杂)

Apache Common BeanUtil是一个常用的在对象之间复制数据的工具类,web开发框架struts就是依赖于它进行ActionForm的创建。

org.apache.commons.beanutils.BeanUtils

BeanUtils.copyProperties(目标对象,源对象)

需要引入依赖

commons-beanutils

commons-beanutils

1.9.3

测试方法:

public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {

User1 user1=new User1();

user1.setName("user1_name");

user1.setPassword("user1_password");

user1.setPhone("user1_phone");

User2 user2=new User2();

BeanUtils.copyProperties(user2,user1);

System.out.println(user2.toString());

}

执行结果:User2(name=user1_name, password=user1_password, phone=user1_phone)

实现原理

BeanUtils部分源码分析

public class BeanUtils {

public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {

BeanUtilsBean.getInstance().copyProperties(dest, orig);

}

public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {

if (dest == null) {

throw new IllegalArgumentException("No destination bean specified");

} else if (orig == null) {

throw new IllegalArgumentException("No origin bean specified");

} else {

if (this.log.isDebugEnabled()) {

this.log.debug("BeanUtils.copyProperties(" + dest + ", " + orig + ")");

}

int i;

String name;

Object value;

if (orig instanceof DynaBean) {

DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();

for(i = 0; i < origDescriptors.length; ++i) {

name = origDescriptors[i].getName();

if (this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {

value = ((DynaBean)orig).get(name);

this.copyProperty(dest, name, value);

}

}

} else if (orig instanceof Map) {

Iterator entries = ((Map)orig).entrySet().iterator();

while(entries.hasNext()) {

Entry entry = (Entry)entries.next();

name = (String)entry.getKey();

if (this.getPropertyUtils().isWriteable(dest, name)) {

this.copyProperty(dest, name, entry.getValue());

}

}

} else {

PropertyDescriptor[] origDescriptors = this.getPropertyUtils().getPropertyDescriptors(orig);

for(i = 0; i < origDescriptors.length; ++i) {

name = origDescriptors[i].getName();

if (!"class".equals(name) && this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {

try {

value = this.getPropertyUtils().getSimpleProperty(orig, name);

this.copyProperty(dest, name, value);

} catch (NoSuchMethodException var7) {

}

}

}

}

}

}

``````

}

commons-beanutils则施加了很多的检验,包括类型的转换,甚至于还会检验对象所属的类的可访问性。BeanUtils能够顺利的完成对象属性值的复制,依赖于其对类型的识别。

除了支持基本类型以及基本类型的数组之外,还支持java.sql.Date,java.sql.Time, java.sql.TimeStamp, java.io.File, javaio.URL这些类的对象,其余一概不支持。不过可以自定义Converter。然后注册进去。在org.apache.commons.beanutils.converters包中有一系列converter类,用于不同类型之间对象的转化。主要通过注入新的类型转换器,因为默认情况下,BeanUtils对复杂对象的复制是引用

注:commons-beanutils中的装换是不支持java.util.Date的。

总结:关于bean复制,如果属性较少,建议直接写个方法完成get/set即可。如果属性较多,可以自己采用反射实现一个满足自己需要的工具类,或者使用BeanUtils类。BeanUtils是利用反射机制对JavaBean的属性进行处理。一个JavaBean通常包含了大量的属性,很多情况下,对JavaBean的处理导致大量get/set代码堆积,增加了代码长度和阅读代码的难度。由于这些BeanUtils类都是采用反射机制实现的,对程序的效率会有影响。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值