反射是 Java 動態性的基石,讓程式在運行期檢查、修改類別結構、創建物件、調用方法。框架(Spring、Hibernate、MyBatis)、註解處理器、序列化庫、測試工具無一不建立在反射之上。本文從原理、三種獲取 Class 方式、動態實例化、方法調用、配置驅動擴展五個維度深度解析。
Class<T> 實例,描述類別完整結構(欄位、方法、建構子、註解、父類、介面等)。int.class、boolean.class、void.class 等。
// 1. 類別字面量(編譯期已知類別)
Class c1 = String.class;
// 2. 實例 getClass()(運行期已有物件)
String s = "abc";
Class extends String> c2 = s.getClass();
// 3. 字串全限定名(完全動態,配置驅動核心)
Class> c3 = Class.forName("java.lang.String");
場景對比:方式 1/2 需編譯期依賴類別;方式 3 僅需字串,支援插件架構、熱載入、配置驅動。
// 傳統方式
Person p = new Person();
p.say("hello");
// 反射方式
Class> clazz = Class.forName("Person");
Person p = (Person) clazz.newInstance(); // 僅調用無參建構子
// 或
Constructor> ctor = clazz.getConstructor(String.class);
Person p = (Person) ctor.newInstance("張三"); // 指定建構子
Method method = clazz.getMethod("say", String.class); // 公開方法
method.invoke(p, "hello"); // 相當於 p.say("hello")
// 私有方法
Method privateMethod = clazz.getDeclaredMethod("secret");
privateMethod.setAccessible(true); // 破壞封裝
privateMethod.invoke(p);
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(p, "李四"); // 設值
String name = (String) field.get(p); // 取值
主板預留 PCI 擴展槽,未來需支援網卡、聲顯卡等未知設備。核心代碼不可修改,新設備只需實現介面並配置即可生效。
interface PCI { void open(); void close(); }
class MainBoard { void usePCI(PCI card) { card.open(); card.close(); } }
class NetCard implements PCI { ... } // 新增設備
// 客戶端必須修改:
MainBoard mb = new MainBoard();
mb.usePCI(new NetCard()); // 硬編碼依賴 NetCard
// 1. 配置檔 conf.txt
com.example.NetCard
// 或 com.example.SoundCard
// 2. 主程式動態載入
public class MainBoardDemo {
public static void main(String[] args) throws Exception {
MainBoard mb = new MainBoard();
// 讀取配置
BufferedReader br = new BufferedReader(new FileReader("conf.txt"));
String className = br.readLine().trim(); // 獲取全限定類名
// 反射實例化
Class> clazz = Class.forName(className);
PCI card = (PCI) clazz.newInstance(); // 需無參建構子
// 使用
mb.usePCI(card);
}
}
// 新增 SoundCard.java
public class SoundCard implements PCI {
public void open() { System.out.println("聲卡開啟"); }
public void close() { System.out.println("聲卡關閉"); }
}
// 只需修改 conf.txt 為 com.example.SoundCard
| 項目 | 影響 | 優化建議 |
|---|---|---|
| 速度 | 比直接調用慢 10-100 倍 | 緩存 Class/Method/Field/Constructor 物件;熱點路徑避免反射 |
| 安全性 | 破壞封裝(setAccessible) | 僅在受信代碼中使用;SecurityManager 限制 |
| 編譯期檢查 | 失去型別安全 | 單元測試覆蓋反射路徑;使用 MethodHandle (JDK 7+) 替代 |
| 模組化 | JDK 9+ 模組系統限制反射存取 | 模組宣明 opens package to 模組;或命令行 --add-opens |