在Java中,反射(Reflection)允许程序在运行时动态获取类的信息并操作类或对象。以下是反射的核心操作及示例:
1. 获取Class对象
反射的入口是Class
对象,有三种获取方式:
// 方式1:通过类名.class
Class<?> clazz1 = String.class;
// 方式2:通过对象.getClass()
String str = "Hello";
Class<?> clazz2 = str.getClass();
// 方式3:通过Class.forName()(需全限定类名)
Class<?> clazz3 = Class.forName("java.lang.String");
2. 操作类成员
(1) 获取构造方法并创建对象
// 获取无参构造方法
Constructor<?> constructor = clazz1.getConstructor();
Object obj = constructor.newInstance(); // 等价于 new String()
// 获取带参构造方法(如String(byte[]))
Constructor<?> byteConstructor = clazz1.getConstructor(byte[].class);
Object byteStr = byteConstructor.newInstance(new byte[]{65, 66}); // "AB"
(2) 获取并调用方法
// 获取public方法(如String.length())
Method lengthMethod = clazz1.getMethod("length");
int len = (int) lengthMethod.invoke("ABC"); // 结果为3
// 获取private方法(需setAccessible(true))
Method privateMethod = clazz1.getDeclaredMethod("privateMethodName");
privateMethod.setAccessible(true);
privateMethod.invoke(obj);
(3) 访问字段
// 获取public字段(如String.value)
Field valueField = clazz1.getDeclaredField("value");
valueField.setAccessible(true);
char[] chars = (char[]) valueField.get("Java"); // 获取底层char数组
// 修改字段值
valueField.set("Java", new char[]{'K', 'o', 't', 'l', 'i', 'n'});
3. 动态代理(反射的高级应用)
通过Proxy
和InvocationHandler
实现接口的动态代理:
interface Greeter { void greet(); }
InvocationHandler handler = (proxy, method, args) -> {
System.out.println("Before greeting");
return null; // 模拟方法调用
};
Greeter proxy = (Greeter) Proxy.newProxyInstance(
Greeter.class.getClassLoader(),
new Class[]{Greeter.class},
handler
);
proxy.greet(); // 输出"Before greeting"
4. 反射的典型应用场景
- 框架开发:如Spring的依赖注入、Hibernate的ORM映射。
- 动态加载类:如插件化架构中加载未知类。
- 测试工具:访问私有方法或字段进行单元测试。
注意事项
- 性能开销:反射比直接调用慢,频繁操作需缓存
Class
/Method
对象。 - 安全限制:需处理
SecurityException
,且可能破坏封装性。 - 代码可读性:过度使用反射会降低代码可维护性。
通过反射,Java程序可以在运行时灵活地探索和操作类结构,但应权衡其带来的灵活性与潜在代价。