詳細(xì)介紹類加載與反射
Java中,反射是一種強(qiáng)大的工具。它使您能夠創(chuàng)建靈活的代碼,這些代碼可以在運(yùn)行時(shí)裝配,無(wú)需在組件之間進(jìn)行源代表鏈接。下面介紹類加載和反射,供參考。
一、類加載
當(dāng)調(diào)用java命令運(yùn)行某個(gè)Java程序時(shí),該命令將啟動(dòng)一條Java虛擬機(jī)進(jìn)程,同一個(gè)JVM的所有線程,所有變量都處于同一進(jìn)程里,它們都是用該JVM進(jìn)程的內(nèi)存區(qū)
- 程序運(yùn)行到***正常結(jié)束
- 程序運(yùn)行到使用System.exit()或Runtime().exit()代碼結(jié)束程序
- 程序執(zhí)行過(guò)程中遇到未捕獲的異?;蝈e(cuò)誤而結(jié)束
- 程序所在平臺(tái)強(qiáng)制結(jié)束了JVM進(jìn)程
類的加載
類加載指的是將類的class文件讀入內(nèi)存,并為之創(chuàng)建一個(gè)java.lang.Class對(duì)象,而系統(tǒng)中所有的類,它們實(shí)際上也是對(duì)象,稱為類對(duì)象,它們都是java.lang.Class的實(shí)例,虛擬機(jī)為每種類型管理一個(gè)***的Class對(duì)象,也就是說(shuō),每個(gè)類(型)都有一個(gè)Class對(duì)象.
類的連接
連接階段將會(huì)負(fù)責(zé)將類的二進(jìn)制數(shù)據(jù)合并到JRE中
- 驗(yàn)證
- 準(zhǔn)備:類準(zhǔn)備階段則負(fù)責(zé)將類的靜態(tài)屬性分配內(nèi)存,并設(shè)置默認(rèn)初始值
- 解析
類的初始化
在類的初始化階段,虛擬機(jī)負(fù)責(zé)對(duì)類進(jìn)行初始化,主要就是對(duì)靜態(tài)屬性進(jìn)行初始化,在Java類中對(duì)靜態(tài)屬性指定初始值有兩種方式:
(1)聲明靜態(tài)屬性時(shí)指定初始值;
(2)使用靜態(tài)初始化塊為靜態(tài)屬性制定初始值
1、當(dāng)Java程序***通過(guò)下面6種方式來(lái)使用某個(gè)類或接口時(shí),系統(tǒng)就會(huì)初始化該類或接口
a) 創(chuàng)建類的實(shí)例
b) 調(diào)用某個(gè)類的靜態(tài)方法
c) 訪問(wèn)某個(gè)類或接口的靜態(tài)屬性,或?yàn)樵撿o態(tài)屬性賦值
d) 使用反射方式來(lái)強(qiáng)制創(chuàng)建某個(gè)類或接口對(duì)應(yīng)的java.lang.Class對(duì)象
e) 初始化某個(gè)類的子類,當(dāng)初始化某個(gè)類的子類時(shí),該子類的所有父類都會(huì)被初始化
f) 直接使用java.exe命令來(lái)運(yùn)行某個(gè)主類時(shí),程序會(huì)先初始化該主類
2、類加載器
類加載器負(fù)責(zé)將.class文件加載到內(nèi)存中,并為之生成對(duì)應(yīng)的java.lang.Class對(duì)象
在Java中,一個(gè)類用其全限定類名(包括包名和類名)作為標(biāo)識(shí)
在JVM中,一個(gè)類用其全限定類名和其類加載器作為其唯一標(biāo)識(shí)
當(dāng)JVM啟動(dòng)時(shí),會(huì)形成由三個(gè)類加載器組成的:
Bootstrap ClassLoader:根類加載器 它不是java.lang.ClassLoader的子類,而是由JVM自身實(shí)現(xiàn)rt.jar
Extension ClassLoader:擴(kuò)展類加載器 它負(fù)責(zé)加載JRE的擴(kuò)展目錄ext中JAR的類包
System ClassLoader:系統(tǒng)類加載器 它負(fù)責(zé)在JVM啟動(dòng)時(shí),加載來(lái)自命令java中的-classpath選項(xiàng)或java.class.path系統(tǒng)屬性,或CLASSPATH環(huán)境變量所指定的JAR包和類路徑,默認(rèn)是以當(dāng)前路徑作為系統(tǒng)加載路徑
AppClassLoader
用戶自定義類加載器
3、JVM類加載機(jī)制
全盤負(fù)責(zé)
父類委托:所謂父類委托是先讓parent(父)類加載器試圖加載該Class,只有在父類加載器無(wú)法加載該類時(shí)才嘗試從自己的類路徑中加載該類
緩存機(jī)制
4、通過(guò)反射查看類信息
Java程序中獲得Class對(duì)象通常有如下三種方法:
a) 使用Class類的forName()靜態(tài)方法.該方法需要傳入字符串參數(shù),該字符串參數(shù)的值是某個(gè)類的全限定類名(必須添加完整包名)
b) 調(diào)用某個(gè)類的class屬性來(lái)獲取該類對(duì)應(yīng)的Class對(duì)象.
c) 調(diào)用某個(gè)對(duì)象的getClass()方法,該方法是java.lang.Object類中的一個(gè)方法,所以所有java對(duì)象都可以調(diào)用該方法,該方法將會(huì)返回該對(duì)象所屬類對(duì)應(yīng)的Class對(duì)象
b方法:代碼更安全,程序在編譯階段就可以檢查需要訪問(wèn)的Class對(duì)象是否存在
程序性能提高,因?yàn)檫@種方法無(wú)需調(diào)用方法,所以性能更好
一旦獲得某個(gè)類所對(duì)應(yīng)的Class對(duì)象后,就可以調(diào)用Class對(duì)象的方法來(lái)獲得該對(duì)象和該類的真實(shí)信息
getDeclared 與訪問(wèn)級(jí)別無(wú)關(guān),顯式聲明的
get 獲得所有的但只是public,包括繼承的
5、使用反射生成并操作對(duì)象
Class對(duì)象可以獲得該類里包括的方法(由Methode對(duì)象表示),構(gòu)造器(由Constructor對(duì)象表示),Field(Field對(duì)象表示),這三個(gè)類都定義在java.lang.reflect包下,并實(shí)現(xiàn)了java.lang.reflect.Member接口,程序可以通過(guò)Method對(duì)象來(lái)執(zhí)行對(duì)應(yīng)的方法,通過(guò)Constructor對(duì)象來(lái)調(diào)用對(duì)應(yīng)的構(gòu)造器創(chuàng)建對(duì)象,能通過(guò)Field對(duì)象直接訪問(wèn)并修改對(duì)象的屬性值
通過(guò)反射來(lái)生成對(duì)象有如下兩種方式:
a) 使用Class對(duì)象的newInstance()方法來(lái)創(chuàng)建該Class對(duì)象對(duì)應(yīng)類的實(shí)例,這種方法要求該Class對(duì)象的對(duì)應(yīng)類有默認(rèn)構(gòu)造器,而執(zhí)行newInstance()方法時(shí)實(shí)際上是利用默認(rèn)構(gòu)造器來(lái)創(chuàng)建該類的實(shí)例
b) 先利用Class對(duì)象獲取指定的Constructor對(duì)象,再調(diào)用Constructor對(duì)象的newInstance()方法來(lái)創(chuàng)建該Class對(duì)象對(duì)應(yīng)類的實(shí)例,通過(guò)這種方式可以選擇使用某個(gè)類的制定構(gòu)造器來(lái)創(chuàng)建實(shí)例
6、實(shí)際上只有當(dāng)程序需要?jiǎng)討B(tài)地創(chuàng)建該對(duì)象時(shí)才會(huì)考慮使用反射,通常在開(kāi)發(fā)通用性比較廣的框架和基礎(chǔ)平臺(tái)時(shí)可能會(huì)大量使用反射
7、當(dāng)獲得某個(gè)類對(duì)應(yīng)的Class對(duì)象后,就可以通過(guò)該Class對(duì)象的getMethods()方法或者getMethod()方法來(lái)獲取全部或指定方法----這兩個(gè)方法的返回值是Method對(duì)象數(shù)組,或者M(jìn)ethod對(duì)象
每個(gè)Method對(duì)象包含一個(gè)方法,獲得Method對(duì)象后,程序就可通過(guò)該Method來(lái)調(diào)用對(duì)應(yīng)方法,在Method里包含一個(gè)invoke方法
Obejct invoke(Object obj, Object …args);該方法中的obj是執(zhí)行該方法的主調(diào),后面的args是執(zhí)行該方法時(shí)傳入該方法的實(shí)參
當(dāng)通過(guò)Method的invoke方法來(lái)調(diào)用對(duì)應(yīng)的方法時(shí),Java會(huì)要求程序必須有調(diào)用該方法的權(quán)限,如果程序確實(shí)需要調(diào)用某個(gè)對(duì)象的invoke方法,可以先調(diào)用Method對(duì)象的如下方法:
setAccessible(boolean flag):將flag對(duì)象的accessible標(biāo)志設(shè)置為指示的Boolean值
true表示該Method在使用時(shí)應(yīng)該取消Java語(yǔ)言訪問(wèn)權(quán)限檢查
8、訪問(wèn)屬性值
通過(guò)對(duì)象的getFields()或getField()方法可以獲取該類所包括的全部Field(屬性)或指定的Field,Field提供如下兩組方法來(lái)訪問(wèn)屬性:
getXxx(Object obj):獲取obj對(duì)象該Field的屬性值
setXxx(Object obj, Xxx val):將obj對(duì)象的該Field設(shè)置成val值
使用這兩個(gè)方法可以隨意地訪問(wèn)指定對(duì)象的所有屬性,包括private訪問(wèn)控制的屬性
必須通過(guò)setAccessible(true)取消訪問(wèn)前檢查
9、操作數(shù)組
在java.lang.reflect包下還提供了一個(gè)Array類,Array對(duì)象可以代表所有的數(shù)組
10、使用反射生成JDK動(dòng)態(tài)代理
在Java的java.lang.reflect包下提供了一個(gè)Proxy類和一個(gè)InvocationHandler接口,通過(guò)使用這個(gè)類和接口可以生成JDK動(dòng)態(tài)代理類或動(dòng)態(tài)代理對(duì)象
Proxy提供了
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):直接創(chuàng)建一個(gè)動(dòng)態(tài)代理對(duì)象,該代理對(duì)象的實(shí)現(xiàn)類實(shí)現(xiàn)了interfaces指定的系列接口,執(zhí)行代理對(duì)象的每個(gè)方法時(shí)都會(huì)被替換執(zhí)行InvocationHandler對(duì)象的invoke方法
通常而言,當(dāng)我們使用Proxy生成一個(gè)動(dòng)態(tài)代理時(shí),往往并不會(huì)憑空產(chǎn)生一個(gè)動(dòng)態(tài)代理,通常都是為制定的目標(biāo)對(duì)象來(lái)生成動(dòng)態(tài)代理
這種動(dòng)態(tài)代理在AOP(Aspect Orient Program,即面向切面編程)里被稱為AOP代理,AOP代理可代替目標(biāo)對(duì)象,AOP代理包含了目標(biāo)對(duì)象的全部方法,但AOP代理中的方法與目標(biāo)對(duì)象的方法存在差異:AOP代理里的方法可以在執(zhí)行目標(biāo)方法之前,之后插入一些通用處理
希望通過(guò)本文的介紹,能夠給你帶來(lái)幫助。