動態加載類、插件化架構、腳本引擎、測試框架等場景需在運行時根據字符串類名/方法名調用靜態方法。
package com.example.demo;
public class TargetService {
public static Integer process(String input) {
System.err.println("靜態方法執行: " + input);
return input.length();
}
public static String concat(String a, String b, String c) {
return a + b + c;
}
}
public final class StaticMethodInvoker {
private StaticMethodInvoker() {}
/**
* 調用靜態方法
* @param className 全限定類名
* @param methodName 方法名
* @param args 實參(按聲明順序)
* @return 方法返回值(void 返回 null)
*/
@SuppressWarnings("unchecked")
public static Object invoke(String className, String methodName, Object... args) {
try {
// 1. 加載類
Class<?> clazz = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
// 2. 解析參數類型
Class<?>[] paramTypes = new Class[args.length];
for (int i = 0; i < args.length; i++) {
paramTypes[i] = args[i] != null ? args[i].getClass() : Object.class;
// 注意:基本類型傳入包裝類時會匹配失敗,需額外處理
}
// 3. 獲取方法(支持重載,取首個匹配)
Method method = findMethod(clazz, methodName, paramTypes);
if (method == null) {
throw new NoSuchMethodException("未找到匹配方法: " + methodName + Arrays.toString(paramTypes));
}
// 4. 設置可訪問(繞過 private/protected)
method.setAccessible(true);
// 5. 執行調用(靜態方法實例參數傳 null)
return method.invoke(null, args);
} catch (Exception e) {
throw new RuntimeException("反射調用失敗: " + className + "#" + methodName, e);
}
}
/** 簡易重載匹配:參數數量一致且實參類型可賦值給形參 */
private static Method findMethod(Class<?> clazz, String name, Class<?>[] argTypes) {
for (Method m : clazz.getDeclaredMethods()) {
if (!m.getName().equals(name) || !Modifier.isStatic(m.getModifiers())) continue;
Class<?>[] params = m.getParameterTypes();
if (params.length != argTypes.length) continue;
boolean match = true;
for (int i = 0; i < params.length; i++) {
if (argTypes[i] != Object.class && !params[i].isAssignableFrom(argTypes[i])) {
match = false; break;
}
}
if (match) return m;
}
return null;
}
}
public class Demo {
public static void main(String[] args) {
// 調用無參/單參
Object r1 = StaticMethodInvoker.invoke("com.example.demo.TargetService", "process", "Hello Reflection");
System.out.println("返回值: " + r1); // 17
// 調用多參
Object r2 = StaticMethodInvoker.invoke("com.example.demo.TargetService", "concat", "A", "B", "C");
System.out.println("返回值: " + r2); // ABC
}
}
| 問題 | 解決方案 |
|---|---|
| 基本類型參數 | 參數類型推斷時將包裝類映射為基本類型(Integer.class → int.class),或直接要求調用者傳入 int.class 等 Type Token |
| 重載消歧 | 引入 MethodType 描述符或要求調用者傳入參數類型數組 Class[] |
| 性能優化 | 緩存 Method 對象(ConcurrentHashMap<String, Method>),避免重複 Class.forName 和 getDeclaredMethods |
| 異常透傳 | 捕獲 InvocationTargetException 拋出 getCause() 保留原始堆棧 |
| 模塊化系統 | Java 9+ 需 --add-opens 或使用 MethodHandles.lookup() 替代 setAccessible |
public final class FastStaticInvoker {
private static final Map<String, Method> CACHE = new ConcurrentHashMap<>();
public static Object invoke(String className, String methodName, Object... args) {
String key = className + "#" + methodName + "#" + Arrays.toString(args);
Method method = CACHE.computeIfAbsent(key, k -> resolveMethod(className, methodName, args));
try {
method.setAccessible(true);
return method.invoke(null, args);
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getCause());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Method resolveMethod(String className, String methodName, Object[] args) {
try {
Class<?> clazz = Class.forName(className);
Class<?>[] types = Arrays.stream(args)
.map(o -> o != null ? primitiveIfWrapper(o.getClass()) : Object.class)
.toArray(Class[]::new);
for (Method m : clazz.getDeclaredMethods()) {
if (m.getName().equals(methodName) && Modifier.isStatic(m.getModifiers())
&& Arrays.equals(m.getParameterTypes(), types)) {
return m;
}
}
throw new NoSuchMethodException();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Class<?> primitiveIfWrapper(Class<?> c) {
if (c == Integer.class) return int.class;
if (c == Long.class) return long.class;
if (c == Double.class) return double.class;
if (c == Boolean.class) return boolean.class;
if (c == Character.class) return char.class;
if (c == Byte.class) return byte.class;
if (c == Short.class) return short.class;
if (c == Float.class) return float.class;
return c;
}
}
反射調用靜態方法屬於動態編程範疇,運行時靈活性極高但損失編譯期類型安全與性能,僅在插件系統、腳本引擎、通用測試框架等基礎設施層使用,業務代碼慎用。