1. 程式人生 > 其它 >淺析Java中的RTTI機制與反射機制

淺析Java中的RTTI機制與反射機制

技術標籤:Java程式設計思想java反射

Java有兩種方式讓我們在執行時識別物件和類的資訊的。一種是“傳統的”RTTI(Run-Time Type Identification),它假定我們編譯時已經知道所有的型別;另一種是“反射”機制,它允許我們在執行是發現和使用類的資訊。

14.2 Class物件

要理解RTTI在Java中的工作原理,首先必須知道型別資訊在執行時是如何表示的。這項工作是由稱為Class物件的特殊物件完成的,它包含了與類有關的資訊。事實上,Class物件就是用來建立類的所有的”常規“物件的。Java使用Class物件來執行其RTTI,即使你正在執行的是類似轉型這樣的操作。

Class.forName("Gum");

這個方法是Class類的一個static成員。Class物件就和其他物件一樣,我們可以獲取並操作它的引用。forName()是取得Class物件的引用的一種方法。它是用一個包含目標類的文字名的String作輸入引數,返回的是一個Class物件的引用。對forName()的呼叫是為了它產生的”副作用“:如果類Gum還沒有被載入就載入它。在載入的過程中,Gum的static子句被執行。

無論何時,只要你想在執行時使用型別資訊,就必須首先獲得對恰當的Class物件的引用。Class.forName()就是實現此功能的便捷途徑,因為你不需要為了獲得Class引用而持有該型別的物件。但是,如果你已經擁有了一個感興趣的型別的物件,那就可以通過呼叫getClass()方法來獲取Class引用了,這個方法屬於根類Object的一部分,它將返回該物件的實際型別的Class引用。

14.2.1 類字面常量

Java還提供了另外一種方法來生成對Class物件的引用,即使用類字面常量。就像下面這樣:

Gum.class;

這樣做不僅更簡單,因為它在編譯時就會受到檢查。並且它根除了對forName()方法的呼叫,所以也就更高效。

類字面常量不僅可以應用於普通的類,也可以應用於介面、陣列以及基本資料型別。

當使用”.class"來建立對Class物件的引用時,不會自動地初始化該Class物件。為了使用類而做的準備工作實際包含三個步驟:

  1. 載入,這是由類的載入器執行的。該步驟將查詢位元組碼(通常在classpath所指定的路徑中查詢),並從這些位元組碼中建立一個Class物件。
  2. 連結。在連結階段將驗證類中的位元組碼,為靜態域分配儲存空間,並且如果必需的話,將解析這個類建立的對其他類的所有引用。
  3. 初始化。如果該類具有超類,則對其初始化,執行靜態初始化器和靜態初始化塊。

14.3 型別轉化前先做檢查

RTTI機制有以下作用:

1)傳統的型別轉換:由RTTI確保型別轉換的正確性,如果執行了一個錯誤的型別轉換,就會丟擲一個ClassCastException異常。

2)代表物件的型別的Class物件。通過查詢Class物件可以獲取執行時所需的資訊。

3)RTTI在Java中還有第三種形式,就是關鍵字instanceof。它返回一個布林值,告訴我們物件是不是某個特定型別的例項。

14.5 instanceof與Class的等價性

在查詢型別資訊時,以instance的形式與直接比較Class物件有一個很重要的差別。instanceof保持了型別的概念,它指的是“你是這個類嗎,或者你也是這個類的派生類嗎?”而如果用==比較實際的Class物件,就沒有考慮繼承——它或者是這個確切的型別,或者不是。

14.6 反射:執行時的類資訊

如果不知道某個確切型別,RTTI可以告訴你。但是有一個限制:這個型別在編譯時必須已知,這樣才能使用RTTI識別它,並利用這些資訊做一些有用的事。換句話說,在編譯時,編譯器必須知道所有要通過RTTI來處理的類。

要認識到反射機制並沒有什麼神奇之處。當通過反射與一個未知型別的物件打交道時,JVM只是簡單地檢查這個物件,看它屬於哪個特定的類(就像RTTI那樣)。在用它做其他事情之前必須先載入那個類的Class物件。因此,那個類的.class檔案對於JVM來說必須是可獲取的:要麼在本地機器上,要麼可以通過網路取得。所以RTTI和反射之間真正的區別只在於,對RTTI來說,編譯器在編譯時開啟和檢查.class檔案。而對於反射機制來說,.class檔案在編譯時是不可獲取的,所以在執行時開啟和檢查.class檔案。

反射在Java中是用來支援其他特性的,例如物件序列化和JavaBean。

14.7 動態代理

代理是基本的設計模式之一,它是你為了提供額外的或不同的操作,而插入的用來代替“實際”物件的物件。這些操作通常涉及與“實際”物件的通訊,因此代理通常充當著中間人的角色。

參考:

  • 《Java程式設計思想》