為理解RTTI在Java裡如何工作,首先必須了解類型信息在運行期是如何表示的。這時要用到一個名為“Class對象”的特殊形式的對象,其中包含了與類有關的信息(有時也把它叫作“元類”)。事實上,我們要用Class對象創建屬於某個類的全部“常規”或“普通”對象。
對於作為程序一部分的每個類,它們都有一個Class對象。換言之,每次寫一個新類時,同時也會創建一個Class對象(更恰當地說,是保存在一個完全同名的.class文件中)。在運行期,一旦我們想生成那個類的一個對象,用於執行程序的Java虛擬機(JVM)首先就會檢查那個類型的Class對象是否已經載入。若尚未載入,JVM就會查找同名的.class文件,並將其載入。所以Java程序啟動時並不是完全載入的,這一點與許多傳統語言都不同。
一旦那個類型的Class對象進入內存,就用它創建那一類型的所有對象。
若這種說法多少讓你產生了一點兒迷惑,或者並沒有真正理解它,下面這個示范程序或許能提供進一步的幫助:
//: SweetShop.java // Examination of the way the class loader works class 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 main Loading Candy After creating Candy Loading Gum After Class.forName("Gum") Loading Cookie After creating Cookie
可以看到,每個Class只有在它需要的時候才會載入,而static初始化工作是在類載入時執行的。
非常有趣的是,另一個JVM的輸出變成了另一個樣子:
Loading Candy Loading Cookie inside main After creating Candy Loading Gum After Class.forName("Gum") After creating Cookie
看來JVM通過檢查main()中的代碼,已經預測到了對Candy和Cookie的需要,但卻看不到Gum,因為它是通過對forName()的一個調用創建的,而不是通過更典型的new調用。盡管這個JVM也達到了我們希望的效果,因為確實會在我們需要之前載入那些類,但卻不能肯定這兒展示的行為百分之百正確。
1. 類標記
在Java 1.1中,可以采用第二種方式來產生Class對象的句柄:使用“類標記”。對上述程序來說,看起來就象下面這樣:
Gum.class;
這樣做不僅更加簡單,而且更安全,因為它會在編譯期間得到檢查。由於它取消了對方法調用的需要,所以執行的效率也會更高。
類標記不僅可以應用於普通類,也可以應用於接口、數組以及基本數據類型。除此以外,針對每種基本數據類型的封裝器類,它還存在一個名為TYPE的標准字段。TYPE字段的作用是為相關的基本數據類型產生Class對象的一個句柄,如下所示:
……等價於……
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