目录
什么是反射?
今天实习的时候,因为我需要预留一个回调地址接收别人的处理结果,但是结果集有点复杂,一层套一层,所以我一直纠结于该用什么对象去接收,怎么定义成员变量。绕了半天别人说不用在乎这些,问我知不知道什么是反射,我脑袋里似乎想到了什么但又不是很明白所以当时搪塞了过去,下来准备去看看反射。
最初接触Java的时候包括到现在一直没有自己用过反射,但是听说过,很多厉害一点的东西包括框架都会用到反射(spring、mybatis、rocketmq等等)。所以Java要进阶,必须懂反射。
反射是什么,借用官方api帮助文档的一句话来表达。
- 反射允许对成员变量、成员方法和构造方法的信息进行编程访问
那么反射作用就很明显了
- 对于成员变量,我们可以获取修饰符、名字、类型、值包括赋值
- 对于成员方法,我们可以获取修饰符、获取名字、获取形参、获取返回值,甚至可以获取抛出的异常、方法的注解包括运行方法
- 对于构造方法,我们可以获取修饰符、名字、形参、包括创建对象
举个简单的例子,idea的使用对象时的提示包括传参时的提示都是利用的反射,当然,反射更高级的用法我们继续进行剖析。
常用方法
(17条消息) 什么是反射?应用场景?_进击的Ace的博客-CSDN博客
对属性
对方法
对构造方法
反射创建对象和new的区别
最开始我理解反射这个东西,我始终不明白,为什么能直接创建对象,还要有反射这个功能,这里从几个方面进行理解。
首先知道一个概念:尽量做到编译期不依赖,运行时依赖
动态代理
首先我们得明白动态代理这个概念,最初我们需要创建一个对象,我们需要关注他其中的参数是什么,如果参数一旦改变我们又需要重新去创建,很麻烦。
就像我们要吃一道菜,我们最开始要吃什么就要买相应的食材,但是有了餐馆,我们就不需要关注这些,我们直接去吃就行,想要什么就点什么,这时餐馆就成为了代理。
但是有人说了,去餐馆要花额外的钱啊😶,哈哈哈,但是我们的spring不需要啊,spring的ioc控制反转不管是配置文件还是注解的注入方式都是利用了动态代理这一特性,只需要知道类名就能帮你创建对象,这都依赖于反射机制。
耦合度
再从耦合度来说,原来的代码我们在一个类里需要使用另一个类,我们就需要创建一个对象,以此概念,大家都这样做,当一个类出问题,所有创建该对象的类都会出错,包括里面没使用该对象的方法。
现在我们利用spring去代理将创建对象交给bean工厂做,无需考虑其他类,每个类的方法都可以拿出来单元测试。
效率
使用new创建对象会在编译时就全部进行全部加载,但是使用动态代理后,利用反射的特性我们就可以在用到对象的时候再去进行加载。就像一个网页有很多功能,我们需要用到什么功能再加载什么功能,大大提示效率。
- new的时候是静态编译,写了就是生成
- 反射是动态编译,编译时不会将模块编译进去,只有需要时才调用
动态编译的优点是,速度快,节省系统资源,利于扩展。最开始反射创建对象的效率是远远低于new的,但是随着反射的发展现在已经差不多了。
反射实例
实例一:对于任意一个对象都可以将对象的字段名和字段值存到文件当中去
代码实现:
public static void saveObject(Object obj) throws IllegalAccessException {
//获取字节码文件的对象
Class clazz = obj.getClass();
//获取所有成员变量
Field[] fields = clazz.gitDeclareFields();
for(Field field : fields){
field.setAccessible(true);
//获取成员变量名
String name = field.getName();
//获取值
Object value = field.get(obj);
}
}
这里就解决了我最开始的问题,我不需要知道具体成员变量,我只需要用一个object类来接收去获取就行。
实例二:利用反射动态创建对象并调用方法
public class ReflectDemo {
//获取配置文件信息
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("xxx\\prop.properties");
prop.load(fis);
fis.close();
//获取类名和方法名
String className = (String) prop.get("classname");
String methodName = (String) prop.get("method");
//利用反射创建对象并运行方法
Class clazz = Class.forName(className);
//获取构造方法
Constructor con = clazz.getDeclaredConstructor();
Object o = con.newInstance();
//获取成员方法并运行
Method method = clazz.getDeclaredMethod(methodName);
method.setAccessicle(true);
method.invoke(o);
}
这里就相当于是spring反射创建对象的原理了。
BeanUtil反射工具
强烈推荐这个工具,自己写反射灰常好用
这是一串别人给我的json
(executionBatchNumber=574407228715433984, attributeBody=4595_4555_83, templateId=cbca1c11a758ecfe871fa3116b3212311, templateVersion=1, status=1, execute=[{highestPriority=[{variableValue=82, description=逾期天数大于15, then={tag=freeze, value=冻结有效额度100%}}], hitRules=[{variableValue=82, description=逾期天数大于15, then={tag=freeze, value=冻结有效额度100%}}]}], executeTime=Fri Apr 07 15:34:30 GMT+08:00 2023)
一层套一层,按原来我需要多个实体类,但是使用反射加强转就完美解决问题
List execute = (List)dbvo.getExecute();
for(Object target : execute) {
List hitRules = (List) BeanUtil.getFieldValue(target, "hitRules");
for(Object rule : hitRules) {
//写入冻结原因
String description = (String) BeanUtil.getFieldValue(rule, "description");
if(description != null && !description.isEmpty()) {
dto.setAdjustReason(dto.getAdjustReason() + description + ";");
}
Object then = BeanUtil.getFieldValue(rule, "then");
String tag = (String) BeanUtil.getFieldValue(then, "tag");
String s = (String) BeanUtil.getFieldValue(then, "value");
int value = new BigDecimal(s).intValue();
if(value > dto.getAdjustPercent() || -value > dto.getAdjustPercent()) {
dto.setAdjustPercent(value);
}
}
}
recordDTOList.add(dto);