1. 概述
本文将快速介绍如何使用Java反射API在运行时调用方法。反射是Java的强大特性,但使用时需注意性能开销和安全性问题。
2. 准备工作
先创建一个用于演示的简单类:
public class Operations {
public double publicSum(int a, double b) {
return a + b;
}
public static double publicStaticMultiply(float a, long b) {
return a * b;
}
private boolean privateAnd(boolean a, boolean b) {
return a && b;
}
protected int protectedMax(int a, int b) {
return a > b ? a : b;
}
}
3. 获取Method对象
要调用方法,首先需要获取对应的Method
对象。Class
对象提供了两种主要方式:
3.1. getMethod()
✅ 用于获取公共方法(包括继承的父类方法)
参数:方法名 + 参数类型列表
Method sumInstanceMethod
= Operations.class.getMethod("publicSum", int.class, double.class);
Method multiplyStaticMethod
= Operations.class.getMethod(
"publicStaticMultiply", float.class, long.class);
3.2. getDeclaredMethod()
✅ 用于获取类中声明的所有方法(包括private/protected)
❌ 不包括继承的方法
参数:方法名 + 参数类型列表
Method andPrivateMethod
= Operations.class.getDeclaredMethod(
"privateAnd", boolean.class, boolean.class);
Method maxProtectedMethod
= Operations.class.getDeclaredMethod("protectedMax", int.class, int.class);
4. 调用方法
获取Method
对象后,通过invoke()
执行方法:
4.1. 实例方法
调用实例方法时,invoke()
的第一个参数必须是方法所属类的实例:
@Test
public void givenObject_whenInvokePublicMethod_thenCorrect() {
Method sumInstanceMethod
= Operations.class.getMethod("publicSum", int.class, double.class);
Operations operationsInstance = new Operations();
Double result
= (Double) sumInstanceMethod.invoke(operationsInstance, 1, 3);
assertThat(result, equalTo(4.0));
}
4.2. 静态方法
静态方法不需要实例,第一个参数传null
即可:
@Test
public void givenObject_whenInvokeStaticMethod_thenCorrect() {
Method multiplyStaticMethod
= Operations.class.getDeclaredMethod(
"publicStaticMultiply", float.class, long.class);
Double result
= (Double) multiplyStaticMethod.invoke(null, 3.5f, 2);
assertThat(result, equalTo(7.0));
}
5. 方法可访问性
⚠️ 默认情况下,反射方法受Java访问控制限制
直接调用private/protected方法会抛出IllegalAccessException
:
@Test(expected = IllegalAccessException.class)
public void givenObject_whenInvokePrivateMethod_thenFail() {
Method andPrivateMethod
= Operations.class.getDeclaredMethod(
"privateAnd", boolean.class, boolean.class);
Operations operationsInstance = new Operations();
Boolean result
= (Boolean) andPrivateMethod.invoke(operationsInstance, true, false);
assertFalse(result);
}
@Test(expected = IllegalAccessException.class)
public void givenObject_whenInvokeProtectedMethod_thenFail() {
Method maxProtectedMethod
= Operations.class.getDeclaredMethod(
"protectedMax", int.class, int.class);
Operations operationsInstance = new Operations();
Integer result
= (Integer) maxProtectedMethod.invoke(operationsInstance, 2, 4);
assertThat(result, equalTo(4));
}
5.1. AccessibleObject#setAccessible()
✅ 调用setAccessible(true)
可绕过访问控制检查
简单粗暴但有效:
@Test
public void givenObject_whenInvokePrivateMethod_thenCorrect() throws Exception {
Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
andPrivatedMethod.setAccessible(true); // 关键操作
Operations operationsInstance = new Operations();
Boolean result = (Boolean) andPrivatedMethod.invoke(operationsInstance, true, false);
assertFalse(result);
}
5.2. AccessibleObject#canAccess()
Java 9新增方法,用于检查调用者是否有权访问反射方法
替代已废弃的isAccessible()
:
@Test
public void givenObject_whenInvokePrivateMethod_thenCheckAccess() throws Exception {
Operations operationsInstance = new Operations();
Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
boolean isAccessEnabled = andPrivatedMethod.canAccess(operationsInstance);
assertFalse(isAccessEnabled); // 默认无访问权限
}
5.3. AccessibleObject#trySetAccessible()
✅ 更优雅的访问控制方式:
- 成功时返回
true
- 失败时返回
false
(不像setAccessible()
抛异常)
@Test
public void givenObject_whenInvokePublicMethod_thenEnableAccess() throws Exception {
Operations operationsInstance = new Operations();
Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
boolean success = andPrivatedMethod.trySetAccessible(); // 尝试开启访问
assertTrue(success); // 验证是否成功开启
boolean isAccessEnabled = andPrivatedMethod.canAccess(operationsInstance);
assertTrue(isAccessEnabled);
}
6. 总结
本文演示了通过反射调用实例方法和静态方法的核心流程,重点解决了访问控制问题:
- 获取
Method
对象:getMethod()
vsgetDeclaredMethod()
- 调用方法:
invoke()
的实例/静态方法差异 - 访问控制:
setAccessible()
、canAccess()
和trySetAccessible()
的使用场景
⚠️ 反射虽强大,但需注意:
- 性能开销比直接调用高
- 破坏封装性,可能引发安全问题
- 在模块化系统(JPMS)中可能受限
完整示例代码可在GitHub仓库查看。