什么是反射
反射:程序在运行时动态获取类的信息、调用方法/属性/构造器。
我们来看下正常类操作和反射类操作的区别:
- 正常编码:先有类 -> new 对象
- 反射:拿到对象/类名 -> 反向拆解类的所有结构
这里该怎么理解呢,就是每一个类其实都自我存在一个CLASS对象,而我们可以从这个对象中拿到这个类的所有相关信息。
获取Class方式
1 | //1. 对象.getClass() |
每个类的new对象都是在调用内存当中加载的该类唯一的Class对象来作为模版创建。
类的加载时机(主动引用一定会加载)
- new对象
- 调用静态方法/使用静态变量
- Class.forName(“全类名”)
- 反射获取XXX.class
- 初始化子类,父类一定会先加载
类的有三个加载阶段:加载->链接->初始化: - 加载:把硬盘
.class二进制文件读到内存,生成唯一Class对象,存放在方法区 - 链接:验证+准备(给静态变量默认初始值)+解析
- 初始化:执行静态代码块、给静态变量赋真实值。
其实new就是完成了这三个阶段,而反射则是先只进行加载而不初始化。
Class常用基础方法
- getName():全类名
- getSimpleName():简单类名
- getPackage():所在包
- getSuperclass():父类Class
- getInterfaces():实现接口
利用反射获得的构造方法创建对象
获取无参构造
1 | Constructor<?> con = c.getDeclaredConstructor(); |
获取有参构造
1 | Constructor<?> con = c.getDeclaredConstructor(String.class,int.class); |
- getDeclaredConstructor():获取所有权限构造(私有也拿走)
- getConstructor():只能public
- con.setAccessible(true):暴力破除私有权限
获得成员变量 Field
1 | //获取属性 |
这里的Field更像是把类对象模版的操作工具,用于对真实对象进行修改。
那么还有一个问题,如果这个属性本身就是静态的呢?那就说明这个属于模版对象本身:
1 | nameField.set(null,"李四"); |
所以我们第一个参数传入null
反射调用方法 Method
1 | //获取普通方法 |
非常关键的两个API
- setAccessible(true):暴力访问私有属性、私有构造、私有方法,关闭权限检查
- newInstance():通过构造器创建实力对象
反射的优点和缺点
优点:
- 动态创建的对象、动态调用方法
- 很多框架的底层核心:Spring、Mybatis、注解
- 解耦:无需硬编码new
缺点: - 性能低:绕过编译,运行期解析
- 破坏封装:能强行访问私有
- 代码复杂、可读性差
说些什么吧!