反射简介

Java的反射( reflection )机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。(摘抄自百度百科)

咱们先弄个实体类用作测试
public class DemoModel {
    private Integer age;
    private String name;
    private Date time;
    private String[] friends;

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name + " Chen";
    }

    public Date getTime() {
        return time;
    }

    public void setTime(Date time) {
        this.time = time;
    }

    public String[] getFriends() {
        return friends;
    }

    public void setFriends(String[] friends) {
        this.friends = friends;
    }
}
暴力法设值(Field)
public static void main(String[] args) throws Exception{
	DemoModel object = new DemoModel();
	// 获取私有变量
	try {
		field = object.getClass().getDeclaredField("name");
	} catch (NoSuchFieldException ex) {
		// 子类不存在该变量那么尝试去父类获取变量
		field=object.getClass().getSuperclass().getDeclaredField("name");
    }
	// 强行授权访问私有成员变量
	f.setAccessible(true);
	f.set(object, "Jack");
	System.out.println(object.getName());// 输出 Jack
}
Field简介
  • Field 是一个类,位于 java.lang.reflect 包下。在Java反射中Field类描述的是类属性信息。
  • 主要用于获取成员变量的类型、对成员变量赋值
获取Field的4种方法
  • Class.getFields() 获取类中所有public修饰的属性,含父类属性
  • Class.getDeclaredFields() 获取类中所有的属性(public、protected、default、private),不含父类属性
  • Class.getField(String name) 获取类特定的public修饰的属性,name参数指定了属性的名称
  • Class.getDeclaredField(String name) 获取类特定的属性,name参数指定了属性的名称,不可获得父类属性
Field的常用方法
  • getName() 获得该成员变量的名称
  • getType() 获得表示该成员变量类型的Class对象
  • get(Object obj) 得指定对象obj中成员变量的值,返回值为Object型
  • set(Object obj, Object value) 将指定对象obj中成员变量的值设置为value
  • setAccessible(boolean flag) 继承自AccessibleObject类,设值是否允许通过反射访问该字段。所以它的作用就是让我们在用反射时访问私有变量。

但是这种暴力访问会破坏对象的封装性,例如上面案例中 setName 方法会将 name加上后缀 " Chen”,但是因为我们绕开了set方法,所以输出的name = “Jack”。

通过属性描述器设值(PropertyDescriptor)
public static void main(String[] args) throws Exception{
	DemoModel object = new DemoModel();
	// 获得属性描述器 如属性在父类:object.getClass().getSuperclass()
	PropertyDescriptor propertyDescriptor = new PropertyDescriptor("name", object.getClass());
	// 获得set方法
	Method setMethod = propertyDescriptor.getWriteMethod();
	// 调用指定对象set方法
	setMethod.invoke(object, "Jack");
	System.out.println(object.getName());// 输出Jack Chen
}
进阶版补充

但是有些时候我们甚至不知道需要赋值的属性类型,那怎么办呢?
看如下代码:

public static void main(String[] args) throws Exception{
    Map<String,Object> map = new HashMap<>();
    map.put("time","2020-01-01 00:00:00");
    map.put("age",18);
    map.put("name","Jack");
    map.put("friends","Jay Chou,Stephen Chow,Andy Lau,Chow Yun Fat");
    DemoModel object = new DemoModel();
    for(Map.Entry<String, Object> entry : map.entrySet()){
        String mapKey = entry.getKey();
        Object mapValue = entry.getValue();
        // 这里使用暴力访问方式也行
        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(mapKey, object.getClass());
        Method setMethod = propertyDescriptor.getWriteMethod();
        // 已知咱们都是调用set方法  且都只有一个参数 所以获取set方法的第一个参数的参数类型
        setMethod.invoke(object, getValue(mapValue,setMethod.getParameterTypes()[0]));
    }
    System.out.println(JSON.toJSONString(object)); // 输出 {"age":18,"friends":["Jay Chou","Stephen Chow","Andy Lau","Chow Yun Fat"],"name":"Jack Chen","time":1577808000000}
}



private static Object getValue(Object value,Class type){
    if (value != null){
        if (type.isAssignableFrom(String[].class))
            return toStringArray(value);
        if (type.isAssignableFrom(Integer[].class))
            return toIntegerArray(value);
        else if (type.isAssignableFrom(Integer.class) || type.isAssignableFrom(int.class) )
            return toInteger(value);
        if (type.isAssignableFrom(Double.class) || type.isAssignableFrom(double.class))
            return toDouble(value);
        else if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class))
            return toBoolean(value);
        else if (type.isAssignableFrom(String.class))
            return toString(value);
        else if (type.isAssignableFrom(Date.class))
            return toDate(value);
    }
    return null;
}

private static String[] toStringArray(Object value){
    return value.toString().split(",");
}

private static Integer[] toIntegerArray(Object value){
    String[] stringArray = toStringArray(value);
    Integer[] intArray = new Integer[stringArray.length];
    for (int i = 0; i < stringArray.length; i++){
        intArray[i] = Integer.parseInt(stringArray[i]);
    }
    return intArray;
}

private static Integer toInteger(Object value){
    return Integer.parseInt(value.toString());
}

private static Double toDouble(Object value){
    return Double.parseDouble(value.toString());
}

private static String toString(Object value){
    return value.toString();
}

private static Date toDate(Object value){
    return DateUtils.parseDate(value.toString(),DateUtils.DATE_FORMAT);
}

private static Boolean toBoolean(Object value){
    return Boolean.parseBoolean(value.toString());
}