java核心技術卷I-反射

反射

能夠分析類能力的程序稱爲反射(reflective )。反射機制的功能極其強大,在下面可以看到, 反射機制可以用來:
1.在運行時分析類的能力。
2.在運行時查看對象, 例如, 編寫一個 toString 方法供所有類使用。
3.實現通用的數組操作代碼。
4.利用 Method 對象, 這個對象很像中的函數指針。
反射是一種功能強大且複雜的機制。 使用它的主要人員是工具構造者,而不是應用程序
員。

Class 類

在程序運行期間,Java 運行時系統始終爲所有的對象維護一個被稱爲運行時的類型標識。這個信息跟蹤着每個對象所屬的類。 虛擬機利用運行時類型信息選擇相應的方法執行。然而, 可以通過專門的 Java 類訪問這些信息。保存這些信息的類被稱爲 Class, 這個名字很容易讓人混淆。
一個 Class 對象將表示一個特定類的屬性。最常用的 Class 方法是 getName。 這個方法將返回類的名字。例如,下面這條語句:

System.out.println(e.getClass().getName() + " " + e.getName());

還可以調用靜態方法 forName 獲得類名對應的 Class 對象。

String className = "java.util.Random";
Class cl = Class.forName(className );

如果類名保存在字符串中, 並可在運行中改變, 就可以使用這個方法。當然, 這個方法只有在 dassName 是類名或接口名時才能夠執行。否則,forName 方法將拋出一個checked exception ( 已檢查異常)。無論何時使用這個方法, 都應該提供一個異常處理器( exception handler) 。

獲得 Class類對象的第三種方法非常簡單。如果 T 是任意的 Java 類型(或 void 關鍵字,) T.class 將代表匹配的類對象

Class cl1 = Random,class; // if you import java.util
Class cl2 = int.class;
Class cl3 = Double.class;

請注意,一個 Class 對象實際上表示的是一個類型,而這個類型未必一定是一種類。例如,int 不是類, 但 int.class 是一個 Class 類型的對象

虛擬機爲每個類型管理一個 Class 對象。 因此,可以利用 =運算符實現兩個類對象比較
的操作

if (e.getClass()== Employee.class) . . .

newlnstance( ), 可以用來動態地創建一個類的實例

e.getClass0.newlnstance();

創建了一個與 e 具有相同類類型的實例。 newlnstance方法調用默認的構造器(沒有參數的構造器)初始化新創建的對象。如果這個類沒有默認的構造器, 就會拋出一個異常

將 forName 與 newlnstance 配合起來使用, 可以根據存儲在字符串中的類名創建一個對象

String s = "java.util.Random";
Object m = Class.forName(s).newlnstance();

捕獲異常

異常有兩種類型: 未檢查異常和已檢查異常。 對於已檢查異常, 編譯器將會檢查是否提供了處理器。 然而,有很多常見的異常, 例如,訪問 null 引用, 都屬於未檢查異常。編譯器不會査看是否爲這些錯誤提供了處理器。
並不是所有的錯誤都是可以避免的。如果竭盡全力還是發生了異常, 編譯器就要求提供
一個處理器。Class.forName 方法就是一個拋出已檢查異常的例子

利用反射分析類的能力

簡要地介紹一下反射機制最重要的內容—檢查類的結構,在 java.lang.reflect 包中有三個類 Field、 Method 和 Constructor 分別用於描述類的域、 方法和構造器。 這三個類都有一個叫做 getName 的方法, 用來返回項目的名稱。Held 類有一個 getType 方法, 用來返回描述域所屬類型的 Class 對象。Method 和 Constructor 類有能夠報告參數類型的方法,Method 類還有一個可以報告返回類型的方法。這 個類還有一個叫做 getModifiers 的方法, 它將返回一個整型數值,用不同的位開關描述 public 和 static 這樣的修飾符使用狀況。另外, 還可以利用java.lang.reflect 包中的 Modifier類的靜態方法分析getModifiers 返回的整型數值。例如, 可以使用 Modifier 類中的 isPublic、 isPrivate 或 isFinal判斷方法或構造器是否是 public、 private 或 final。 我們需要做的全部工作就是調用 Modifier類的相應方法,並對返回的整型數值進行分析,另外,還可以利用 Modifier.toString方法將修飾符打印出來。

Class類中的 getFields、 getMethods 和 getConstructors 方 法 將 分 別 返 回 類 提 供 的public 域、 方法和構造器數組, 其中包括超類的公有成員。Class 類的 getDeclareFields、getDeclareMethods 和 getDeclaredConstructors 方法將分別返回類中聲明的全部域、 方法和構造器, 其中包括私有和受保護成員,但不包括超類的成員。

在運行時使用反射分析對象

在編寫程序時, 如果知道想要査看的域名和類型,查看指定的域是一件很容易的事情。而利用反射機制可以查看在編譯時還不清楚的對象域。
查看對象域的關鍵方法是 Field類中的 get 方法。如果 f 是一個 Field 類型的對象(例如,通過 getDeclaredFields 得到的對象,) obj 是某個包含 f 域的類的對象,f.get(obj) 將返回一個對象,其值爲 obj 域的當前值。

Employee harry = new Employee("Harry Hacker", 35000, 10, 1, 1989);
Class cl = harry.getClass(); // the class object representing Employee
Field f = cl.getDeclaredField('name"): // the name field of the Employee class
Object v = f.get(harry); // the value of the name field of the harry object , i .e., the String object "Harry Hacker"

由於 name 是一個私有域, 所以 get 方法將會拋出一個IllegalAccessException。只有利用 get 方法才能得到可訪問域的值。除非擁有訪問權限,否則Java 安全機制只允許査看任意對象有哪些域, 而不允許讀取它們的值。
反射機制的默認行爲受限於 Java 的訪問控制。然而, 如果一個 Java 程序沒有受到安全管理器的控制, 就可以覆蓋訪問控制。 爲了達到這個目的, 需要調用 Field、 Method 或Constructor 對象的 setAccessible 方法。

f.setAtcessible(true); // now OK to call f.get(harry);

setAccessible 方法是 AccessibleObject 類中的一個方法, 它是 Field、 Method 和 Constructor類的公共超類。這個特性是爲調試、 持久存儲和相似機制提供的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章