mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
615 字
2 分钟
Java反射机制详解
2026-02-09

反射是Java高级特性中最让我着迷的一个——运行时能看见自己,还能修改自己。工作中用Spring的依赖注入时一直在想它是怎么做到的,后来才明白反射就是那个幕后推手。这篇文章把反射的用法和原理讲清楚,看完你也能写出自己的框架。

Java反射机制详解#

什么是反射?#

反射就是程序在运行时能获取自己的信息——有哪些类、哪些方法、哪些字段,还能动态调用它们。我第一次用反射的时候感觉像在写黑科技,原来代码能看见自己长什么样。

反射的应用场景#

  • 框架开发:Spring、Hibernate等框架的核心机制
  • 动态代理:AOP(面向切面编程)的基础
  • 序列化/反序列化:JSON、XML转换
  • 插件系统:动态加载类
  • 配置文件解析:根据配置动态加载类

获取Class对象#

反射的入口,有三种方式:

// 1. 类名.class
Class<?> clazz = String.class;
// 2. 对象.getClass()
String str = "Hello";
Class<?> clazz = str.getClass();
// 3. Class.forName()(最常用,因为可以动态指定类名)
Class<?> clazz = Class.forName("java.lang.String");

核心操作#

获取类信息#

Class<?> clazz = Person.class;
clazz.getName(); // 全限定名
clazz.getSimpleName(); // 简单类名
clazz.getModifiers(); // 修饰符
clazz.getSuperclass(); // 父类
clazz.getInterfaces(); // 实现的接口

获取构造方法并创建实例#

// 获取构造方法
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
// 创建实例
Object instance = constructor.newInstance("张三", 25);
// 调用无参构造(Java 9+已弃用newInstance())
Object instance = clazz.getDeclaredConstructor().newInstance();

获取方法并调用#

Method method = clazz.getMethod("methodName", String.class);
Object result = method.invoke(instance, "参数");
// 调用私有方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true); // 重要!跳过访问检查
privateMethod.invoke(instance);

获取字段并读写#

Field field = clazz.getDeclaredField("name");
field.setAccessible(true); // 私有字段需要这个
// 读取
String name = (String) field.get(instance);
// 写入
field.set(instance, "李四");

动态代理#

反射在动态代理中的经典应用——AOP的基础:

// 接口
interface UserService {
void addUser(String name);
}
// 实现
class UserServiceImpl implements UserService {
public void addUser(String name) {
System.out.println("添加用户:" + name);
}
}
// InvocationHandler
class LogHandler implements InvocationHandler {
private Object target;
public LogHandler(Object target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始执行:" + method.getName());
Object result = method.invoke(target, args);
System.out.println("执行完毕:" + method.getName());
return result;
}
}
// 使用
UserService userService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
new LogHandler(userService)
);
proxy.addUser("张三");

性能优化#

反射比直接调用慢,因为需要在运行时解析类型信息。不过大部分场景下这点性能损耗可以忽略,除非是在高并发的热点路径上。

优化方法:

  1. 缓存反射对象——Method、Field等获取后缓存起来重用
  2. setAccessible(true)——跳过访问权限检查
  3. 用MethodHandle(Java 7+)——比反射更高效
// 缓存Method对象
private static final Method METHOD;
static {
try {
METHOD = SomeClass.class.getDeclaredMethod("someMethod");
METHOD.setAccessible(true);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

常见陷阱#

  1. ClassNotFoundException——类名拼写错误
  2. NoSuchMethodException——方法名或参数类型不对
  3. IllegalAccessException——忘了setAccessible(true)
  4. InvocationTargetException——被调用的方法内部抛了异常
  5. 性能问题——热点路径上别用反射

💡 实战贴士: 反射虽然强大,但能不用就不用。如果只是为了获取某个类的信息,优先考虑用接口或抽象类来设计。框架和工具类里用反射是合理的,但业务代码里用反射就要小心了——代码可读性差、编译时检查不到、出了问题不好排查。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

部分信息可能已经过时

目录