Java反射深入再理解
字数 875 2025-08-10 16:34:22
Java反射机制深入解析与实战应用
一、反射基础概念
Java反射(Reflection)是Java语言的一种强大机制,它允许程序在运行时:
- 动态加载和检查类
- 动态调用类的方法
- 动态访问和修改类的属性
- 即使这些成员是私有的(private)
反射是Java许多高级特性的基础,如:
- 动态代理
- Spring框架的依赖注入
- 反序列化机制
二、反射核心API
1. 获取Class对象的三种方式
// 方式1:通过对象实例获取
Runtime runtime = Runtime.getRuntime();
Class<? extends Runtime> class1 = runtime.getClass();
// 方式2:通过.class语法获取
Class<Runtime> class2 = Runtime.class;
// 方式3:通过Class.forName()获取
Class<?> class3 = Class.forName("java.lang.Runtime");
2. 构造方法操作
// 获取构造方法
Constructor<?> constructor = clazz.getDeclaredConstructor(parameterTypes);
// 设置可访问性(即使是private)
constructor.setAccessible(true);
// 创建实例
Object instance = constructor.newInstance(args);
3. 方法操作
// 获取方法
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
// 设置可访问性
method.setAccessible(true);
// 调用方法
Object result = method.invoke(instance, args);
4. 字段操作
// 获取字段
Field field = clazz.getDeclaredField(fieldName);
// 设置可访问性
field.setAccessible(true);
// 读取字段值
Object value = field.get(instance);
// 设置字段值
field.set(instance, newValue);
三、反射实战案例
案例1:反射调用Runtime执行命令
方式1:直接调用静态方法
Runtime.getRuntime().exec("calc");
方式2:基础反射调用
Runtime runtime = Runtime.getRuntime();
Class<? extends Runtime> aClass = runtime.getClass();
Method exec = aClass.getMethod("exec", String.class);
exec.invoke(runtime, "calc");
方式3:反射构造Runtime实例
Class<Runtime> runtimeClass = Runtime.class;
Constructor<Runtime> constructor = runtimeClass.getDeclaredConstructor();
constructor.setAccessible(true);
Runtime runtime1 = constructor.newInstance();
Method exec = runtime1.getClass().getMethod("exec", String.class);
exec.invoke(runtime1, "calc");
方式4:更复杂的反射调用
Class<?> aClass1 = Class.forName("java.lang.Runtime");
Constructor<?> constructor = aClass1.getDeclaredConstructor();
constructor.setAccessible(true);
Runtime runtime1 = (Runtime) constructor.newInstance();
Method getRuntime = runtime1.getClass().getMethod("getRuntime");
Object invoke = getRuntime.invoke(runtime1);
Method method = invoke.getClass().getMethod("exec", String.class);
method.invoke(invoke, "calc");
案例2:反射调用私有内部类
public class People {
private class Student {
private void badBoy(String username) {
System.out.println(username + " is a bad Boy~");
}
}
}
反射调用代码:
// 1. 先实例化外部类
Class<?> peopleClass = Class.forName("org.example.test.People");
Constructor<?> constructor = peopleClass.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
People people = (People) constructor.newInstance("lemono", 100);
// 2. 实例化内部类
Class<?> studentClass = Class.forName("org.example.test.People$Student");
Constructor<?> studentConstructor = studentClass.getDeclaredConstructor(peopleClass);
studentConstructor.setAccessible(true);
Object student = studentConstructor.newInstance(people);
// 3. 调用内部类私有方法
Method badBoyMethod = student.getClass().getDeclaredMethod("badBoy", String.class);
badBoyMethod.setAccessible(true);
badBoyMethod.invoke(student, "lemono");
关键点:
- 内部类的类名格式为
OuterClass$InnerClass - 内部类构造方法隐含持有外部类引用作为第一个参数
- 即使是无参构造方法,实际上也需要传入外部类实例
四、反射安全注意事项
- 性能影响:反射操作比直接调用慢,应避免在性能敏感场景过度使用
- 安全限制:反射可以绕过访问控制,破坏封装性
- 安全隐患:反射常被用于反序列化攻击等恶意场景
- 类型安全:反射绕过了编译时类型检查,可能引发运行时异常
五、反射高级应用场景
- 动态代理:基于反射实现AOP编程
- 依赖注入:Spring等框架的核心机制
- 注解处理:运行时通过反射解析注解
- 序列化/反序列化:对象与字节流转换
- 插件系统:动态加载和执行代码
六、最佳实践建议
- 缓存反射结果(如Method/Field对象)以提高性能
- 优先使用getMethod/getField而非getDeclaredMethod/getDeclaredField
- 使用setAccessible(true)后应及时恢复原状态
- 对反射操作进行必要的安全检查
- 考虑使用MethodHandle作为反射的替代方案(Java7+)
通过掌握这些反射技术,开发者可以构建更加灵活和动态的Java应用程序,但同时也应注意合理使用,避免滥用带来的性能和安全问题。