運行期類型識別(RTTI,run-time type identification):當你只有一個指向對象的基類的引用時,RTTI機制可以讓你找出這個對象確切的類型。
兩種方式:一種是傳統的 RTTI,它假定我們在編譯期和運行期已經知道了所有的類型;另一種是反射機制(reflection),它允許我們在運行期獲得類的信息。
多態機制,Shape對象實際上執行什么樣的代碼,是由引用指向的具體對象是Circle,Square 或者Triangle而決定的。
類型信息在運行期的表示是由 “Class 對象”的特殊對象完成的,它包含了與類有關的信息。Class 對象正是被用來創建類的“常規”對象的。每個類都有一個 Class 對象。編寫并且編譯了一個新類,就會產生一個 Class 對象,保存在一個同名的.class 文件中。想生成這個類的一個對象,運行這個程序的 Java 虛擬機首先檢查這個類的Class對象是否已經加載。如果尚未加載,JVM 就會根據類名查找.class 文件,并將其載入。Java 程序并不是一開始執行,就被完全加載的。一旦某個類的 Class 對象被載入內存,它就被用來創建這個類的所有對象。
Class.forName("Gum");這是Class類(所有Class對象都屬于這個類型)的一個static 成員。Class對象就和其他對象一樣,我們可以獲取并操作它的引用(這也就是類加載器的工作)。forName()是取得Class 對象的引用的一種方法。對forName()的調用是為了它產生的“副作用”:如果類Gum還沒有被加載就加載它。
Gum.class; 在編譯期就會受到檢查。
TYPE域是一個引用,指向對應的基本數據類型的 Class 對象。
RTTI形式包括:
1. 經典的類型轉換,由RTTI確保類型轉換的正確性,如果你執行了一個錯誤的類型轉換,就會拋出一個ClassCastException異常。
2. 代表對象類型的Class對象。通過查詢Class對象可以獲取運行期所需的信息。
Java 是通過Class對象來實現RTTI機制的。
Class.forName()方法,在獲取Class的引用時,并不需要生成該Class類型的對象。僅僅是獲取引用。而如果你有了一個類型的對象,那么你可以通過調用getClass()來獲取Class的引用,這是根類Object提供的方法。它返回Class 的引用,用來表示對象的實際類型。
Class.getInterfaces()方法返回Class對象的數組,這些對象代表的是某個Class對象所包含的接口。
如果你有一個Class對象,可以通過getSuperclass()獲取它的直接基類,返回一個Class的引用,這意味著在運行期,你可以找到一個對象完整的類層次結構。
Class的newInstance()方法創建一個新的對象的方法。“盡管我不知道你的準確類型是什么,但還是請正確地創建你自己。”
反射(Reflection)運行期的類信息
在編譯期,編譯器必須知道你要通過RTTI 來處理的所有類。
java.lang.reflect包含了Field,Method以及Constructor類(每個類都實現了 Member 接口)。這些類型的對象是由JVM在運行期創建的,用以表示未知類里對應的成員。
匿名對象的類信息就能在運行期被完全確定下來,而在編譯期不需要知道任何事情。
通過反射與一個未知類型的對象打交道時,JVM只是簡單地檢查這個對象,看它屬于哪個特定的類,在做其它事情之前,必須加載那個類的 Class 對象。那個類的.class 文件對于JVM 來說必須是可獲取的,要么在本地機器上,要么可以通過網絡取得。
RTTI和反射之間真正的區別只在于,對 RTTI 來說,編譯器在編譯期打開和檢查.class 文件。而對于反射機制來說.class文件在編譯期間是不可獲取的,所以是在運行期打開和檢查.class 文件。