一、引言
最近在閱讀《Java編程思想》,學(xué)習(xí)一下java類型信息,現(xiàn)在做一下總結(jié)。Java如何讓我們在運(yùn)行時(shí)識別對象和類的信息的。主要有兩種方式:一種是傳統(tǒng)的“RTTI”,它假定我們在編譯時(shí)已經(jīng)知道了所有的類型;另一種是“反射”機(jī)制,它允許我們在運(yùn)行時(shí)發(fā)現(xiàn)和使用類的信息。
二、Class對象
要理解RTTI的工作原理,首先必須知道類型信息在運(yùn)行時(shí)是如何表示的。這項(xiàng)工作是由稱為Class對象的特殊對象完成的,它包含了與類有關(guān)的信息。事實(shí)上,Class對象就是用來創(chuàng)建類的所有“常規(guī)”對象的。Java使用Class對象來執(zhí)行RTTI,即使你正在執(zhí)行的是類似轉(zhuǎn)型這樣的操作。Class類還擁有大量使用RTTI的其它方式。
類是程序的一部分,每個(gè)類都有一個(gè)Class對象。換言之,每當(dāng)編寫并且編譯一個(gè)新類,就會產(chǎn)生一個(gè)Class對象(更恰當(dāng)?shù)恼f,是被保存在一個(gè)同名.class文件中)。為了生成這個(gè)類的對象,運(yùn)行這個(gè)程序的Java虛擬機(jī)(JVM)將使用被稱為“類加載器”的子系統(tǒng)。
類加載器子系統(tǒng)實(shí)際上可以包含一條類加載器鏈,但是只有一個(gè)原生類加載器。它是JVM實(shí)現(xiàn)的一部分。原生類加載器加載的是所謂的可信類,它包括Java API類,它們通常是從本地盤加載的。在這條鏈中,通常不需要額外的類加載器,但是如果你有特殊需求(例如以某種特殊方式加載類,以支持Web服務(wù)器應(yīng)用,或者在網(wǎng)絡(luò)中下載類),那么你有一種方式可以掛接這個(gè)額外的類加載器。
所有的類都是在對其第一次使用時(shí)候,動(dòng)態(tài)加載到JVM中的。當(dāng)程序創(chuàng)建第一個(gè)對類的靜態(tài)成員引用時(shí),就會加載這個(gè)類。這個(gè)證明構(gòu)造器也是類的靜態(tài)方法,即使在構(gòu)造器之前并沒有使用static關(guān)鍵字。因此使用new 操作符創(chuàng)建的新的類對象也會被當(dāng)做是對類的靜態(tài)成員的引用。
三、Class對象的生成方式如下
1.Class.forName("類名字符串")
2.類名.class
3.實(shí)例對象.getClass()
四、為了使用類而做的準(zhǔn)備工作實(shí)際包含三個(gè)步驟:
1.加載。 這是由類加載器執(zhí)行的。該步驟將查找字節(jié)碼(通常在classpath所指的路徑中查找,但并非是必需的),并從這些字節(jié)碼中創(chuàng)建一個(gè)Class對象。
2.鏈接。 在鏈接階段將驗(yàn)證類中的字節(jié)碼,為靜態(tài)域分配存儲空間,并且如果必需的話,將解析這個(gè)類創(chuàng)建對其他類的所有引用。
3.初始化。 如果該類具有超類,則對其超類初始化,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化塊。初始化被延遲到了對靜態(tài)方法(構(gòu)造器隱式的是靜態(tài)的)或者非常數(shù)靜態(tài)域進(jìn)行首次引用時(shí)才執(zhí)行:
下面是一個(gè)初始化的例子
1 import java.util.Random; 2 3 class Initable{ 4 static final int staticFinal = 47 ; 5 static final int staticFinal2 = 6 ClassInitialization.random.nextInt(1000); 7 static { 8 System.out.運(yùn)行結(jié)果如下:
1 After creating Initable ref2 473 Initialzing Initable4 2585 1476 Initializing Initable37 After creating Initable3 ref8 74
這個(gè)例子展示了:
1.初始化盡可能“惰性”。對initable引用的創(chuàng)建中可以看到, 僅使用.class語法來獲得對類的引用不會發(fā)生初始化。但是,為了產(chǎn)生Class引用,Class.forName()立即就進(jìn)行了初始化,就像在initable3引用創(chuàng)建中看到的。
2.如果一個(gè)static final 值是“編譯期常量”,就像Initable.staticFinal那樣,那么這個(gè)值不需要對Initable類進(jìn)行初始化話就可以被讀取。但是,如果只是將一個(gè)域設(shè)置為static 和final的,還不足以確保這種行為,例如,對Initable.staticFinal2的訪問將強(qiáng)行進(jìn)行類初始化,因?yàn)樗皇且粋€(gè)編譯期常量。
3.如果一個(gè)static域不是final的,那么在對它進(jìn)行訪問時(shí),總是要求在它被讀取之前,要先進(jìn)行連接(為這個(gè)域分配存儲空間)和初始化(初始化該存儲空間),就像在對Initable.staticNonFinal的訪問中看到的那樣。
五、Class對象方法概述:
1.Class對象概述
(1)持有RTTI信息
(2)每個(gè)類都有一個(gè)Class對象,每當(dāng)編譯一個(gè)新類就產(chǎn)生一個(gè)Class對象。
(3) Class引用總是指向某個(gè)Class對象。Class引用表示的就是它所指向的對象的確切類型,而該對象便是Class類的一個(gè)對象。
2.forName()
(1) 獲取Class對象的一個(gè)引用,但引用的類還沒有加載(該類的第一個(gè)對象沒有生成)就加載了這個(gè)類.
(2) 為了產(chǎn)生Class引用,forName()立即就進(jìn)行了初始化。
3.Object-getClass() 獲取Class對象的一個(gè)引用,返回表示該對象的實(shí)際類型的Class引用。
4.getName() 獲取全限定的類名(包括包名),即類的完整名字。
5.getSimpleName() 獲取類名(不包括包名)
6.getCanonicalName() 獲取全限定的類名(包括包名)
7.isInterface() 判斷Class對象是否是表示一個(gè)接口
8.getInterface() 返回Class對象,表示Class對象所引用的類所實(shí)現(xiàn)的所有接口。
9.getSupercalss() 返回Class對象,表示Class對象所引用的類所繼承的直接基類。應(yīng)用該方法可在運(yùn)行時(shí)發(fā)現(xiàn) 一個(gè)對象完整的繼承結(jié)構(gòu)。
10.newInstance() 返回一個(gè)Oject對象,是實(shí)現(xiàn)“虛擬構(gòu)造器”的一種途徑。“虛擬構(gòu)造器”:我不知道你的確切的 類型,但無論如何都要正確創(chuàng)建你自己。用該方法創(chuàng)建的類,必須帶有默認(rèn)的構(gòu)造器。
11.cast() 接受一個(gè)對象為參數(shù),并將其轉(zhuǎn)型為Class引用的類型。該法一般是在無法使用普通轉(zhuǎn)型的情況下使用。
12.getClassLoader() 返回該類的類加載器。
13.getComponentType() 返回表示數(shù)組組件類型的Class。
14.isArray() 判定此 Class 對象是否表示一個(gè)數(shù)組類。
15.泛化的Class引用
1)實(shí)現(xiàn)方法:使用通配符“?”。
2)Class<?>優(yōu)于Class,即便他們是等價(jià)的。
3)Class<?>的好處是明確地告訴編譯器你選擇了非具體的類版本,而不是由于碰巧或者疏忽而使用了一個(gè)非具體的類引用。
4)創(chuàng)建一個(gè)范圍:創(chuàng)建一個(gè)Class引用 ,使它被限定為某種類型<className>;或該類型的任何子類型,< ? extends superClass>;或者該類型的超類,< ? super super sunClassName>
新聞熱點(diǎn)
疑難解答
圖片精選