参考书籍《疯狂java》
反射的用处
反射能获取对象的属性,以及对象的属性值,甚至是私有的方法,这个一般是用在框架的,或者是权限校验,参数校验等框架的设计,反射在面试也比较常见,但是在实际的项目中,每个开发者接触的机会不多,一般都是大牛们在负责写这些,利用反射写一些通用组件等等,来解决发杂需求或者给其他开发者提供组件
提问
如何通过反射获取UpdateCheckRequestType中子对象list里面的inspectionItemId以及checkDTO中的description
public class UpdateCheckRequestType {
private CheckDTO checkDTO;
private List list;
}
public class CheckDTO {
private String id;
private String description;
public class InspectionDTO {
private String inspectionItemId;
private String inspectionValue;
首先要明确一点,在反射获取属性的过程中一定是先有class,然后才有属性,不多说上代码
public static void main(String[] args) {
// 准备数据
CheckDTO dto = new CheckDTO();
dto.setDescription("123434");
InspectionDTO inspectionDTO = new InspectionDTO();
inspectionDTO.setInspectionItemId("123234");
List list = new ArrayList<>();
list.add(inspectionDTO);
UpdateCheckRequestType type = new UpdateCheckRequestType();
type.setList(list);
type.setCheckDTO(dto);
}
有些伙伴会问,直接new UpdateCheckRequestType不就完事了吗?为啥还要给里面的子对象赋值?你如果直接new出来,没有赋值,拿到的子对象就是null,通过这个子对象去获取子对象里面的属性是获取不到的,会抛出空指针异常,还是那句话,现有class再有属性,既然class都是null,那么何来的属性呢?
// 获取type的属性
Field[] fields = type.getClass().getDeclaredFields();
System.out.println(fields[0].getName());
输出结果
checkDTO
- 这里要说下的是getDeclaredFields,getDeclaredField,getFields,getField
首先不包含s的获取的是单个属性的,需要提前知道属性的名字,而且当不存在时候会有异常抛出 - 不含有Declared的方法只能获取共有属性,而Declared可以获取私有的属性,我们实体类中的属性都是私有,当然还有getMethod等等规则和上面是一样的
从这里看到我们已经拿到了type中的子对象checkDTO,对type来说checkDTO是其中的一个属性,因此我们这里可以直接或取checkDTO,下面获取checkDTO中的Description的值
// 获取type的属性
Field[] fields = type.getClass().getDeclaredFields();
// 设置私有属性可以访问
fields[0].setAccessible(true);
// 获取checkDTO的实际对象
Object value = fields[0].get(type);
System.out.println(value.toString());
如果缺少设置私有属性允许访问这一步会抛出如下异常
Exception in thread "main" java.lang.IllegalAccessException: Class com.lightkits.mes.dto.equipment.FanShe can not access a member of class com.lightkits.mes.dto.equipment.UpdateCheckRequestType with modifiers "private"
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.lightkits.mes.dto.equipment.FanShe.main(FanShe.java:28)
输出结果如下
CheckDTO(id=null, description=123434, equipmentId=null, remark=null, collector=null, taskStatus=null, taskNum=null)
OK,离目标又进了一步,接下来反射获取description
Field[] fields1 = value.getClass().getDeclaredFields();
System.out.println(fields1[1].getName());
输出结果
description
接下来获取description的值 套路是一样的
// 获取checkDTO的实际对象
Object value = fields[0].get(type);
System.out.println(value.toString());
Field[] fields1 = value.getClass().getDeclaredFields();
System.out.println(fields1[1].getName());
fields1[1].setAccessible(true);
System.out.println(fields1[1].get(value));
值得注意的是在获取子对象的属性值的时候get里面放置的是子对象
输出结果
123434
ok 既然有get 那就必定有set
Object value = fields[0].get(type);
System.out.println(value.toString());
Field[] fields1 = value.getClass().getDeclaredFields();
System.out.println(fields1[1].getName());
fields1[1].setAccessible(true);
System.out.println(fields1[1].get(value));
fields1[1].set(value, "你好");
System.out.println(fields1[1].get(value));
输出结果对比
123434
你好
set可以用来改变属性值,这个在sqlhelp这个框架里面用到了,有兴趣的可以去了解下,总之来说get和set在反射中使用频率比较高
如果想获取list对象里面属性对象的属性值这个就和上面的套路是一样的了,直接上代码
// 获取type的属性
Field[] fields = type.getClass().getDeclaredFields();
// 设置私有属性可以访问
fields[1].setAccessible(true);
// 在强转之前需要事先预判,否则会抛出异常
List> list1 = (List>)fields[1].get(type);
Object o = list1.get(0);
Field[] fields1 = o.getClass().getDeclaredFields();
这里是拿到子对象的属性,能拿到属性,就可以解决其他问题,套路和上面是一样
在项目中的多数情况下是要去拿属性上的注解,根据属性注解去做操作,使用的方法是
Field[] fields1 = o.getClass().getDeclaredFields();
Annotation[] annotations = fields[0].getAnnotations();
System.out.println(annotations.length);
System.out.println(annotations[0].annotationType());
结果如下
1
interface javax.validation.Valid