《java編程思想系列之9》 類型信息

java是如何讓我們在運行時候識別對象和類的信息的?
1)傳統的RTTI:編譯時候就已經知道了所有的類型
2)反射機制:允許我們在尋星的時候發現和使用類的信息

一、爲什麼需要RTTI

RTTI名字的含義:在運行時,識別一個對象的類型。

二、Class對象

所有的類都是在對其第一次使用的時候,動態加載到JVM中的。當程序創建第一個對類的靜態成員的引用時,就會加載這個類。這個證明構造器也是類的靜態方法,即使在構造器之前並沒有使用static關鍵字。



forName()的調用是爲了它產生的“副作用”:如果類Gum還沒有被加載就加載它。在加載的過程中,Gum的static子句被執行。

無論何時,只要你想在運行時候使用類型信息,就必須首先獲得對恰當的Class對象的引用。Class.forName()就是實現此功能的便捷途徑,因爲你不需要爲了獲得Class引用而持有該類型的對象。


1)在傳遞給forName()的字符串中,你必須使用全限定名(包含包名)
2)up僅僅只是一個Class引用,在編譯期不具備任何進一步的類型信息。當你創建新實例的時候,會得到Object引用,但是這個引用指向的是Toy對象。
3)使用newInstance()來創建的類,必須帶有默認的構造器。

1、類字面常量

java還提供了另一種方法來生成對Class對象的引用,即使用類字面常量。如:
FancyToy.class;   

java取得Class對象引用:1)forName()方法  2)類字面常量

這樣做不僅簡單,而且更安全,因爲它是在編譯時候就會受到檢查(因此不需要置於try語句塊中),並且它根除了對forName()方法的調用,所以也更高效

類字面常量不僅可以應用於普通的類,也可以應用於接口、數組、以及基本數據類型。另外,對於基本數據類型的包裝類,還有一個標準的TYPE。TYPE字段是一個引用,指向對應的基本數據類型的Class對象。

當使用.class來創建對Class對象的引用的時候,不會自動初始化該Class對象,爲了使用類而做的準備工作實際包含三個步驟:
1)加載
2)鏈接
3)初始化

初始化被延遲到了對靜態方法(構造器隱式的是靜態的)或者非常數靜態域進行首次引用的時候來執行:


2、泛化的Class引用

Class引用總是指向某個Class對象,它可以製造類的實例,幷包含可以作用域這些實例的所有方法代碼。它還包含該類的靜態成員,因此,Class引用表示的就是它所指向的對象的確切類型,而該對象便是Class類的一個對象。

普通的類引用不會產生警告信息,你可以看到,儘管泛型類引用只能賦值爲指向其聲明的類型,但是普通的類引用可以被重新賦值爲指向任何其他的Class對象。通過使用泛型語法,可以讓編譯器強制執行額外的類型檢查。

在Java SE5中,Class<?>優於平凡的Class,即便它們是等價的,並且平凡的Class如你所見,不會產生編譯器警告信息。Class<?>的好處是它表示你並非碰巧或者由於疏忽,而使用了一個非具體的類引用,你就是選擇了非具體的版本。


向Class引用添加泛型語法的原因僅僅是爲了提供編譯期類型檢查,因此如果你操作有誤,稍後立即就會發現這一點。

當你將泛型語法用於Class對象的時候,會發生一件很有趣的事情:newInstance()將返回該對象的確切類型,而不僅僅只是在ToyTest。java中看到的基本的Object:

3、新的轉型語法

Java SE5還添加了用於Class引用的轉型語法,即cast()方法:


三、類型轉換前先做檢查

1)instance of
2)Class.isInstance

四、註冊工廠

五、instanceof與Class的等價性


1)instanceof保持了類型的概念,它指的是“你是這個類嗎?或者你是這個類的派生類嗎?”
2)如果用==比較實際的Class對象,就沒有考慮繼承---它或者是這個確切的類型,或者不是。

六、反射:運行時候的類信息

七、動態代理

通過調用靜態方法Proxy.newProxyInstance()可以創建動態代理,這個方法需要得到一個類加載器(你通常可以從已經被加載的對象中獲取其類加載器,然後傳遞給它),一個你希望該代理實現的接口列表(不是類或者抽象類),以及InvocationHandle接口的一個實現。

八、空對象

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