前言
反射是 Java 動態性的核心,允許運行時檢查類結構、創建實例、調用方法、訪問字段,廣泛應用於框架開發(Spring、Hibernate、MyBatis 等)。本文以一個完整的 Person 類為例,系統演示反射的核心 API 用法。
示例類定義
package pro.shaowei.reflect;
public class Person {
public String name;
private int age;
public Gender gender;
private static String species = "人類";
public Person() { this.name = "佚名"; this.age = -1; }
public Person(String name) { this.name = name; }
private Person(String name, int age) { this.name = name; this.age = age; }
private Person(Gender g) { this.gender = g; }
public void run() { System.out.println(name + " 跑!"); }
public void attack() { System.out.println(name + " 打!"); }
public void attack(String target) { System.out.println(name + " 打 " + target + "!"); }
private void eat(String food) { System.out.println(name + " 吃 " + food); }
public void introduce() { System.out.println("我叫" + name + ",今年" + age + "歲。"); }
public static void playGame(String game) { System.out.println("玩 " + game + " 遊戲"); }
public static void main(String[] args) { /* ... */ }
}
enum Gender { Male, Female }
核心反射操作分類
1. 構造器反射
| 類型 | 關鍵 API | 代碼片段 |
| 公有無參 | clazz.getConstructor() / clazz.newInstance() | Person p = (Person) clazz.newInstance(); |
| 公有有參 | clazz.getConstructor(String.class) | Constructor c = clazz.getConstructor(String.class); p = (Person) c.newInstance("張三"); |
| 私有有參 | clazz.getDeclaredConstructor(...) + setAccessible(true) | Constructor c = clazz.getDeclaredConstructor(String.class, int.class); c.setAccessible(true); p = (Person) c.newInstance("李四", 25); |
2. 方法反射
| 類型 | 獲取方式 | 調用示例 |
| 公有無參 | getMethod("run") | method.invoke(p); |
| 公有有參 | getMethod("attack", String.class) | method.invoke(p, "王五"); |
| 私有有參 | getDeclaredMethod("eat", String.class) + setAccessible(true) | method.invoke(p, "香蕉"); |
| 靜態有參 | getDeclaredMethod("playGame", String.class) | method.invoke(null, "掃雷"); // 靜態方法傳 null |
| main 方法 | getDeclaredMethod("main", String[].class) | method.invoke(null, (Object) new String[]{"1","2"}); // 注意數組轉 Object |
3. 字段反射
| 類型 | 獲取方式 | 讀寫示例 |
| 公有實例 | getField("name") | field.get(p); field.set(p, "新名"); |
| 私有實例 | getDeclaredField("age") + setAccessible(true) | field.get(p); field.set(p, 18); |
| 私有靜態 | getDeclaredField("species") + setAccessible(true) | field.get(null); field.set(null, "神族"); // 靜態字段傳 null |
關鍵注意點總結
- getMethod vs getDeclaredMethod:前者只能獲取公有方法(含繼承),後者獲取本類聲明的所有方法(含私有),需配合
setAccessible(true) 破壞封裝。
- 靜態成員調用:
invoke 第一個參數傳 null(或 Class 對象),而非實例。
- 數組參數傳遞:調用接受數組的方法(如 main)時,需將數組強轉為
Object,否則會被展開為可變參數。
- 性能考量:反射比直接調用慢,熱點代碼應緩存
Constructor/Method/Field 對象,或使用 MethodHandle/VarHandle(JDK 9+)。
- 安全性:
setAccessible(true) 可能受 SecurityManager 限制,模塊化系統(JPMS)下需在 module-info.java 中 opens 相關包。
實戰應用場景
- 框架通用對象映射(ORM、JSON 序列化)
- 註解處理器、依賴注入容器
- 動態代理、AOP 織入
- 熱加載、插件系統
- 測試框架訪問私有狀態
結語
反射打破了編譯時類型檢查的限制,賦予 Java 極大的靈活性。掌握其 API 體系與最佳實踐,是進階為框架開發者、源碼閱讀者的必經之路。建議結合 JDK 文檔與開源框架源碼(如 Spring 的 ReflectionUtils)深入鑽研。