在類別中定義屬性 abc,如何動態獲取這個變數的名稱字串「abc」?這是一個有趣且實用的問題,解決方案核心在於 Java 反射機制。
Java 提供強大的反射機制,比動態語言更動態。以下提供一個通用工具方法,動態獲取物件中所有宣告欄位的名稱與值:
public class MyTools {
public static void setAllComponentsName(Object obj) {
// 獲取物件對應類別中所有宣告欄位
Field[] fields = obj.getClass().getDeclaredFields();
for (int i = 0, len = fields.length; i < len; i++) {
// 獲取欄位名稱
String varName = fields[i].getName();
try {
// 保存原訪問控制權限
boolean accessFlag = fields[i].isAccessible();
// 修改訪問控制權限,允許存取 private 欄位
fields[i].setAccessible(true);
// 獲取物件中該欄位對應的實際值
Object value = fields[i].get(obj);
System.out.println("物件包含欄位: " + varName + " = " + value);
// 恢復原訪問控制權限
fields[i].setAccessible(accessFlag);
} catch (IllegalArgumentException | IllegalAccessException ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
// 測試:獲取 JLabel 所有宣告欄位名稱與值
setAllComponentsName(new JLabel("測試"));
}
}
fields[i].setAccessible(true) 是核心關鍵。私有欄位在類別外無法直接存取,透過此設定可改變私有欄位存取權限。這雖然強大,但也引發安全性擔憂——私有成員竟可被外部存取。不過在需要解決問題的情境下(如序列化、除錯工具、測試框架),此特權被廣泛採納。
此行取得傳入物件 obj 中類別欄位 fields[i] 對應的實際值。例如類別 A 有 int abc 欄位,擁有兩個實例 a1、a2。呼叫 fields[i].get(a1) 獲取 a1 的具體 abc 值;呼叫 fields[i].get(a2) 獲取 a2 的 abc 值。
以 JLabel("測試") 為例,輸出所有宣告欄位名稱與值:
物件包含欄位: uiClassID = LabelUI 物件包含欄位: mnemonic = 0 物件包含欄位: mnemonicIndex = -1 物件包含欄位: text = 測試 物件包含欄位: defaultIcon = null 物件包含欄位: disabledIcon = null 物件包含欄位: disabledIconSet = false 物件包含欄位: verticalAlignment = 0 物件包含欄位: horizontalAlignment = 10 物件包含欄位: verticalTextPosition = 0 物件包含欄位: horizontalTextPosition = 11 物件包含欄位: iconTextGap = 4 物件包含欄位: labelFor = null 物件包含欄位: LABELED_BY_PROPERTY = labeledBy
getDeclaredFields() 僅返回當前類別宣告欄位,不含繼承欄位。需遞迴遍歷父類別獲取完整欄位。Field 物件。setAccessible 可能受 SecurityManager 限制。