logo 范 · 拾光录
网址收集 关于作者 Github Gitee
杂文随笔4
Hexo博客:基础使用Hexo博客:Next主题Hexo博客:Next进阶使用Hexo博客:Next高级配置
前端编程3
前端基础知识16
UniApp15
Vue框架19
Java编程7
Java基础知识14
SpringBoot31
SpringMVC14
MyBatis8
SpringCloud15
RocketMQ1
MyCat1
数据库4
MySQL6
Redis7
MongoDB10
H21
Java技术栈4
知识整理分布式锁的实现方案MySQL事务MySQL的锁
软件工具箱14
IDEAGitMavenGradleNginx安装Nginx配置JMeter压测OllamaPicGoRustFSVSCodeDockerObsidianObs录制
Linux知识11
树莓派安装及使用ArchLinux:常用软件ArchLinux:基础系统安装ArchLinux:深度优化ArchLInux:图形化界面安装ArchLinux:Nirifrp内网穿透Jar启动脚本Linux常用命令VirtualBox安装CentOSVirtualBox安装Ubuntu
Python编程6
Python基础知识Python语法yolo目标检测OpenCV的使用及树莓派平台condauv
创意设计2
Blender:入门知识UI设计基础知识
AI相关9
Claude CodeHermes AgentOpenAI基本使用OpenAI工具调用OpenAI记忆管理OpenAI推理执行OpenAI开发框架Langchainllama.cpp

反射(Reflection)

Java 是静态语言,但通过反射机制可以在运行时动态获取类信息、创建对象、调用方法,使 Java 具备"准动态语言"的特性。

动态语言:在运行时能改变程序自身的结构(如 JavaScript、Python)。

核心原理:

Class 类

对于每个类,JRE 都为其保留一个不变的 Class 类型对象,包含该类的所有结构信息。

常用方法

方法 说明
static Class<?> forName(String name) 根据全限定名返回 Class 对象
T newInstance() 调用无参构造创建实例(JDK 9 已废弃,推荐用 Constructor.newInstance)
String getName() 返回全限定类名
String getSimpleName() 返回简单类名(不含包名)
String getPackageName() 返回包名
Class<?> getSuperclass() 获取父类的 Class 对象
Class<?>[] getInterfaces() 获取实现的接口
int getModifiers() 获取修饰符(通过 Modifier 工具类解析)
ClassLoader getClassLoader() 获取类加载器
Constructor<?>[] getConstructors() 获取所有 public 构造器
Constructor<?>[] getDeclaredConstructors() 获取所有构造器(含私有)
Method[] getMethods() 获取所有 public 方法(含继承的)
Method[] getDeclaredMethods() 获取本类声明的所有方法(不含继承)
Method getMethod(String name, Class<?>... params) 获取指定 public 方法
Method getDeclaredMethod(String name, Class<?>... params) 获取本类声明的指定方法(含私有)
Field[] getFields() 获取所有 public 字段
Field[] getDeclaredFields() 获取本类声明的所有字段(含私有)
Field getField(String name) 获取指定 public 字段
Field getDeclaredField(String name) 获取本类声明的指定字段(含私有)
Annotation[] getAnnotations() 获取所有注解
<A extends Annotation> A getAnnotation(Class<A> c) 获取指定注解
boolean isAnnotationPresent(Class<? extends Annotation> c) 判断是否有某注解
boolean isInterface() / isArray() / isPrimitive() 判断类型
Type[] getGenericInterfaces() 获取带泛型的接口信息
Type getGenericSuperclass() 获取带泛型的父类信息

getFields vs getDeclaredFields 区别

获取 Class 对象的五种方式

// 1. 类名.class —— 最安全可靠,性能最高
Class<Person> clazz1 = Person.class;

// 2. 实例.getClass()
Class<?> clazz2 = person.getClass();

// 3. Class.forName("全限定名") —— 最常用,运行时动态加载
Class<?> clazz3 = Class.forName("com.example.Person");

// 4. 基本类型的 TYPE 属性
Class<Integer> clazz4 = Integer.TYPE;  // 等价于 int.class

// 5. 通过 ClassLoader
Class<?> clazz5 = ClassLoader.getSystemClassLoader().loadClass("com.example.Person");

操作 Field(字段)

Class<?> clazz = Class.forName("com.example.Person");

// 获取 public 字段
Field nameField = clazz.getField("name");

// 获取私有字段
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true);  // 绕过访问检查

// 获取字段信息
String fieldName = ageField.getName();
Class<?> fieldType = ageField.getType();
int modifiers = ageField.getModifiers();
boolean isPrivate = Modifier.isPrivate(modifiers);

// 读写字段值
Object obj = clazz.getDeclaredConstructor().newInstance();
nameField.set(obj, "张三");          // 写
String value = (String) nameField.get(obj);  // 读

// 静态字段读写(传 null)
Field staticField = clazz.getDeclaredField("COUNT");
staticField.set(null, 100);

操作 Method(方法)

Class<?> clazz = Class.forName("com.example.Person");
Object obj = clazz.getDeclaredConstructor().newInstance();

// 获取无参方法
Method sayHello = clazz.getDeclaredMethod("sayHello");
sayHello.invoke(obj);

// 获取带参方法
Method setName = clazz.getDeclaredMethod("setName", String.class);
setName.invoke(obj, "李四");

// 获取私有方法
Method privateMethod = clazz.getDeclaredMethod("secretMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(obj);

// 获取静态方法
Method staticMethod = clazz.getDeclaredMethod("staticMethod", int.class);
staticMethod.invoke(null, 42);  // 静态方法传 null

// 获取返回值
Method getAge = clazz.getDeclaredMethod("getAge");
int age = (int) getAge.invoke(obj);

操作 Constructor(构造器)

Class<?> clazz = Class.forName("com.example.Person");

// 获取无参构造
Constructor<?> noArgCons = clazz.getDeclaredConstructor();
Object obj1 = noArgCons.newInstance();

// 获取带参构造
Constructor<?> paramCons = clazz.getDeclaredConstructor(String.class, int.class);
Object obj2 = paramCons.newInstance("王五", 25);

// 获取私有构造(如单例模式)
Constructor<?> privateCons = clazz.getDeclaredConstructor(String.class);
privateCons.setAccessible(true);
Object obj3 = privateCons.newInstance("秘密");

// 获取构造器参数信息
Class<?>[] paramTypes = paramCons.getParameterTypes();
int paramCount = paramCons.getParameterCount();

setAccessible 与安全检查

// JDK 9+ 跨模块反射需要 VM 参数
// --add-opens java.base/java.lang=ALL-UNNAMED

注解反射

Class<?> clazz = MyService.class;

// 判断类上是否有某注解
if (clazz.isAnnotationPresent(Service.class)) {
    Service anno = clazz.getAnnotation(Service.class);
    String name = anno.value();  // @Service("userService")
}

// 获取方法上的注解
Method method = clazz.getDeclaredMethod("doSomething");
if (method.isAnnotationPresent(Transactional.class)) {
    Transactional tx = method.getAnnotation(Transactional.class);
    boolean readOnly = tx.readOnly();
}

// 获取字段上的注解
Field field = clazz.getDeclaredField("id");
Id idAnno = field.getAnnotation(Id.class);

// 获取参数的注解
for (Parameter param : method.getParameters()) {
    if (param.isAnnotationPresent(Param.class)) {
        String paramName = param.getAnnotation(Param.class).value();
    }
}

泛型反射

Java 泛型在编译后会擦除,但通过反射可以获取部分泛型信息。

// 获取方法参数泛型
Method method = clazz.getMethod("setList", List.class);
Type[] genericParamTypes = method.getGenericParameterTypes();
for (Type type : genericParamTypes) {
    if (type instanceof ParameterizedType) {
        ParameterizedType pt = (ParameterizedType) type;
        Type[] actualTypes = pt.getActualTypeArguments();
        // actualTypes[0] 就是 List<String> 中的 String
    }
}

// 获取方法返回值泛型
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType) {
    Type[] args = ((ParameterizedType) genericReturnType).getActualTypeArguments();
}

// 获取字段泛型
Field listField = clazz.getDeclaredField("dataList");
Type genericType = listField.getGenericType();
if (genericType instanceof ParameterizedType) {
    Type[] args = ((ParameterizedType) genericType).getActualTypeArguments();
}

// 获取父类泛型(常用于 DAO 基类)
Type superType = clazz.getGenericSuperclass();
if (superType instanceof ParameterizedType) {
    Type entityType = ((ParameterizedType) superType).getActualTypeArguments()[0];
}

数组反射

// 创建数组
int[] arr = (int[]) Array.newInstance(int.class, 10);
String[] strArr = (String[]) Array.newInstance(String.class, 5);

// 操作数组元素
Array.set(arr, 0, 42);
Array.set(strArr, 0, "hello");
int val = (int) Array.get(arr, 0);

// 判断是否为数组
Class<?> arrClass = int[].class;
boolean isArr = arrClass.isArray();
Class<?> componentType = arrClass.getComponentType();  // int.class

// 多维数组
int[][] matrix = (int[][]) Array.newInstance(int.class, 3, 4);

动态代理

反射的核心应用之一,在运行时动态创建代理类。

// JDK 动态代理(基于接口)
public class MyInvocationHandler implements InvocationHandler {
    private Object target;
    
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before: " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("after: " + method.getName());
        return result;
    }
}

// 创建代理对象
MyService target = new MyServiceImpl();
MyService proxy = (MyService) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new MyInvocationHandler(target)
);
proxy.doSomething();  // 前后自动打印日志

要求:被代理类必须实现接口。如无需接口可用 CGLIB(字节码层面代理子类)。

性能考虑

优化建议:

  1. 缓存 Class / Method / Field 对象,避免重复查找
  2. 能用 getMethod 就别用 getDeclaredMethod(查找范围更小)
  3. 批量处理时关闭安全检查(setAccessible 一次性设置)
  4. 高频调用考虑用 MethodHandle(JDK 7+)或 Lambda 替代
  5. 框架层面常用反射,业务代码尽量直接调用

常见应用场景

场景 说明
Spring IoC 通过反射创建 Bean、注入依赖
ORM 框架 反射读取实体类字段,映射数据库列
序列化/反序列化 Jackson、Gson 通过反射读写对象属性
动态代理 AOP 切面编程、RPC 远程调用
注解处理器 运行时扫描注解并执行对应逻辑
单元测试 JUnit 通过反射调用 @Test 方法
插件化 动态加载 jar 中的类
IDE 功能 代码补全、重构等依赖反射获取类信息

反射的优缺点

优点:

缺点:

JDK 版本差异

反射(Reflection)
Class 类
常用方法
getFields vs getDeclaredFields 区别
获取 Class 对象的五种方式
操作 Field(字段)
操作 Method(方法)
操作 Constructor(构造器)
setAccessible 与安全检查
注解反射
泛型反射
数组反射
动态代理
性能考虑
常见应用场景
反射的优缺点
JDK 版本差异