運行時類型信息使得你可以在程序運行時發(fā)現(xiàn)和使用類型信息。
Java讓我們能在運行時識別對象和類的信息的兩種方式:
- 傳統(tǒng)的RTTI:它假定我們在編譯時已經(jīng)知道了所有的類型;
- 反射機制:它允許我們在運行時發(fā)現(xiàn)和使用類信息胚股。
1.為什么需要RTTI
-
當把
Shape
對象放入List<Shape>
的數(shù)組時會向上轉(zhuǎn)型。但在向上轉(zhuǎn)型為Shape
的時候也丟失了Shape對象
的具體類型释树。對于數(shù)組而言削祈,它們只是Shape
類的對象。當從數(shù)組中取出元素時肮砾,這種容器—實際上他將所有的事物當作
Object
持有—會自動將結(jié)果轉(zhuǎn)型回Shape
诀黍。這是RTTI最基本的使用形式,因為在Java中仗处,所有類型轉(zhuǎn)換都是在運行時進行正確性檢查的眯勾。接下來就是多態(tài)機制的事情了枣宫,
Shape
對象實際執(zhí)行什么樣的代碼,是由引用所指向的具體對象Circle
吃环、Square
或Triangle
而決定的也颤。你希望大部分代碼盡可能地了解對象的具體類型,而是只與對象家族的一個通用表示打交道郁轻。
2.Class對象
Class對象就是用來創(chuàng)建類的所有的常規(guī)對象翅娶。
類似程序的一部分,每一個類都有一個Class對象范咨。為了生成這個類的對象故觅,運行這個程序的Java虛擬機將使用被稱為 類加載器
的子系統(tǒng)。
Class.forName()
可以獲取Class對象的引用渠啊。getName()
產(chǎn)生全限定的類名,并分別使用 getSimpleName()
和getCanonicalName()
來產(chǎn)生不含包名的類名和全限定名权旷。isInterface()
可以告訴你這個Class對象是否表示某個接口替蛉。還可以使用 getSuperclass()
方法查詢其直接基類。
2.1 類字面常量
-
使用字面常量也可以生成對Class對象的引用
FancyToy.class
類字面常量不僅可以應(yīng)用于普通的類拄氯,也可以應(yīng)用于接口躲查、數(shù)組以及基本數(shù)據(jù)類型。
當使用
.class
來創(chuàng)建對Class對象的引用時译柏,不會自動地初始化該Class對象镣煮。為使用類而做的準備工作實際包含三個步驟:- 加載:這是由類加載器執(zhí)行的;
- 鏈接:在鏈接階段將驗證類中的字節(jié)碼鄙麦,為靜態(tài)域分配存儲空間典唇,并且如果必須的話,將解析這個類創(chuàng)建的對其他類的所有引用胯府。
- 初始化:如果該類具有超類介衔,則對其初始化,執(zhí)行靜態(tài)初始化其和靜態(tài)初始化塊骂因。
Class.forName()
會立即執(zhí)行初始化炎咖;
2.2 泛化的Class引用
-
Class引用總是指向某個Class對象,它可以制造類的示例寒波,并包含可作用與這些實例的所有方法代碼乘盼。它還包含該類的靜態(tài)成員,因此俄烁,Class引用表示的就是它所指向的對象的確切類型绸栅。而該對象便是Class類的一個對象。
盡管泛型類引用只能賦值為指向其聲明的類型猴娩,但是普通類引用可以被重新賦值為指向任何其他的Class對象阴幌。通過泛型語法可以讓編譯器強制執(zhí)行額外的類型檢查勺阐。
-
使用通配符
?
放松限制,?
代表任何事物矛双。Class<?> intClass = int.class;
限定創(chuàng)建Class引用的范圍
Class<? extends Numner> bounded = int.class; Class<? super FancyToy> up = ftClass.getSuperclass();
newInstance()
返回的不是精確類型渊抽,而只是Object。
2.3 新的轉(zhuǎn)型語法
-
cast()
用于Class引用的轉(zhuǎn)型語法议忽,新的轉(zhuǎn)型語法對于無法使用普通轉(zhuǎn)型的情況顯得非常有用懒闷。
3.類型轉(zhuǎn)換前先做檢查
RTTI的第三種形式,就是關(guān)鍵字 instanceof
栈幸。它返回一個布爾值愤估,告訴我們對象是不是某個特定類型的實例。
3.1 使用類字面量
3.2 動態(tài)的 instanceof
-
Class.isInstance
方法提供了一種動態(tài)地測試對象的途徑速址。
3.3 遞歸計數(shù)
4 注冊工廠
- 使用
工廠方法
設(shè)計模式玩焰,將對象的創(chuàng)建工作交給類自己去完成。
5.instanceof與Class的等價性
6.反射:運行時的類信息
- 如果不知道某個對象的確切類型芍锚,RTTI可以告訴你昔园。但這個類型在編譯時必須已知。
- 人們想要在運行時獲取類的信息的另一動機并炮,便是希望提供在跨網(wǎng)絡(luò)的遠程平臺上創(chuàng)建和運行對象的能力默刚。這種被稱為遠程方法調(diào)用,它允許一個Java程序?qū)ο蠓植嫉蕉嗯_機器上逃魄。
- RTTI和反射之間真正的區(qū)別在于:對于RTTI來說荤西,編譯器在編譯時打開和檢查
.class
文件。而對于反射機制來說伍俘,在運行時打開和檢查.class
文件邪锌。
7.動態(tài)代理
- Java的動態(tài)代理比代理的思想更向前邁進一步,因為它可以動態(tài)創(chuàng)建代理并動態(tài)地處理對所代理方法的調(diào)用养篓。在動態(tài)代理上所做的所有調(diào)用都會被重定向到單一的調(diào)用處理器上秃流,它的工作室揭示調(diào)用的類型并確定相應(yīng)的對策。
8.空對象
空對象最有用支出在于它更靠近數(shù)據(jù)柳弄,因為對象表示的是問題空間內(nèi)的實體舶胀。
8.1 模擬對象與樁
-
空對象的邏輯變體是模擬對象和樁。與空對象一樣碧注,它們都表示在最終的程序中所使用的實際對象嚣伐。
模擬對象和樁之間的差異在于程度不同。模擬對象往往是輕量級和自測試的萍丐,通常很多模擬對象被創(chuàng)建出來是為了處理各種不同的測試情況轩端。樁只是返回樁數(shù)據(jù),它通常是重量級的逝变。
9.接口與類型信息
- interface關(guān)鍵字的一種重要目標就是允許程序員隔離構(gòu)建基茵,進而降低耦合性奋构。如果你編寫接口,那么就可以實現(xiàn)這一目標拱层,但是通過類型信息弥臼,這種耦合性還是會傳播出去——接口并非是對解耦的一種無懈可擊的保障。最簡單的解決方式是對實現(xiàn)使用包訪問權(quán)限根灯。
06/06/2019 :created