// 訪問私有字段
Field field = TargetClass.class.getDeclaredField("privateField");
field.setAccessible(true); // 關鍵:抑制訪問檢查
Object value = field.get(targetInstance); // 靜態字段傳 null
field.set(targetInstance, newValue);
// 訪問私有方法
Method method = TargetClass.class.getDeclaredMethod("privateMethod", String.class, int.class);
method.setAccessible(true);
Object result = method.invoke(targetInstance, "arg1", 123); // 靜態方法傳 null
public static Field findField(Class> clazz, String name) {
while (clazz != null && clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(name);
}
Field field = String.class.getDeclaredField("value"); // char[] value
field.setAccessible(true);
char[] value = (char[]) field.get("Hello");
value[0] = 'J'; // "Hello" 變為 "Jello" (字符串常量池被污染!)
警告:修改 final 字段行為未定義 (JIT 優化可能內聯常量),**嚴禁生產代碼使用**,僅限極端測試/熱修復。
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(TargetClass.class, MethodHandles.lookup());
VarHandle fieldHandle = lookup.findVarHandle(TargetClass.class, "privateField", int.class);
MethodHandle methodHandle = lookup.findVirtual(TargetClass.class, "privateMethod",
MethodType.methodType(void.class, String.class));
// 使用
fieldHandle.set(targetInstance, 999);
methodHandle.invoke(targetInstance, "arg");
MethodHandles.privateLookupIn 需模塊開放 opens com.target to java.base,比 setAccessible 更受控。
| 風險 | 描述 | 防護措施 |
|---|---|---|
| 封裝破壞 | 業務邏輯繞過訪問控制,導致不變量被破壞 | 代碼審查禁止業務代碼用 setAccessible;架構層面提供受控 API |
| 安全管理器繞過 | 舊版 SecurityManager 可限制 suppressAccessChecks 權限 | JDK 17+ 移除 SecurityManager,改用模塊系統封裝 |
| 模塊系統封裝 (JDK 9+) | 非導出包、非 opens 包的私有成員默認不可訪問 | 模塊宣告 opens com.yourpkg to java.base 或命令行 --add-opens |
| 最終字段語義破壞 | 修改 final 字段導致 JIT 優化失效、並發可見性問題 | 嚴禁修改 final;若必須,用 Unsafe/VarHandle 並理解後果 |
| 性能陷阱 | 頻繁反射調用慢 10-100x | 緩存 Field/Method/MethodHandle;熱點改用代碼生成/MethodHandle |
getDeclaredField/getDeclaredMethod。NoSuchFieldException/IllegalAccessException/InvocationTargetException 轉為業務異常。Field.setAccessible(true) 非原子,緩存初始化用 ConcurrentHashMap.computeIfAbsent。--add-opens 或模塊宣告 opens。setAccessible(true) 是 Java 反射突破封裝的「核按鈕」。**框架作者**用它構建強大的基礎設施;**業務開發者**應視為禁區。現代替代方案:VarHandle/MethodHandle (JDK 9+) 提供更細粒度權限控制、更高性能、更友好的模塊化支持。記住:封裝是為了保護不變量,反射是為了解決框架級通用問題,二者邊界必須清晰。