Java 是一种极其流行的面向对象编程语言,拥有丰富的特性和功能。其中之一就是反射(Reflection),它为开发人员提供了一种动态获取信息以及在运行时操作类、接口、对象等的能力。
什么是 Java 反射?
Java 反射指的是在程序运行时检查或修改其结构、状态以及行为的能力。通过反射,我们可以动态地创建对象、调用方法、访问成员变量,并且可以更加灵活地操作类的各种元素。
如何使用 Java 反射?
获取 Class 对象
// 方式1
Class<?> clazz = OrderContext.class;
// 方式2
Class<?> clazz = Class.forName(cn.xicode.cloud.lab.spring.OrderContext");
创建对象
// 方式1
OrderContext obj = (OrderContext) clazz.newInstance();
// 方式2
Constructor<OrderContext> constructor = clazz.getConstructor(parameterTypes);
OrderContext obj = constructor.newInstance(args);
访问父类
// 获取父类class对象
Class<? super OrderContext> superclass = clazz.getSuperclass();
// 获取父类泛型信息
Type genericSuperclass = clazz.getGenericSuperclass();
// 父类泛型名称
String typeName = genericSuperclass.getTypeName();
// 获取class实现接口信息
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println("interfaces:" + anInterface.getName());
}
调用方法
Method method = clazz.getMethod("methodName", parameterTypes);
method.invoke(obj, args);
访问字段
// 根据字段名获取Field对象
Field field = clazz.getDeclaredField("fieldName");
// 字段赋值
field.setAccessible(true);
field.set(obj, value);
// 获取字段名
String fieldName = field.getName();
// 获取字段类型
Class<?> fieldType = field.getType();
// 获取字段泛型类型
Type genericType = field.getGenericType();
// 字段段是否引入注解 如:Autowired注解
field.isAnnotationPresent(Autowired.class);
spring 反射的使用
在创建 bean 的属性赋值过程中,就需要通过反射获取对象字段的信息并进行依赖注入。
对添加@Autowired 注解的属性找到对应 Class
注意:@Autowired 注解除了可以作用在字段,也可以作用在 set 方法和构造器上
- 是否有某个注解
// 字段是否加了@Autowired注解
boolean annotationPresent= field.isAnnotationPresent(Autowired.class);
// 方法获取注解信息
Annotation[] allMAnnos = method.getAnnotations();
Annotation[] deMAnnos = method.getDeclaredAnnotations();
Annotation annotation = method.getAnnotation(Autowired.class);
- 获取字段泛型类型对象
Type genericType = field.getGenericType();
处理泛型相关的字段
如果是泛型类型 或 泛型变量 或 泛型数组,需要找出泛型对应字段的 Class 对象
public static void main(String[] args) {
// 获取class类型
Class<OrderContext> clazz = OrderContext.class;
// 获取父类泛型信息
Type genericSuperclass = clazz.getGenericSuperclass();
String typeName = genericSuperclass.getTypeName();
System.out.println("父类泛型信息:" + typeName);
// 获取父类泛型变量名
Class<? super OrderContext> superclass = clazz.getSuperclass();
TypeVariable<? extends Class<? super OrderContext>>[] typeParameters = superclass.getTypeParameters();
for (TypeVariable<? extends Class<?>> typeParameter : typeParameters) {
System.out.println("父类泛型变量名:" + typeParameter.getName());
}
}
// 输出
父类泛型信息:cn.xicode.cloud.lab.spring.AbstractContext<cn.xicode.cloud.lab.spring.OrderService>
父类泛型变量名:R
看上代码输出,R 泛型变量对应实际类型是 cn.xicode.cloud.lab.spring.OrderService,进而可以通过 Class 和 bean 名称从 BeanFactory 中找到或创建该对象,并通过反射对字段进行属性赋值
Java 反射的优缺点
优点
- 灵活性:可以在运行时获取类的信息并进行操作,使得代码更加灵活。
- 动态性:可以动态地加载类、调用方法,以及处理对象。
缺点
- 性能损耗:反射操作通常比直接代码执行更慢,因为它需要在运行时进行额外的检查和解析。
- 安全性问题:由于反射可以绕过访问限制,可能会导致安全隐患。
最佳实践
缓存 Class 对象
获取 Class 对象是一个昂贵的操作,应该尽量避免重复获取,而是将其缓存起来重复使用。
异常处理
在使用反射时,一定要注意合理处理可能出现的异常,如 ClassNotFoundException、NoSuchMethodException 等。