Java反射机制允许程序在运行时获取类的内部信息并操作类的方法和属性,其中Method类专门用于描述类的方法信息,通过它可以实现不依赖硬编码的方法调用,也就是动态调用函数。这种能力在很多框架和通用工具类开发中被广泛使用,比如依赖注入框架、序列化工具等场景都需要用到反射动态调用方法的能力。
Method类的获取方式
要使用Method类动态调用函数,首先需要获取目标类的Method对象,常见的获取方式有两种,分别对应获取所有方法和获取指定方法。
获取Class对象
所有反射操作的前提是获取目标类的Class对象,常见的获取方式如下:
// 方式1:通过类字面量获取
Class<?> clazz1 = String.class;
// 方式2:通过对象实例获取
String str = "test";
Class<?> clazz2 = str.getClass();
// 方式3:通过全类名获取
Class<?> clazz3 = Class.forName("java.lang.String");
获取指定Method对象
获取到Class对象后,可以通过getMethod或者getDeclaredMethod方法获取指定的Method对象,两者的区别是getMethod只能获取public修饰的方法,包括父类的public方法,而getDeclaredMethod可以获取当前类声明的所有方法,包括private、protected修饰的方法,但不包括父类的方法。
// 定义一个测试类
class TestService {
public void publicMethod(String param) {
System.out.println("执行public方法,参数:" + param);
}
private void privateMethod(int num) {
System.out.println("执行private方法,数字:" + num);
}
}
// 获取public方法,参数为方法名和参数类型的Class对象
Class<?> serviceClass = TestService.class;
// 获取publicMethod方法,参数类型为String.class
Method publicMethod = serviceClass.getMethod("publicMethod", String.class);
// 获取private方法,需要用到getDeclaredMethod
Method privateMethod = serviceClass.getDeclaredMethod("privateMethod", int.class);
通过invoke方法动态调用函数
Method类的核心方法是invoke,它用于执行获取到的目标方法,方法的签名是Object invoke(Object obj, Object... args),第一个参数是调用方法的对象实例,第二个参数是可变参数,对应方法的入参,返回值是方法的执行结果。
调用public方法
对于public修饰的方法,获取Method对象后可以直接调用invoke,不需要额外的权限处理。
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class<?> serviceClass = TestService.class;
// 获取publicMethod方法
Method publicMethod = serviceClass.getMethod("publicMethod", String.class);
// 创建TestService实例
TestService service = new TestService();
// 调用方法,第一个参数是实例对象,第二个是方法参数
publicMethod.invoke(service, "反射调用测试");
}
}
上述代码执行后会输出:执行public方法,参数:反射调用测试。
调用private方法
如果要调用private修饰的方法,需要先调用Method对象的setAccessible(true)方法,修改访问权限,否则会抛出IllegalAccessException异常。
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class<?> serviceClass = TestService.class;
// 获取privateMethod方法
Method privateMethod = serviceClass.getDeclaredMethod("privateMethod", int.class);
// 设置可访问private方法
privateMethod.setAccessible(true);
// 创建TestService实例
TestService service = new TestService();
// 调用private方法
privateMethod.invoke(service, 100);
}
}
上述代码执行后会输出:执行private方法,数字:100。
调用静态方法
如果目标方法是静态方法,那么invoke的第一个参数可以传入null,因为静态方法不需要对象实例即可调用。
class StaticTest {
public static void staticMethod(String content) {
System.out.println("执行静态方法,内容:" + content);
}
}
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class<?> staticClass = StaticTest.class;
Method staticMethod = staticClass.getMethod("staticMethod", String.class);
// 静态方法调用时第一个参数传null
staticMethod.invoke(null, "静态方法反射调用");
}
}
常见注意事项
- 参数类型匹配:获取Method对象时传入的参数类型必须和方法声明的参数类型完全一致,否则会抛出NoSuchMethodException异常,比如方法参数是int,就不能传入Integer.class。
- 基本类型处理:如果方法参数是基本类型,需要传入对应的基本类型Class对象,比如int对应int.class,而不是Integer.class。
- 返回值处理:如果方法有返回值,invoke的返回值就是方法的执行结果,如果方法是void,invoke返回null。
- 异常处理:invoke方法会抛出InvocationTargetException异常,这个异常包装了目标方法执行时抛出的异常,如果需要获取原始异常,可以调用getCause()方法。
使用场景说明
Method类动态调用函数的能力主要适用于以下场景:
- 框架开发:比如Spring的依赖注入、AOP切面等功能都依赖反射动态调用方法。
- 通用工具类:比如对象属性拷贝、序列化反序列化等工具需要处理不同类型的对象方法调用。
- 插件化开发:运行时加载外部插件类,动态调用插件提供的接口方法。
需要注意的是,反射动态调用方法的性能比直接硬编码调用要低,如果在高频调用的场景中,建议对获取到的Method对象进行缓存,避免重复获取带来的性能损耗。