反射是 Java 動態性的基石,允許運行時檢查類結構、創建實例、調用方法、訪問字段。本文系統涵蓋 Class 類、動態加載、對象檢查、方法調用、泛型擦除驗證、泛型數組創建六大核心場景。
每個類在 JVM 中對應一個 Class 實例。獲取方式三種:
// 1. 對象.getClass()
Class cl = obj.getClass();
// 2. Class.forName("全限定名") - 支援字符串動態加載
Class cl = Class.forName("java.util.Date");
// 3. 類字面常量 .class - 編譯期常量
Class cl1 = String.class;
Class cl2 = int.class; // 基本類型也有 Class
Class cl3 = Double[].class; // 數組類型也有 Class
比較用 ==:obj.getClass() == String.class。getName() 返回全限定名。newInstance() 調用無參構造器創建實例(已棄用,建議用 getDeclaredConstructor().newInstance())。
try {
Class cl = Class.forName(className);
// 使用 cl
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
java.lang.reflect 三大核心類:
Field:getName()、getType()、getModifiers()Method:getName()、getReturnType()、getParameterTypes()、getModifiers()Constructor:getName()、getParameterTypes()、getModifiers()Modifier.toString(modifiers) 將修飾符位掩碼轉可讀字符串。
| 方法 | 範圍 | 含繼承 |
|---|---|---|
| getFields()/getMethods()/getConstructors() | public | 是 |
| getDeclaredFields()/getDeclaredMethods()/getDeclaredConstructors() | 所有聲明 | 否 |
public static void printClassInfo(String className) {
Class cl = Class.forName(className);
// 打印類聲明、構造器、方法、字段...
}
輸入 java.lang.String 可見 15 構造器、70+ 方法、5 字段(value/hash/serialVersionUID/serialPersistentFields/CASE_INSENSITIVE_ORDER)。
場景:Office 啟動 Word/Excel 組件,Excel 未編譯完成時 Office 仍可編譯運行。
interface OfficeAble { void start(); }
// Office 主程序
Class cl = Class.forName(args[0]); // 動態加載
OfficeAble oa = (OfficeAble) cl.newInstance(); // 創建實例
oa.start(); // 統一接口調用
OfficeAble 接口Field.get(obj):獲取字段值(返回 Object,基本類型自動裝箱)Field.set(obj, value):設置字段值Field.getDouble(obj) 等類型專用獲取器,避免裝箱Field f = cl.getDeclaredField("privateField");
f.setAccessible(true); // 關鍵:關閉安全檢查
Object val = f.get(obj);
f.set(obj, newValue);
setAccessible(true) 需無 SecurityManager 禁止。
Method m = cl.getMethod("methodName", String.class, int.class);
m.invoke(targetObj, "arg1", 123); // 實例方法
m.invoke(null, "arg1", 123); // 靜態方法 targetObj 傳 null
Object... args,基本類型自動裝箱InvocationTargetException 包裹真實異常,需 getCause()ArrayList raw = new ArrayList(); ArrayListgeneric = new ArrayList<>(); System.out.println(raw.getClass() == generic.getClass()); // true // 繞過編譯器檢查,向 List 添加 Integer Method add = generic.getClass().getDeclaredMethod("add", Object.class); add.invoke(generic, 99); // 成功! System.out.println(generic); // [99] // 但遍歷時會 ClassCastException for (String s : generic) { ... } // 運行時拋出異常
結論:泛型僅編譯期檢查,運行期擦除為原始類型。反射可繞過編譯器注入異類數據,但後續使用仍受類型系統約束。
問題:Object[] 無法轉型為 String[]。
public static Object goodCopyOf(Object arr, int newLength) {
Class cl = arr.getClass();
if (!cl.isArray()) return null;
Class componentType = cl.getComponentType(); // 獲取元素類型
int length = Array.getLength(arr); // 獲取長度
Object newArr = Array.newInstance(componentType, newLength); // 創建同類型數組
System.arraycopy(arr, 0, newArr, 0, Math.min(length, newLength));
return newArr;
}
Object 而非 Object[],因 int[] 是 Object 子類但非 Object[]Array.newInstance(componentType, length) 動態創建指定類型數組