0%

Java反射入门

Java反射入门

反射基础概念

反射(Reflection)是Java语言的一个重要特性,它允许程序在运行时动态地获取类的信息并操作类或对象的属性、方法。

核心特点

  • 运行时获取类的完整结构信息

  • 动态创建对象实例

  • 动态调用方法和访问属性

  • 绕过访问权限检查(访问私有成员)

反射核心API

1. 获取Class对象的三种方式

1
2
3
4
5
6
7
8
9
// 1. 通过类名.class获取
Class<String> stringClass = String.class;

// 2. 通过对象.getClass()获取
String str = "Hello";
Class<?> strClass = str.getClass();

// 3. 通过Class.forName()获取(最常用)
Class<?> clazz = Class.forName("java.lang.String");

2. 常用反射操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建实例
Class<?> clazz = Class.forName("com.example.User");
Object user = clazz.newInstance(); // 调用无参构造
// 或
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object user = constructor.newInstance("张三", 25);

// 调用方法
Method method = clazz.getMethod("setName", String.class);
method.invoke(user, "李四");

// 访问字段
Field field = clazz.getDeclaredField("age");
field.setAccessible(true); // 突破私有访问限制
field.set(user, 30);

反射的应用场景

  1. 框架开发:Spring、Hibernate等框架大量使用反射实现依赖注入、动态代理等功能
  2. 动态代理:AOP编程的基础
  3. 注解处理:运行时通过反射读取注解信息
  4. 单元测试:测试私有方法
  5. 通用工具:如BeanUtils.copyProperties等属性拷贝工具

反射的性能问题与优化

反射虽然强大,但存在明显的性能问题:

  1. 动态解析开销:反射操作比直接调用慢很多。每次反射调用都需要解析类、方法、字段的元数据;需要检查方法签名、参数类型等
  2. JIT优化受限:直接调用可以被JIT内联优化;反射调用是动态的,难以优化
  3. 安全检查:每次调用都要检查访问权限;需要验证参数类型等

优化建议

  • 缓存反射对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 不好的做法:每次调用都获取Method
    public void slowReflection(Object obj, String methodName) throws Exception {
    Method method = obj.getClass().getMethod(methodName);
    method.invoke(obj);
    }

    // 好的做法:缓存Method
    private static Map<String, Method> methodCache = new HashMap<>();

    public void fastReflection(Object obj, String methodName) throws Exception {
    Method method = methodCache.get(methodName);
    if (method == null) {
    method = obj.getClass().getMethod(methodName);
    methodCache.put(methodName, method);
    }
    method.invoke(obj);
    }
  • **跳过安全检查setAccessible(true)**:

    1
    2
    3
    Field field = clazz.getDeclaredField("privateField");
    field.setAccessible(true); // 只需设置一次
    // 后续访问不再检查访问权限

反射的安全问题

反射可以突破Java的访问控制机制,带来安全隐患:

  1. 访问私有成员:可能导致敏感信息泄露
  2. 修改final字段:破坏不可变性设计
  3. 调用任意方法:可能执行危险操作

反射的应用示例

Spring IOC容器简化实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* 简易IOC容器实现
* 演示反射如何用于依赖注入
*/
public class SimpleContainer {
// 存储所有Bean实例,key为Bean名称
private Map<String, Object> beans = new HashMap<>();

/**
* 注册Bean到容器
* @param name Bean名称
* @param className 类全限定名
*/
public void register(String name, String className) throws Exception {
// 1. 通过反射加载类
Class<?> clazz = Class.forName(className);

// 2. 创建实例(调用无参构造)
Object instance = clazz.getDeclaredConstructor().newInstance();

// 3. 存入容器
beans.put(name, instance);
}

/**
* 获取Bean实例
*/
public Object getBean(String name) {
return beans.get(name);
}

/**
* 依赖注入方法
*/
public void injectDependencies() throws Exception {
// 遍历所有Bean
for (Object bean : beans.values()) {
// 获取Bean的Class对象
Class<?> clazz = bean.getClass();

// 遍历所有字段
for (Field field : clazz.getDeclaredFields()) {
// 检查是否有@Autowired注解
if (field.isAnnotationPresent(Autowired.class)) {
// 1. 获取依赖的Bean(按字段名查找)
Object dependency = beans.get(field.getName());

// 2. 突破私有访问限制
field.setAccessible(true);

// 3. 注入依赖
field.set(bean, dependency);
}
}
}
}
}