`

JAVA对象属性复制

阅读更多

        编程过程中,有些时候某些不同的类有相同的字段,可能会遇到同时给他们赋值的情况。简单的时候,可以写重复的代码,重复太多的时候,就不如给一个赋值完,直接复制给另一个对象。
        上面这种情况在数据库的实体类中比较常见。

        java对象之间属性值复制在许多开源框架中也有实现,在这里介绍自实现、Spring、apache commons-beanutils三种实现方式。

一.自己实现简单的对象属性复制
CopyUtil01.java

package com.bijian.study;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;

/*
 * 复制的对象必须同时拥有setter和getter方法,只有一个的时候会报异常,都没有的时候就不复制
 */
public class CopyUtil01 {
    
    public static void Copy(Object source, Object dest) throws Exception {
        //获取属性
        BeanInfo sourceBean = Introspector.getBeanInfo(source.getClass(), java.lang.Object.class);
        PropertyDescriptor[] sourceProperty = sourceBean.getPropertyDescriptors();

        BeanInfo destBean = Introspector.getBeanInfo(dest.getClass(), java.lang.Object.class);
        PropertyDescriptor[] destProperty = destBean.getPropertyDescriptors();

        try {
            for (int i = 0; i < sourceProperty.length; i++) {

                for (int j = 0; j < destProperty.length; j++) {

                    if (sourceProperty[i].getName().equals(destProperty[j].getName())) {
                        //调用source的getter方法和dest的setter方法
                        destProperty[j].getWriteMethod().invoke(dest, sourceProperty[i].getReadMethod().invoke(source));
                        break;
                    }
                }
            }
        } catch (Exception e) {
            throw new Exception("属性复制失败:" + e.getMessage());
        }
    }
}

 

CopyUtil02.java

package com.bijian.study;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Set;

/*
 * 该方法接收两个参数,一个是复制的源对象——要复制的对象,一个是复制的目标对象——对象副本。
 * 当然这个方法也可以在两个不同对象间使用,这时候只要目标对象和对象具有一个或多个相同类型及名称的属性,那么就会把源对象的属性值赋给目标对象的属性
 */
public class CopyUtil02 {

	public static <T> T getBean(T TargetBean, T SourceBean) throws Exception {
		if (TargetBean == null)
			return null;
		Field[] tFields = TargetBean.getClass().getDeclaredFields();
		Field[] sFields = SourceBean.getClass().getDeclaredFields();
		try {
			for (Field field : tFields) {
				String fieldName = field.getName();
				if (fieldName.equals("serialVersionUID"))
					continue;
				if (field.getType() == Map.class)
					continue;
				if (field.getType() == Set.class)
					continue;
				if (field.getType() == List.class)
					continue;
				for (Field sField : sFields) {
					if (!sField.getName().equals(fieldName)) {
						continue;
					}
					Class type = field.getType();
					String setName = getSetMethodName(fieldName);
					Method tMethod = TargetBean.getClass().getMethod(setName, new Class[] { type });
					String getName = getGetMethodName(fieldName);
					Method sMethod = SourceBean.getClass().getMethod(getName, null);
					Object setterValue = sMethod.invoke(SourceBean, null);
					tMethod.invoke(TargetBean, new Object[] { setterValue });
				}
			}
		} catch (Exception e) {
			throw new Exception("设置参数信息发生异常", e);
		}
		return TargetBean;
	}

	private static String getGetMethodName(String fieldName) {
		fieldName = replaceFirstCharToUpper(fieldName);
		return "get" + fieldName;
	}

	private static String getSetMethodName(String fieldName) {
		fieldName = replaceFirstCharToUpper(fieldName);
		return "set" + fieldName;
	}

	private static String replaceFirstCharToUpper(String fieldName) {
		char[] chars = new char[1];
		chars[0] = fieldName.charAt(0);
		String temp = new String(chars);
		if (chars[0] >= 'a' && chars[0] <= 'z') {
			fieldName = fieldName.replaceFirst(temp, temp.toUpperCase());
		}
		return fieldName;
	}
}

 

CopyUtil03.java

package com.bijian.study;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/*
 * 采用反射,通过源对象getter 方法获取属性值,并通过目标对象的setter方法设置到目标对象中去
 */
public class CopyUtil03 {

	/**
	 * 利用反射实现对象之间属性复制
	 * @param from
	 * @param to
	 */
	public static void copyProperties(Object from, Object to) throws Exception {
		copyPropertiesExclude(from, to, null);
	}
	
	/**
	 * 复制对象属性
	 * @param from
	 * @param to
	 * @param excludsArray 排除属性列表
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static void copyPropertiesExclude(Object from, Object to, String[] excludsArray) throws Exception {
		List<String> excludesList = null;
		if(excludsArray != null && excludsArray.length > 0) {
			excludesList = Arrays.asList(excludsArray);	//构造列表对象
		}
		Method[] fromMethods = from.getClass().getDeclaredMethods();
		Method[] toMethods = to.getClass().getDeclaredMethods();
		Method fromMethod = null, toMethod = null;
		String fromMethodName = null, toMethodName = null;
		for (int i = 0; i < fromMethods.length; i++) {
			fromMethod = fromMethods[i];
			fromMethodName = fromMethod.getName();
			if (!fromMethodName.contains("get"))
				continue;
			//排除列表检测
			if(excludesList != null && excludesList.contains(fromMethodName.substring(3).toLowerCase())) {
				continue;
			}
			toMethodName = "set" + fromMethodName.substring(3);
			toMethod = findMethodByName(toMethods, toMethodName);
			if (toMethod == null)
				continue;
			Object value = fromMethod.invoke(from, new Object[0]);
			if(value == null)
				continue;
			//集合类判空处理
			if(value instanceof Collection) {
				Collection newValue = (Collection)value;
				if(newValue.size() <= 0)
					continue;
			}
			toMethod.invoke(to, new Object[] {value});
		}
	}
	
	/**
	 * 对象属性值复制,仅复制指定名称的属性值
	 * @param from
	 * @param to
	 * @param includsArray
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static void copyPropertiesInclude(Object from, Object to, String[] includsArray) throws Exception {
		List<String> includesList = null;
		if(includsArray != null && includsArray.length > 0) {
			includesList = Arrays.asList(includsArray);	//构造列表对象
		} else {
			return;
		}
		Method[] fromMethods = from.getClass().getDeclaredMethods();
		Method[] toMethods = to.getClass().getDeclaredMethods();
		Method fromMethod = null, toMethod = null;
		String fromMethodName = null, toMethodName = null;
		for (int i = 0; i < fromMethods.length; i++) {
			fromMethod = fromMethods[i];
			fromMethodName = fromMethod.getName();
			if (!fromMethodName.contains("get"))
				continue;
			//排除列表检测
			String str = fromMethodName.substring(3);
			if(!includesList.contains(str.substring(0,1).toLowerCase() + str.substring(1))) {
				continue;
			}
			toMethodName = "set" + fromMethodName.substring(3);
			toMethod = findMethodByName(toMethods, toMethodName);
			if (toMethod == null)
				continue;
			Object value = fromMethod.invoke(from, new Object[0]);
			if(value == null)
				continue;
			//集合类判空处理
			if(value instanceof Collection) {
				Collection newValue = (Collection)value;
				if(newValue.size() <= 0)
					continue;
			}
			toMethod.invoke(to, new Object[] {value});
		}
	}
	
	/**
	 * 从方法数组中获取指定名称的方法
	 * 
	 * @param methods
	 * @param name
	 * @return
	 */
	public static Method findMethodByName(Method[] methods, String name) {
		for (int j = 0; j < methods.length; j++) {
			if (methods[j].getName().equals(name))
				return methods[j];
		}
		return null;
	}
}

 

O1.java

package com.bijian.study;

public class O1 {
    private String aac001;
    private String aac002;
    private Double ddd001;
    
    public String getAac001() {
            return aac001;
    }
    public void setAac001(String aac001) {
            this.aac001 = aac001;
    }
    public String getAac002() {
            return aac002;
    }
    public void setAac002(String aac002) {
            this.aac002 = aac002;
    }
    public Double getDdd001() {
            return ddd001;
    }
    public void setDdd001(Double ddd001) {
            this.ddd001 = ddd001;
    }
}

 

O2.java

package com.bijian.study;

public class O2 {
    private String aac001;
    private String aac002;
    private Double ddd001;
    private String aac003;
    private String aac004;
    public String getAac002() {
            return aac002;
    }
    public void setAac002(String aac002) {
            this.aac002 = aac002;
    }
    public String getAac001() {
            return aac001;
    }
    public void setAac001(String aac001) {
            this.aac001 = aac001;
    }
    public String getAac003() {
            return aac003;
    }
    public void setAac003(String aac003) {
            this.aac003 = aac003;
    }
    public String getAac004() {
            return aac004;
    }
    public void setAac004(String aac004) {
            this.aac004 = aac004;
    }
    public Double getDdd001() {
            return ddd001;
    }
    public void setDdd001(Double ddd001) {
            this.ddd001 = ddd001;
    }
}

 

CopyJunitTest.java

package com.bijian.study;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class CopyJunitTest {
	O1 o1 = null;
	O2 o2 = null;

	@Before
	public void setUp() {
		this.o1 = new O1();
		this.o2 = new O2();

		this.o1.setAac001("00007");
		this.o1.setAac002("国产零零零零七");
		this.o1.setDdd001(Double.valueOf(3.141592653589793D));
	}

	@Test
	public void test_copyUtil01() {
		try {
			CopyUtil01.Copy(this.o1, this.o2);
			System.out.println("CopyUtil01单元测试");

			System.out.println("-------------源对象o1----------------");
			showObjectPropertyValue(this.o1);
			System.out.println("-------------目标对象o2----------------");
			showObjectPropertyValue(this.o2);
			System.out.println();
		} catch (Exception e) {
			Assert.fail();
		}
	}

	@Test
	public void test_copyUtil02() {
		try {
			this.o2 = ((O2) CopyUtil02.getBean(this.o2, this.o1));
			System.out.println("CopyUtil02单元测试");
			System.out.println("-------------源对象o1----------------");
			showObjectPropertyValue(this.o1);
			System.out.println("-------------目标对象o2----------------");
			showObjectPropertyValue(this.o2);
			System.out.println();
		} catch (Exception e) {
			Assert.fail();
		}
	}

	@Test
	public void test_copyUtil03_copyProperties() {
		try {
			CopyUtil03.copyProperties(this.o1, this.o2);
			System.out.println("CopyUtil03的copyProperties方法单元测试");
			System.out.println("-------------源对象o1----------------");
			showObjectPropertyValue(this.o1);
			System.out.println("-------------目标对象o2----------------");
			showObjectPropertyValue(this.o2);
			System.out.println();
		} catch (Exception e) {
			Assert.fail();
		}
	}

	@Test
	public void test_copyUtil03_copyPropertiesExclude() {
		try {
			String[] excludsArray = new String[1];
			excludsArray[0] = "aac001";
			CopyUtil03.copyPropertiesExclude(this.o1, this.o2, excludsArray);
			System.out.println("CopyUtil03的copyPropertiesExclude方法单元测试");
			System.out.println("-------------源对象o1----------------");
			showObjectPropertyValue(this.o1);
			System.out.println("-------------目标对象o2----------------");
			showObjectPropertyValue(this.o2);
			System.out.println();
		} catch (Exception e) {
			Assert.fail();
		}
	}

	@Test
	public void test_copyUtil03_copyPropertiesInclude() {
		try {
			String[] includsArray = new String[2];
			includsArray[0] = "aac001";
			includsArray[1] = "ddd001";
			CopyUtil03.copyPropertiesInclude(this.o1, this.o2, includsArray);
			System.out.println("CopyUtil03的copyPropertiesInclude方法单元测试");
			System.out.println("-------------源对象o1----------------");
			showObjectPropertyValue(this.o1);
			System.out.println("-------------目标对象o2----------------");
			showObjectPropertyValue(this.o2);
		} catch (Exception e) {
			Assert.fail();
		}
	}

	private void showObjectPropertyValue(Object o) throws Exception {
		for (PropertyDescriptor property : Introspector.getBeanInfo(
				o.getClass(), Object.class).getPropertyDescriptors())
			System.out.println(property.getReadMethod()
					.invoke(o, new Object[0]));
	}
}

 单元测试输出结果:

CopyUtil01单元测试
-------------源对象o1----------------
00007
国产零零零零七
3.141592653589793
null
-------------目标对象o2----------------
00007
国产零零零零七
null
null
3.141592653589793

CopyUtil02单元测试
-------------源对象o1----------------
00007
国产零零零零七
3.141592653589793
null
-------------目标对象o2----------------
00007
国产零零零零七
null
null
3.141592653589793

CopyUtil03的copyProperties方法单元测试
-------------源对象o1----------------
00007
国产零零零零七
3.141592653589793
null
-------------目标对象o2----------------
00007
国产零零零零七
null
null
3.141592653589793

CopyUtil03的copyPropertiesExclude方法单元测试
-------------源对象o1----------------
00007
国产零零零零七
3.141592653589793
null
-------------目标对象o2----------------
null
国产零零零零七
null
null
3.141592653589793

CopyUtil03的copyPropertiesInclude方法单元测试
-------------源对象o1----------------
00007
国产零零零零七
3.141592653589793
null
-------------目标对象o2----------------
00007
null
null
null
3.141592653589793
    如上方式都是通过java的反射机制复制对象,不支持深度复制以及复制集合类型,但通用性会提高很多。java本身提供了对象复制的能力,在java.lang.Object类中有clone方法,该方法是一个protected方法,在子类需要重写此方法并声明为public类型,而且还需实现Cloneable接口才能提供对象复制的能力,clone()是一个native方法,native方法的效率一般来说都是远高于java中的非native方法,对性能比较关心的话应考虑这种方式。
 
二.利用Spring实现属性之间的复制
    spring内部自有实现方法,如果我们需要在外面采用spring的托管复制,需要修改spring的源码,将spring中的org.springframework.beans.CachedIntrospectionResults类的forClass、getPropertyDescriptor、getBeanInfo改为可见的后重新打包。然后将Spring中关于复制的代码提取出来,最后修改成代码如下:
/**
 * 利用spring实现bean之间属性复制
 * @param source
 * @param target
 */
@SuppressWarnings("unchecked")
public static void copyPropertiesBySpring(Object source, Object target) throws Exception {
	Class actualEditable = target.getClass();
	PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
	for (int i = 0; i < targetPds.length; i++) {
		PropertyDescriptor targetPd = targetPds[i];
		if (targetPd.getWriteMethod() != null) {
			PropertyDescriptor sourcePd = getPropertyDescriptor(source
					.getClass(), targetPd.getName());
			if (sourcePd != null && sourcePd.getReadMethod() != null) {
				try {
					Method readMethod = sourcePd.getReadMethod();
					if (!Modifier.isPublic(readMethod.getDeclaringClass()
							.getModifiers())) {
						readMethod.setAccessible(true);
					}
					Object value = readMethod.invoke(source, new Object[0]);
					if(value == null)
						continue;
					//集合类判空处理
					if(value instanceof Collection) {
//							Collection newValue = (Collection)value;
//							if(newValue.size() <= 0)
							continue;
					}
					Method writeMethod = targetPd.getWriteMethod();
					if (!Modifier.isPublic(writeMethod.getDeclaringClass()
							.getModifiers())) {
						writeMethod.setAccessible(true);
					}
					writeMethod.invoke(target, new Object[] { value });
				} catch (Throwable ex) {
				}
			}
		}
	}
}
	
/**
 * 获取指定类指定名称的属性描述符
 * @param clazz
 * @param propertyName
 * @return
 * @throws BeansException
 */
@SuppressWarnings("unchecked")
public static PropertyDescriptor getPropertyDescriptor(Class clazz,
		String propertyName) throws BeansException {
	CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
	return cr.getPropertyDescriptor(propertyName);
}

/**
 * 获取指定类得所有属性描述符
 * @param clazz
 * @return
 * @throws BeansException
 */
@SuppressWarnings("unchecked")
public static PropertyDescriptor[] getPropertyDescriptors(Class clazz) throws BeansException {
	CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
	return cr.getBeanInfo().getPropertyDescriptors();
}
 
三.利用apache commons-beanutils的开源实现
    BeanUtils.copyProperties(dst, src)。方法能够将源对象和目标对象中相同名称的属性值复制过去。注意的是参数前面的是目标对象,后面是源对象。使用该方法需要注意:不能将入口方法与源对象、目标对象之一放在同一源文件之内,否者将没有任何效果。
    PropertyUtils.copyProperties(dst, src)。功能与BeanUtils.copyProperties类似,只是在同属性名称的类型参数之间可以执行转换操作。
 
分享到:
评论
1 楼 qiuboboy 2014-05-14  
看下我实现的这种呢

http://bohr.me/2014/05/13/pojo-perperty-copy.html

相关推荐

    java对象复制克隆

    深度复制Java对象实例,复制后对象属性值改变不影响被复制对象,有注释

    MyBatisDemo && JAVA把一个对象的全部属性复制到另一个相同的对象

    NULL 博文链接:https://ollevere.iteye.com/blog/1323205

    对象属性值的复制工具类

    对象属性值的复制工具类 1.全复制 2.部分复制 3.部分不复制

    两个对象属性值的拷贝和文件的拷贝

    实现了两个对象之间属性值的拷贝,只要具有相同的属性名就可以拷贝,还有两个file对象的拷贝,实现文件的复制功能

    java 对象克隆

    对象的克隆操作过程,展示了对象中数据字段是如何是实现克隆的

    Java copy对象的工具类

    Java CopyUtil工具类,可以进行对象的深copy,比如:对象里面包含对象,对象里面包含Map,List,Set...等复杂类型的属性都可以copy,copy后的对象与原有对象没有联系,即改变原有对象内容,不会改变copy后的对象里面的...

    idea插件实现自动set属性拷贝

    Student student=new Student(); (Student2)student //在这个表达式(记得光标在表达式里面哦)按下alt+Enter,选中“哟嚯。。。好爽啊”就能自动生成下面代码 Student2 student2=new Student2();...

    java源码包---java 源码 大量 实例

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    第4章-Java面向对象程序设计-Java语言GUI程序设计-赵满来-清华大学出版社.pptx

    例4.2 TwoInt类中有两个int类型的属性,编写方法swap交换该类对象的两个属性的值,并显示交换前后形参引用对象和实参引用对象的属性值。 第4章-Java面向对象程序设计-Java语言GUI程序设计-赵满来-清华大学出版社...

    java源码包4

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    Java开发技术大全(500个源代码).

    CopyFile.java 文件复制实例 dir.java 显示目录下的所有文件和目录 encrypt.java 文件加密 FileList.java 自己实现的一个文件名称枚举类 MyDataIO.java 数据输入输出示例 MyFileOutput.java 将键盘读入的文字...

    JAVA上百实例源码以及开源项目

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java源码包3

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    JAVA上百实例源码以及开源项目源代码

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    计算机Java、网络面试资料

    由于Java不支持多继承,而有可能某个类或对象要使用分别在几个类或对象里面的方法或属性,现有的单继承机制就不能满足要求。与继承相比,接口有更高的灵活性,因为接口中没有任何实现代码。当一个类实现了接口以后,...

    java源码包2

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    Java数据编程指南

    目录服务与JNDI 命名与目录服务 使用JNDI JAVA与LDAP 从理论到实践 标准的LDAP操作 LDAP服务器改进 在LDAP中存储并检索Java对象 其他目录服务 小结 第三部分 网络客户机上的数据显示...

    疯狂JAVA讲义

    学生提问:构造器是创建Java对象的途径,是不是说构造器完全负责创建Java对象? 141 5.5.2 构造器的重载 142 学生提问:为什么要用this来调用另一个重载的构造器?我把另一个构造器里的代码复制、粘贴到这个构造器...

    java 编程入门思考

    A.1.3 传递和使用Java对象 A.1.4 JNI和Java违例 A.1.5 JNI和线程处理 A.1.6 使用现成代码 A.2 微软的解决方案 A.3 J/Direct A.3.1 @dll.import引导命令 A.3.2 com.ms.win32包 A.3.3 汇集 A.3.4 编写回调函数 A.3.5 ...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    密钥 Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存...

Global site tag (gtag.js) - Google Analytics