Java 反射:Class 类与 Method 对象的使用
Java 反射机制允许程序在运行时检查和修改类、方法、属性等行为。通过反射,代码能够动态加载类、调用方法,而无需在编译期明确知道这些类的具体名称。这为框架开发(如 Spring、MyBatis)和通用工具库编写提供了强大的灵活性。以下指南将详细介绍如何使用 Class 类和 Method 对象进行动态编程。
一、 获取 Class 对象
Class 对象是反射操作的入口,它代表了运行时的类或接口。要操作任何类的元数据,首先必须获取该类的 Class 实例。
选择 以下三种方式之一 获取 Class 对象:
-
调用 类的
.class静态属性。- 适用于已知编译期类名的情况。
- 代码示例:
Class<?> clazz = String.class;
-
调用 对象的
.getClass()实例方法。- 适用于已拥有对象实例的情况。
- 代码示例:
Class<?> clazz = "hello".getClass();
-
调用
Class.forName()静态方法。- 适用于已知类的全限定名字符串,常用于动态加载。
- 注意:类名必须是完整的包名加类名。
- 代码示例:
Class<?> clazz = Class.forName("java.lang.String");
二、 获取 Method 对象
Method 对象封装了类的方法信息(如名称、参数类型、返回类型)。获取 Method 对象是实现动态调用的关键步骤。
区分 两种获取方法列表的场景:
getMethods():获取 该类及其父类所有public方法。getDeclaredMethods():获取 当前类声明的所有方法(包括private、protected,但不包括继承的方法)。
执行 以下步骤 获取 特定的 Method 对象:
- 准备 方法名称字符串(如
"methodName")。 - 准备 参数类型的
Class数组。如果方法无参,使用null或空数组。 - 调用
clazz.getMethod(name, parameterTypes)或clazz.getDeclaredMethod(name, parameterTypes)。
查看 以下代码示例:
// 假设有一个 User 类
public class User {
public String name;
public void setName(String name) {
this.name = name;
}
private void printInfo() {
System.out.println("User Info");
}
}
// 反射获取方法
try {
Class<?> userClass = User.class;
// 获取 public 方法 setName
// 参数类型为 String.class
Method setNameMethod = userClass.getMethod("setName", String.class);
// 获取 private 方法 printInfo
// getDeclaredMethod 可以获取任意访问权限的方法
Method printMethod = userClass.getDeclaredMethod("printInfo");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
解析 Method 对象的元数据:
- 调用
method.getName()获取 方法名。 - 调用
method.getReturnType()获取 返回值类型。 - 调用
method.getParameterTypes()获取 参数类型列表。
三、 调用方法(Invoke)
获取 Method 对象后,可以使用 invoke 方法动态执行目标逻辑。这是反射最核心的操作。
使用 Method.invoke(obj, args) 方法 执行 逻辑:
obj:如果方法是实例方法,传入目标对象;如果是静态方法,传入null。args:方法执行所需的参数值数组,顺序必须与参数类型一致。
区分 实例方法与静态方法的调用:
-
调用 实例方法:
- 需要先 创建 类的实例(通过
clazz.newInstance()或 Constructor)。 - 将实例作为
invoke的第一个参数。
- 需要先 创建 类的实例(通过
-
调用 静态方法:
- 第一个参数传入
null。
- 第一个参数传入
处理 访问权限冲突:
- 如果调用的是
private方法,必须 设置method.setAccessible(true)跳过 安全检查,否则会抛出IllegalAccessException。
查看 完整调用示例:
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
// 1. 获取 Class 对象
Class<?> userClass = User.class;
// 2. 创建对象实例(用于调用非静态方法)
Object userInstance = userClass.newInstance();
// 3. 获取 setName 方法
Method setNameMethod = userClass.getMethod("setName", String.class);
// 4. 调用 setName 方法
// 参数1:目标对象 userInstance
// 参数2:方法参数 "ZhangSan"
setNameMethod.invoke(userInstance, "ZhangSan");
// 5. 获取并调用 printInfo 方法(private)
Method printMethod = userClass.getDeclaredMethod("printInfo");
// 6. 暴力反射,取消访问检查
printMethod.setAccessible(true);
// 7. 执行私有方法
printMethod.invoke(userInstance);
}
}
四、 动态方法调用工具(实战)
在实际开发中,经常需要根据字符串形式的类名和方法名执行操作。以下是一个封装好的工具类逻辑,展示了如何整合上述步骤。
实现 ReflectApplyUtils 工具类:
输入 参数:
className:类全路径(如"com.example.User")。methodName:方法名。params:参数对象。
逻辑 流程图如下:
参考 以下核心代码实现:
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class ReflectApplyUtils {
/**
* 根据类名和方法名执行方法
*/
public static Object executeByName(String className, String methodName, Object params) {
Object result = null;
try {
// 1. 获取 Class 对象
Class<?> clasz = Class.forName(className);
// 2. 查找方法
Method method = null;
// 遍历所有 public 方法(含继承)
for (Method m : clasz.getMethods()) {
if (m.getName().equals(methodName)) {
method = m;
break;
}
}
// 3. 执行方法
if (method != null) {
int mod = method.getModifiers();
// 判断是否为 public
if (Modifier.isPublic(mod)) {
if (Modifier.isStatic(mod)) {
// 静态方法:第一个参数传 Class 对象
result = method.invoke(clasz, params);
} else {
// 实例方法:创建新实例并传入
result = method.invoke(clasz.newInstance(), params);
}
} else {
throw new Exception("Method is not public");
}
} else {
throw new Exception("Method not found");
}
} catch (Exception e) {
result = e;
}
return result;
}
}
五、 关键注意事项
在使用反射进行开发时,遵守 以下规则以避免常见陷阱:
- 性能损耗:反射调用比直接代码调用慢。避免 在高频循环或性能敏感的核心路径中大量使用反射。
- 安全限制:反射可能 破坏 封装性(如访问私有成员)。在安全管理器严格的环境下(如某些 App Server),
setAccessible(true)可能会失败。 - 泛型擦除:运行时通过反射获取泛型类型(如
List<String>中的String)较为复杂,通常只能获取到原始类型(如List)。 - 异常处理:反射相关的异常(如
InvocationTargetException,ClassNotFoundException)较多,必须 妥善捕获并处理。
通过掌握 Class 与 Method 的使用,开发者可以编写出高度灵活、可配置的通用代码,这是构建底层架构和中间件不可或缺的技能。

暂无评论,快来抢沙发吧!