Java反射机制
本文最后更新于 2025-10-31,文章内容可能已经过时。
一、反射的核心原理
1. Class对象的本质
每个类在JVM加载后都会生成唯一的Class对象,作为运行时类型信息的载体。
示例:获取Class对象的三种方式
// 方式1:类.class(编译期确定)
Class<String> clazz1 = String.class;
// 方式2:对象.getClass()(运行时确定)
String str = "Hello";
Class<?> clazz2 = str.getClass();
// 方式3:Class.forName()(动态加载类,需全限定名)
Class<?> clazz3 = Class.forName("java.util.ArrayList");
// 验证是否为同一Class对象
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz1 == clazz3); // false
二、反射的核心API详解
1. 获取类成员
1.1 获取构造方法
// 获取所有公共构造方法
Constructor<?>[] constructors = String.class.getConstructors();
// 获取指定参数类型的构造方法
Constructor<String> constructor = String.class.getConstructor(char[].class);
String str = (String) constructor.newInstance(new char[]{'H', 'e', 'l', 'l', 'o'});
System.out.println(str); // Hello
1.2 获取字段
// 获取所有公共字段(含继承)
Field[] publicFields = String.class.getFields();
// 获取本类声明的所有字段(含私有)
Field declaredField = String.class.getDeclaredField("value");
declaredField.setAccessible(true); // 突破访问限制
char[] value = (char[]) declaredField.get("Hello");//byte[] value = (byte[]) declaredField.get("Hello");
System.out.println(value); // [H, e, l, l, o]
在Java 9+中,String类的内部实现发生了变化。虽然String类的value字段在Java语言层面仍然是char[],但JVM内部实现使用了byte[]来存储UTF-8编码的字符串(特别是对于非ASCII字符)。
具体来说:
- 在Java 8及更早版本中,
String类的value字段类型是char[],所以反射获取时返回char[]。 - 在Java 9+中,
String类的value字段在JVM内部被表示为byte[](尽管Java语言层面仍视为char[]),所以反射获取时返回的是byte[]。
在Java 9+中,String类确实引入了新的内部实现:
value字段类型仍是char[](Java语言层面)- 但内部使用
byte[]存储UTF-8编码的字符串 - 有一个新的
coder字段用于区分编码方式
在Java 8中,String类的value字段内部表示就是char[],所以反射获取时返回char[],代码可以正常工作。但在Java 9+中,由于内部实现变化,需要使用byte[]来接收返回值。
1.3 获取方法
// 获取所有公共方法(含继承)
Method[] methods = String.class.getMethods();
// 获取本类声明的特定方法(含私有)
Method privateMethod = String.class.getDeclaredMethod("hashCode");
privateMethod.setAccessible(true);
int hash = (int) privateMethod.invoke("Hello");
System.out.println(hash); // 输出"Hello".hashCode()
2. 动态创建对象
// 通过无参构造创建实例
Class<?> listClass = Class.forName("java.util.ArrayList");
Object list = listClass.getDeclaredConstructor().newInstance();
System.out.println(list); // []
// 通过有参构造创建实例
Constructor<Date> dateConstructor = Date.class.getConstructor(long.class);
Date date = dateConstructor.newInstance(System.currentTimeMillis());
System.out.println(date); // 当前时间
三、反射的高级应用
1. 动态代理(AOP实现)
// 定义接口
interface Service {
void execute();
}
// 实现类
class RealService implements Service {
@Override
public void execute() {
System.out.println("RealService executing...");
}
}
// 动态代理处理器
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(new RealService(), args); // 调用真实对象
System.out.println("After method: " + method.getName());
return result;
}
};
// 创建代理对象
Service proxy = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class[]{Service.class},
handler
);
proxy.execute();
/* 输出:
Before method: execute
RealService executing...
After method: execute
*/
2. 依赖注入模拟(类似Spring)
// 定义接口和实现类
interface Dependency {
void doSomething();
}
class DependencyImpl implements Dependency {
@Override
public void doSomething() {
System.out.println("DependencyImpl working...");
}
}
// 需要注入依赖的类
class Client {
private Dependency dependency;
public void setDependency(Dependency dependency) {
this.dependency = dependency;
}
public void run() {
dependency.doSomething();
}
}
// 通过反射实现依赖注入
public class ReflectiveDI {
public static void main(String[] args) throws Exception {
Client client = new Client();
Field field = Client.class.getDeclaredField("dependency");
field.setAccessible(true);
field.set(client, new DependencyImpl()); // 手动注入
client.run(); // 输出 "DependencyImpl working..."
}
}
3. 注解处理器
自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface MyAnnotation {
String value();
}
使用注解
class AnnotatedClass {
@MyAnnotation("name")
private String username = "Alice";
}
反射读取注解
public class AnnotationProcessor {
public static void main(String[] args) throws Exception {
Class<?> clazz = AnnotatedClass.class;
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
field.setAccessible(true);
System.out.println(annotation.value() + ": " + field.get(new AnnotatedClass()));
// 输出 name: Alice
}
}
}
}
四、反射的性能优化
1. 缓存反射对象
// 缓存Method对象,避免重复查找
private static final Map<Class<?>, Map<String, Method>> METHOD_CACHE = new ConcurrentHashMap<>();
public static Method getCachedMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
return METHOD_CACHE.computeIfAbsent(clazz, k -> new HashMap<>())
.computeIfAbsent(methodName, k -> {
try {
return clazz.getDeclaredMethod(methodName, parameterTypes);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
五、反射的典型应用场景
1. JSON序列化工具
public class JsonSerializer {
public static String toJson(Object obj) throws Exception {
StringBuilder sb = new StringBuilder("{");
for (Field field : obj.getClass().getDeclaredFields()) {
field.setAccessible(true);
sb.append(field.getName()).append(":")
.append(field.get(obj)).append(",");
}
return sb.deleteCharAt(sb.length()-1).append("}").toString();
}
public static void main(String[] args) throws Exception {
User user = new User("Bob", 30);
System.out.println(JsonSerializer.toJson(user));
// 输出: {"username":"Bob","age":30}
}
}
class User {
private String username;
private int age;
public User(String username, int age) {
this.username = username;
this.age = age;
}
}
六、反射的局限性与替代方案
1. 性能对比
// 直接调用 vs 反射调用
class Benchmark {
public void targetMethod() {}
public static void main(String[] args) throws Exception {
Benchmark obj = new Benchmark();
Method method = Benchmark.class.getMethod("targetMethod");
long start = System.nanoTime();
for (int i = 0; i < 1_000_000; i++) {
obj.targetMethod(); // 直接调用
}
System.out.println("Direct call: " + (System.nanoTime() - start) + " ns");
start = System.nanoTime();
for (int i = 0; i < 1_000_000; i++) {
method.invoke(obj); // 反射调用
}
System.out.println("Reflection call: " + (System.nanoTime() - start) + " ns");
}
}
// 输出示例:
// Direct call: 120,000,000 ns
// Reflection call: 1,200,000,000 ns
2. 替代方案
- Lombok:通过注解自动生成代码,避免手动编写getter/setter。
- MapStruct:编译期生成对象映射代码,替代反射拷贝。
- 字节码操作工具(ASM/ByteBuddy):直接操作字节码,性能接近原生代码。
七、反射的安全问题
1. 访问私有构造方法
// 获取String的私有构造方法(不推荐使用!)
Constructor<String> constructor = String.class.getDeclaredConstructor(char[].class);
constructor.setAccessible(true);
String hacked = (String) constructor.newInstance(new char[]{'S', 'e', 'c', 'r', 'e', 't'});
System.out.println(hacked); // Secret
2. 安全管理器限制
Java 9+默认禁用反射访问私有成员,需通过JVM参数启用:
--add-opens java.base/java.lang=ALL-UNNAMED
八、反射的底层实现(JVM视角)
- 类加载阶段
.class文件被加载到方法区,生成Class对象。
- 符号引用解析
- 反射调用时,JVM会解析方法/字段的符号引用为直接引用。
- 动态链接
invokevirtual指令用于调用反射方法,涉及额外的查找过程。
九、完整反射工具类示例
import java.lang.reflect.*;
public class ReflectionUtils {
// 通用对象属性拷贝
public static void copyProperties(Object dest, Object src) throws Exception {
Class<?> srcClass = src.getClass();
Class<?> destClass = dest.getClass();
for (Field destField : destClass.getDeclaredFields()) {
String fieldName = destField.getName();
Field srcField = srcClass.getDeclaredField(fieldName);
srcField.setAccessible(true);
destField.setAccessible(true);
destField.set(dest, srcField.get(src));
}
}
// 动态调用方法
public static Object invokeMethod(Object obj, String methodName, Object... args) throws Exception {
Class<?>[] paramTypes = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
paramTypes[i] = args[i].getClass();
}
Method method = obj.getClass().getDeclaredMethod(methodName, paramTypes);
method.setAccessible(true);
return method.invoke(obj, args);
}
// 测试用例
public static void main(String[] args) throws Exception {
User user1 = new User("Alice", 25);
User user2 = new User();
copyProperties(user2, user1);
System.out.println(user2.getUsername()); // Alice
String result = (String) invokeMethod(user1, "toString");
System.out.println(result); // User{username='Alice', age=25}
}
}
class User {
private String username;
private int age;
public User() {}
public User(String username, int age) {
this.username = username;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
}
十、总结
| 特性 | 描述 |
|---|---|
| 动态性 | 运行时分析和操作类,实现高度灵活的设计 |
| 应用场景 | 框架开发(Spring/MyBatis)、动态代理、注解处理、通用工具等 |
| 性能代价 | 反射调用比直接调用慢约10倍,需通过缓存优化 |
| 安全风险 | 可访问私有成员,可能破坏封装性,需严格控制权限 |
| 替代方案 | Lombok、MapStruct、ASM等工具可减少反射使用,提升性能和可维护性 |
掌握反射是成为Java高级开发者的关键,但需在灵活性与性能之间权衡。
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 软件从业者Hort
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果

