| 方法 | 用途 |
|---|---|
Class.forName(String) | 載入類別,回傳 Class 物件 |
Class.getMethod(String, Class...) | 取得公開方法(含繼承) |
Method.invoke(Object, Object...) | 呼叫方法,第一參數為實例(static 方法傳 null) |
import java.lang.reflect.Method;
public class DynamicLoader {
public static void main(String[] args) throws Exception {
if (args.length == 0) {
System.out.println("用法: java DynamicLoader <完整類別名> [參數...]");
return;
}
// 1. 動態載入類別
String className = args[0];
Class> clazz = Class.forName(className);
// 2. 準備 main() 方法參數(去掉類別名)
String[] mainArgs = new String[args.length - 1];
System.arraycopy(args, 1, mainArgs, 0, mainArgs.length);
// 3. 尋找 public static void main(String[])
Method mainMethod = findMainMethod(clazz);
if (mainMethod == null) {
System.err.println("找不到 public static void main(String[]) 方法: " + className);
return;
}
// 4. 呼叫 main 方法(static 方法傳 null)
mainMethod.invoke(null, (Object) mainArgs);
}
private static Method findMainMethod(Class> clazz) {
for (Method m : clazz.getMethods()) {
if ("main".equals(m.getName())
&& java.lang.reflect.Modifier.isStatic(m.getModifiers())
&& m.getParameterCount() == 1
&& m.getParameterTypes()[0].equals(String[].class)) {
return m;
}
}
return null;
}
}
public class EchoIt {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println("Echo arg[" + i + "] = " + args[i]);
}
}
}
javac DynamicLoader.java EchoIt.java
java DynamicLoader EchoIt ONE TWO THREE
Echo arg[0] = ONE
Echo arg[1] = TWO
Echo arg[2] = THREE
import java.net.URL;
import java.net.URLClassLoader;
public class CustomPathLoader {
public static void main(String[] args) throws Exception {
// 指定額外 classpath
URL[] urls = { new File("/path/to/plugins/").toURI().toURL() };
URLClassLoader loader = new URLClassLoader(urls, CustomPathLoader.class.getClassLoader());
Class> clazz = loader.loadClass("com.example.PluginImpl");
// ... 後續同 DynamicLoader
}
}
Class> clazz = Class.forName("com.example.ServiceImpl");
Object instance = clazz.getDeclaredConstructor().newInstance(); // 預設建構子
Method process = clazz.getMethod("process", String.class);
String result = (String) process.invoke(instance, "input data");
// 定義介面
public interface Plugin {
void execute(String[] args);
}
// 動態載入實作
Class> clazz = Class.forName("com.example.MyPlugin");
Plugin plugin = (Plugin) clazz.getDeclaredConstructor().newInstance();
plugin.execute(args); // 型別安全,不需反射呼叫方法
| 錯誤 | 原因 | 解決 |
|---|---|---|
| ClassNotFoundException | 類別名稱錯誤、classpath 不含該類別 | 檢查完整類別名、確認 jar/目錄在 classpath |
| NoSuchMethodException | 找不到符合簽名的方法 | 確認方法為 public static void main(String[]) |
| IllegalAccessException | 方法非 public | 確認方法修飾詞、或用 setAccessible(true) |
| InvocationTargetException | 目標方法拋出例外 | 檢查 e.getCause() 取得真實例外 |
| ClassCastException | 類別未實作預期介面 | 確認實作關係、或用介面導向設計 |
Method 物件MethodHandles (Java 7+) 比 Method.invoke 更快動態載入類別若接受外部輸入(如 HTTP 參數指定類別名),極高風險:攻擊者可載入任意類別執行惡意代碼。必須實施白名單驗證、安全管理器、或限制於受信任來源。
Class.forName() + 反射 API 實現動態類別載入與方法呼叫。核心流程:載入類別 → 尋找方法 → 準備參數 → invoke。現代框架(Spring、Dubbo、Pf4j)皆建立在此基礎之上,提供更完善的生命週期管理、依賴注入、隔離機制。實務上優先使用成熟框架,僅在簡單工具或特殊需求時手寫反射邏輯。