【java基礎(三十一)】反射(一)

反射類(reflection library)提供了一個非常豐富且精心設計的工具類,以便編寫能夠動態操作Java代碼的程序。特別是在設計或運行中添加新類時,能夠快速地應用開發工具動態地查詢添加類的能力。

能夠分析類能力的程序稱爲反射(reflective)。反射機制的功能極其強大,反射可以用來:

  • 在運行時分析類的能力。
  • 在運行時查看對象,如,編寫一個toString方法提供所有類使用。
  • 實現通用的數組操作代碼。
  • 利用Method對象。

反射是一種功能強大且複雜的機制。使用它的主要人員是工具構造者,而不是應用程序員。如果僅對設計應用程序感興趣,而對構造工具不感興趣,可以不看這個博文。以後再返回來學習瞭解。

Class類

在程序運行期間,Java運行時系統始終爲所有的對象維護一個被稱爲運行時的類型標識。這個信息跟蹤着每個對象所屬的類。虛擬機利用運行時類型信息選擇相應的方法執行。

然而,可以通過專門的Java類訪問這些信息。保存這些信息的類成爲Class。Object類中的getClass()方法將會返回一個Class類型的實例。

Employee e;
...
Class cl = e.getClass();

如同用一個Employee對象表示一個特定的僱員屬性一樣,一個Class對象將表示一個特定類的屬性。最常用的Class方法是getName。這個方法返回類的名字。例如,

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

如果e是一個僱員,則會打印輸出:

Employee Harry Hacker

如果e是經理,則會打印輸出:

Manager Harray Hacker

如果類在一個包裏,包的名字也作爲類名的一部分:

Random generator = new Random();
Class cl = generator.getClass();
String name = cl.getName(); // 輸出:java.util.Random

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

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

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

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

Class cl1 = Random.class;
Class cl2 = int.class;
Class cl3 = Double[].class;

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

鑑於歷史原因,getName方法在應用於數組類型的時候會返回一個很奇怪的名字:

  • Double[].class.getName()返回[LJava.lang.Double;
  • int[].class.getName()放回[I

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

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

還有一個很有用的方法newInstance(),可以用來動態地創建一個類的實例。如:

e.getClass().newInstance();

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

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

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

捕獲異常

以後我們詳細在說明異常處理機制,現在我們先了解一下平時遇到的一些方法如何拋出異常。

當程序運行過程中發生錯誤時,就會“拋出異常”。拋出異常比終止程序要靈活很多,這是因爲可以提供一個“捕獲”異常的處理器(handler)對異常情況進行處理。

如果沒有提供處理器,程序就會終止,並在控制檯上打印出一條信息,其中給出了異常的類型。前面我們應該遇到過很多。

異常分兩種類型:未檢查異常和已檢查異常。對於已檢查異常,編譯器將會檢查是否提供了處理器。然而,有很多常見的異常,例如,訪問null引用,都屬於未檢查異常。編譯器不會查看是否爲這些錯誤提供了處理器。畢竟,應該精心地編寫代碼來避免這些錯誤的發生,而不是將精力花在編寫處理異常處理器上。

並不是所有的錯誤都可以避免的。如果竭盡全力還是發生了異常,編譯器就要求提供一個處理器。Class.forName方法就是一個拋出已檢查異常的例子,我們來看一種最簡單的處理器:

將可能拋出已檢查異常的一個或多個方法調用放在try塊中,然後在catch字句中提供處理器代碼:

try {
	statements that might throw exceptions;
} catch (Exception e) {
	handler action;
}

如:

try {
	String name = ...;
	Class cl = Class.forName(name);
	do something with cl;
} catch (Exception e) {
	e.printStrackTrace();
}

如果類名不存在,則將跳過try塊中剩餘代碼,程序直接進入catch字句。如果try塊中沒有拋出任何異常,那麼會跳過catch字句的處理器代碼。

對於已檢查異常,只需要提供一個異常處理器。可以很容易地發現會拋出已檢查異常的方法。如果調用了一個拋出已檢查異常的方法,而又沒有提供處理器,編譯器就會給出錯誤報告。

捐贈

若你感覺讀到這篇文章對你有啓發,能引起你的思考。請不要吝嗇你的錢包,你的任何打賞或者捐贈都是對我莫大的鼓勵。

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