Java反射复制类属性值的2种方法说明

1、通过Field方法

  • 通过Object.class 或者 object.getClass() 获取到 Class 类型,再通过 getDeclaredFields() 方法获取到 Field[] 字段列表, Field 在java.lang.reflect 包底下
     
  • 主要通过 java.lang.reflect.Field.get() 方法获取值 和 java.lang.reflect.Field.set(Object obj, Object value) 方法读取和写入值
/**
     * 复制对象字段值
     * @param source 源对象
     * @param target 目标对象
     */
    private static void copyPropertiesWithFields(Object source, Object target) {
        if (source == null || target == null) {
            return;
        }

        // 通过 getDeclaredFields() 方法获取到 source 和 target 对象的定义的字段列表
        final List<Field> sourceFields = Arrays.asList(source.getClass().getDeclaredFields());
        final List<Field> targetFields = Arrays.asList(target.getClass().getDeclaredFields());

        // 遍历 targetFields
        targetFields.forEach(targetField->{
            // 搜索 sourceFields 里字段名称一样的
            Optional<Field> optionalField = sourceFields.stream().filter(i -> i.getName().equals(targetField.getName())).findFirst();
            if(optionalField.isPresent())
            {
                Field sourceField = optionalField.get();
                try {
                    // 获取 sourceField 的值, 引用第三方包时用 'private' 和 'protected' 修饰的字段会报错
                    Object value = sourceField.get(source);
                    // 把获取的值设置到 targetField, 类型不匹配时会抛出异常
                    targetField.set(target,value);
                } catch (IllegalAccessException e) {
                    // 异常处理
                    e.printStackTrace();
                }
            }
        });
    }
  • 可能出现的情况:当 source Class 来自第三方的包, 它的字段属性的 修饰符private 或者 protected 时, 说明这个字段是私有的或者 受保护的 因此 无法外部无法直接访问字段
java.lang.IllegalAccessException: Class com.java.demo.controller.DemoController can not access a member of class org.activiti.engine.impl.persistence.entity.ExecutionEntityImpl with modifiers "protected"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
	at java.lang.reflect.Field.get(Field.java:390)
	at com.java.boot2activiti6modeletogether0617.demo.controller.DemoController.lambda$copyPropertiesWithFields$4(DemoController.java:225)
	at java.util.Arrays$ArrayList.forEach(Arrays.java:3880)
	at com.java.boot2activiti6modeletogether0617.demo.controller.DemoController.copyPropertiesWithFields(DemoController.java:217)
  • java.lang.reflect.Field :
package java.lang.reflect;

/**
 * A {@code Field} provides information about, and dynamic access to, a
 * single field of a class or an interface.  The reflected field may
 * be a class (static) field or an instance field.
 *
 * <p>A {@code Field} permits widening conversions to occur during a get or
 * set access operation, but throws an {@code IllegalArgumentException} if a
 * narrowing conversion would occur.
 *
 * @see Member
 * @see java.lang.Class
 * @see java.lang.Class#getFields()
 * @see java.lang.Class#getField(String)
 * @see java.lang.Class#getDeclaredFields()
 * @see java.lang.Class#getDeclaredField(String)
 *
 * @author Kenneth Russell
 * @author Nakul Saraiya
 */
public final
class Field extends AccessibleObject implements Member {

    private Class<?>            clazz; 
    private int                 slot; // 插槽
    private String              name; // 字段名称
    private Class<?>            type; // 字段类型
    private int                 modifiers; // 访问修饰符 
    private transient String    signature; // Generics and annotations support
    // generic info repository; lazily initialized
    private transient FieldRepository genericInfo;
    private byte[]              annotations;
    // Cached field accessor created without override
    private FieldAccessor fieldAccessor;

2、通过PropertyDescriptor方法

  • PropertyDescriptor 也是基于反射 的方式, 它会获取class的属性值以及该属性值的 getset 方法, 这两种方法都是 public 的访问权限,因此一般不会出现访问权限的异常
     
  • 主要通过java.lang.reflect.Method.invoke() 调用getset方法读取和写入值
/**
     * 复制对象字段值
     * @param source 源对象
     * @param target 目标对象
     */
    @SneakyThrows
    private static void copyPropertiesWithPropertyDescriptor(Object source, Object target) {
        if (source == null || target == null) {
            return;
        }

        // 从 source 和 target 对象获取到 PropertyDescriptor 列表
        List<PropertyDescriptor> sourcePropertyDescriptors = Arrays.asList(Introspector.getBeanInfo(source.getClass()).getPropertyDescriptors());
        List<PropertyDescriptor> targetPropertyDescriptors = Arrays.asList(Introspector.getBeanInfo(target.getClass()).getPropertyDescriptors());

        // 遍历 targetPropertyDescriptors
        targetPropertyDescriptors.forEach(targetPropertyDescriptor -> {
            // 搜索 sourcePropertyDescriptors 里名称和 targetPropertyDescriptor 一样的 PropertyDescriptor
            Optional<PropertyDescriptor> sourcePropertyDescriptorOptional = sourcePropertyDescriptors.stream().filter(i -> !i.getName().equals("class") && i.getName().equals(targetPropertyDescriptor.getName())).findFirst();
            if (sourcePropertyDescriptorOptional.isPresent()) {
                PropertyDescriptor sourcePropertyDescriptor = sourcePropertyDescriptorOptional.get();
                try {
                    // 通过 PropertyDescriptor.getReadMethod() 获取到 get方法, 再通过 invoke 方法以反射的方式获取到字段值
                    Object value = sourcePropertyDescriptor.getReadMethod().invoke(source);
                    // Object cadValue = foundPropertyDescriptor.getPropertyType().cast(value);
                    if (value != null) {
                        // 获取到 targetPropertyDescriptor 的 写方法
                        Method writeMethod = targetPropertyDescriptor.getWriteMethod();
                        if (writeMethod != null) {
                            // 以反射方式写入
                            writeMethod.invoke(target, value);
                        } else {
                            log.info(" writeMethod  " + targetPropertyDescriptor.getName() + " is null  ");
                        }
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        });

    }
方法返回类型描述
getName()java.lang.String获取属性名称,如 name
getReadMethod()java.lang.reflect.Method获取属性读方法, 如 String getName()
getWriteMethod()java.lang.reflect.Method获取属性写方法, 如 void setName(String name)
  • java.lang.reflect.Method:
package java.lang.reflect;

/**
 *
 * @see Member
 * @see java.lang.Class
 * @see java.lang.Class#getMethods()
 * @see java.lang.Class#getMethod(String, Class[])
 * @see java.lang.Class#getDeclaredMethods()
 * @see java.lang.Class#getDeclaredMethod(String, Class[])
 *
 * @author Kenneth Russell
 * @author Nakul Saraiya
 */
public final class Method extends Executable{
    private Class<?>            clazz; 
    private int                 slot; // 插槽,
    private String              name; // 方法名称
    private Class<?>            returnType; // 返回类型
    private Class<?>[]          parameterTypes; // 参数类型[]
    private Class<?>[]          exceptionTypes; // 异常类型[]
    private int                 modifiers;      // 对应修饰符如 private\protected\public
    ...
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值