文章目录

Java 反射:Class 类与 Method 对象的使用

发布于 2026-04-18 15:22:01 · 浏览 12 次 · 评论 0 条

Java 反射:Class 类与 Method 对象的使用

Java 反射机制允许程序在运行时检查和修改类、方法、属性等行为。通过反射,代码能够动态加载类、调用方法,而无需在编译期明确知道这些类的具体名称。这为框架开发(如 Spring、MyBatis)和通用工具库编写提供了强大的灵活性。以下指南将详细介绍如何使用 Class 类和 Method 对象进行动态编程。


一、 获取 Class 对象

Class 对象是反射操作的入口,它代表了运行时的类或接口。要操作任何类的元数据,首先必须获取该类的 Class 实例。

选择 以下三种方式之一 获取 Class 对象:

  1. 调用 类的 .class 静态属性。

    • 适用于已知编译期类名的情况。
    • 代码示例:Class<?> clazz = String.class;
  2. 调用 对象的 .getClass() 实例方法。

    • 适用于已拥有对象实例的情况。
    • 代码示例:Class<?> clazz = "hello".getClass();
  3. 调用 Class.forName() 静态方法。

    • 适用于已知类的全限定名字符串,常用于动态加载。
    • 注意:类名必须是完整的包名加类名。
    • 代码示例:Class<?> clazz = Class.forName("java.lang.String");

二、 获取 Method 对象

Method 对象封装了类的方法信息(如名称、参数类型、返回类型)。获取 Method 对象是实现动态调用的关键步骤。

区分 两种获取方法列表的场景:

  1. getMethods()获取 该类及其父类所有 public 方法。
  2. getDeclaredMethods()获取 当前类声明的所有方法(包括 privateprotected,但不包括继承的方法)。

执行 以下步骤 获取 特定的 Method 对象:

  1. 准备 方法名称字符串(如 "methodName")。
  2. 准备 参数类型的 Class 数组。如果方法无参,使用 null 或空数组。
  3. 调用 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:方法执行所需的参数值数组,顺序必须与参数类型一致。

区分 实例方法与静态方法的调用:

  1. 调用 实例方法:

    • 需要先 创建 类的实例(通过 clazz.newInstance() 或 Constructor)。
    • 将实例作为 invoke 的第一个参数。
  2. 调用 静态方法:

    • 第一个参数传入 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:参数对象。

逻辑 流程图如下:

graph TD A[Start: Execute By Name] --> B[Get Class Object via forName] B --> C{Iterate getMethods} C -->|Match Name| D[Found Method] C -->|No Match| E[Throw Exception] D --> F{Check Modifiers} F -->|Public Static| G[method.invoke clazz, params] F -->|Public Instance| H[clazz.newInstance] H --> I[method.invoke instance, params] F -->|Non-Public| J[Throw Exception] G --> K[Return Result] I --> K

参考 以下核心代码实现:

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;
    }
}

五、 关键注意事项

在使用反射进行开发时,遵守 以下规则以避免常见陷阱:

  1. 性能损耗:反射调用比直接代码调用慢。避免 在高频循环或性能敏感的核心路径中大量使用反射。
  2. 安全限制:反射可能 破坏 封装性(如访问私有成员)。在安全管理器严格的环境下(如某些 App Server),setAccessible(true) 可能会失败。
  3. 泛型擦除:运行时通过反射获取泛型类型(如 List<String> 中的 String)较为复杂,通常只能获取到原始类型(如 List)。
  4. 异常处理:反射相关的异常(如 InvocationTargetException, ClassNotFoundException)较多,必须 妥善捕获并处理。

通过掌握 ClassMethod 的使用,开发者可以编写出高度灵活、可配置的通用代码,这是构建底层架构和中间件不可或缺的技能。

评论 (0)

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

扫一扫,手机查看

扫描上方二维码,在手机上查看本文