為理解RTTI在Java裡如何工作,首先必須瞭解類型資訊在運行期是如何表示的。這時要用到一個名為“Class對象”的特殊形式的對象,其中包含了與類有關的資訊(有時也把它叫作“元類”)。事實上,我們要用Class對象建立屬於某個類的全部“常規”或“普通”對象。
對於作為程式一部分的每個類,它們都有一個Class對象。換言之,每次寫一個新類時,同時也會建立一個Class對象(更恰當地說,是儲存在一個完全同名的.class檔案中)。在運行期,一旦我們想產生那個類的一個對象,用於執行程式的Java虛擬機器(JVM)首先就會檢查那個類型的Class對象是否已經載入。若尚未載入,JVM就會尋找同名的.class檔案,並將其載入。所以Java程式啟動時並不是完全載入的,這一點與許多傳統語言都不同。
一旦那個類型的Class對象進入記憶體,就用它建立那一類型的所有對象。
若這種說法多少讓你產生了一點兒迷惑,或者並沒有真正理解它,下面這個示範程式或許能提供進一步的協助:
//: SweetShop.java// Examination of the way the class loader worksclass Candy { static { System.out.println("Loading Candy"); }}class Gum { static { System.out.println("Loading Gum"); }}class Cookie { static { System.out.println("Loading Cookie"); }}public class SweetShop { public static void main(String[] args) { System.out.println("inside main"); new Candy(); System.out.println("After creating Candy"); try { Class.forName("Gum"); } catch(ClassNotFoundException e) { e.printStackTrace(); } System.out.println( "After Class.forName(\"Gum\")"); new Cookie(); System.out.println("After creating Cookie"); }} ///:~
對每個類來說(Candy,Gum和Cookie),它們都有一個static從句,用於在類首次載入時執行。相應的資訊會列印出來,告訴我們載入是什麼時候進行的。在main()中,對象的建立代碼位於列印語句之間,以便偵測載入時間。
特別有趣的一行是:
Class.forName("Gum");
該方法是Class(即全部Class所從屬的)的一個static成員。而Class對象和其他任何對象都是類似的,所以能夠擷取和控制它的一個控制代碼(裝載模組就是幹這件事的)。為獲得Class的一個控制代碼,一個辦法是使用forName()。它的作用是取得包含了目標類文本名字的一個String(注意拼字和大小寫)。最後返回的是一個Class控制代碼。
該程式在某個JVM中的輸出如下:
inside mainLoading CandyAfter creating CandyLoading GumAfter Class.forName("Gum")Loading CookieAfter creating Cookie
可以看到,每個Class只有在它需要的時候才會載入,而static初始化工作是在類載入時執行的。
非常有趣的是,另一個JVM的輸出變成了另一個樣子:
Loading CandyLoading Cookieinside mainAfter creating CandyLoading GumAfter Class.forName("Gum")After creating Cookie
看來JVM通過檢查main()中的代碼,已經預測到了對Candy和Cookie的需要,但卻看不到Gum,因為它是通過對forName()的一個調用建立的,而不是通過更典型的new調用。儘管這個JVM也達到了我們希望的效果,因為確實會在我們需要之前載入那些類,但卻不能肯定這兒展示的行為百分之百正確。
1. 類標記
在Java 1.1中,可以採用第二種方式來產生Class對象的控制代碼:使用“類標記”。對上述程式來說,看起來就象下面這樣:
Gum.class;
這樣做不僅更加簡單,而且更安全,因為它會在編譯期間得到檢查。由於它取消了對方法調用的需要,所以執行的效率也會更高。
類標記不僅可以應用於普通類,也可以應用於介面、數組以及基礎資料型別 (Elementary Data Type)。除此以外,針對每種基礎資料型別 (Elementary Data Type)的封裝器類,它還存在一個名為TYPE的標準欄位。TYPE欄位的作用是為相關的基礎資料型別 (Elementary Data Type)產生Class對象的一個控制代碼,如下所示:
……等價於……
... is equivalent to ...
|
|
boolean.class
|
Boolean.TYPE
|
|
char.class
|
Character.TYPE
|
|
byte.class
|
Byte.TYPE
|
|
short.class
|
Short.TYPE
|
|
int.class
|
Integer.TYPE
|
|
long.class
|
Long.TYPE
|
|
float.class
|
Float.TYPE
|
|
double.class
|
Double.TYPE
|
|
void.class
|
Void.TYPE
|