在談?wù)摰椒瓷溥@個(gè)問(wèn)題時(shí),你是否有如下疑問(wèn)?
無(wú)論是在.NET還是Java中反射的原理和機(jī)制是一樣的,理解了一種另一種就可以迎刃而解,想要理解反射首先需要了解底層的一些概念和運(yùn)行,理解了反射有助于你理解程序的運(yùn)行原理,目前很多框架(java、.NET)中都引入了反射這一個(gè)技術(shù),反射其實(shí)也不是什么新的技術(shù)只是幾個(gè)不同的操作過(guò)程集成到一起關(guān)聯(lián)起來(lái)了。
從表面上我們看到的效果是這樣的:通過(guò)傳入一個(gè)字符串可以得到某個(gè)類(lèi)的對(duì)象,在這一個(gè)過(guò)程中做了很多事情。你是否有下面的一些疑問(wèn)存在?
JDK、JRE是一回事嗎?
JRE和JVM有聯(lián)系嗎?
JVM和類(lèi)加載器什么關(guān)系?
類(lèi)加載器加載類(lèi)的過(guò)程?
反射和類(lèi)加載之間有什么神秘關(guān)系?
看完這篇文章,也許你會(huì)對(duì)反射有一個(gè)清晰的認(rèn)識(shí)。
JDK是為我們開(kāi)發(fā)提供的一個(gè)開(kāi)發(fā)類(lèi)庫(kù),里面存在著大量的開(kāi)發(fā)類(lèi),而JRE是開(kāi)發(fā)好的程序運(yùn)行的環(huán)境,也就是說(shuō)你的電腦上如果想運(yùn)行java程序可以沒(méi)有JDK,但要有JRE這個(gè)運(yùn)行環(huán)境,往往下載了JDK開(kāi)發(fā)包已經(jīng)包含了JRE這個(gè)環(huán)境,安裝時(shí)是可以選擇不安裝JRE的,你開(kāi)發(fā)好的程序都需要測(cè)試、運(yùn)行等,因此有必要安裝JRE。
而JVM是存在JRE這個(gè)環(huán)境里的,只是在JRE這個(gè)環(huán)境里面不止僅有JVM,JVM是必須的,如果沒(méi)有其它的類(lèi)輔助JVM運(yùn)行,JVM是沒(méi)有辦法運(yùn)行的,舉個(gè)簡(jiǎn)單的例子來(lái)看看好想紅花也許綠葉來(lái)襯托、如果沒(méi)有綠葉的襯托怎么會(huì)顯示出紅花的價(jià)值呢。
類(lèi)加載器這些類(lèi)是JVM提供的,負(fù)責(zé)把類(lèi)(.class文件)讀入到內(nèi)存中,并且為每個(gè)加載到內(nèi)存中的類(lèi)創(chuàng)建一個(gè)Class對(duì)象,你可以理解為一般我們看到的類(lèi)都有一個(gè)超類(lèi)Class,當(dāng)一個(gè)類(lèi)加載時(shí)就會(huì)為這個(gè)類(lèi)實(shí)例化一個(gè)Class對(duì)象,這個(gè)對(duì)象負(fù)責(zé)唯一標(biāo)示該類(lèi),事實(shí)表明這個(gè)Class對(duì)象非常有用,如論是反射還是注解等的實(shí)現(xiàn)都依賴(lài)于這一對(duì)象,我們通過(guò)這個(gè)Class對(duì)象里面的方法可以獲取到任何一個(gè)類(lèi)的所有方法(包括父類(lèi)集成來(lái)的)、所有字段(包括私有屬性)、構(gòu)造器等等,在上一篇注解博客中核心就是利用了Class對(duì)象的getMethods()方法,得到一個(gè)類(lèi)的所有方法,然后循環(huán)判斷注解才實(shí)現(xiàn)對(duì)注解方法起作用。
獲得Class對(duì)象的三種方式
1.Class類(lèi)的forName(String clazzName)
2.調(diào)用某個(gè)類(lèi)的class屬性,如Person.class
3.某個(gè)對(duì)象的getClass()方法
類(lèi)加載的步驟(想了解的更細(xì)節(jié)可以看一下JVM規(guī)范)
1.加載
指的是將.class文件讀入內(nèi)容,并為之創(chuàng)建一個(gè)Class對(duì)象;可以理解為所有的類(lèi)也是實(shí)例,它們都是java.lang.Class這個(gè)類(lèi)的實(shí)例。
加載類(lèi)的途徑
A:從本地文件系統(tǒng)中加載.class文件
B:從JAR包中加載.class文件,例如你連接mysql或oracle數(shù)據(jù)庫(kù)時(shí),是不是有一個(gè)驅(qū)動(dòng)jar包,驅(qū)動(dòng)類(lèi)都放在這個(gè)jar中,再多說(shuō)一點(diǎn):關(guān)于驅(qū)動(dòng)jar文件,一個(gè)驅(qū)動(dòng)可以連接哪個(gè)數(shù)據(jù)庫(kù)或者支持啥功能,本地事務(wù)還是全局事務(wù),主要看驅(qū)動(dòng)里面支持不支持。
C:網(wǎng)絡(luò)加載
D;java文件先編譯,再加載
2.連接
將內(nèi)容中的.class二進(jìn)制文件讀入到JRE代表的進(jìn)程內(nèi)容中,又分為驗(yàn)證、準(zhǔn)備、解析三個(gè)過(guò)程。
3.初始化
主要是對(duì)一些靜態(tài)字段賦值操作,初始化時(shí)可能并沒(méi)有類(lèi)的實(shí)例呢,所以是只初始化類(lèi)范圍的變量,如static修飾的變量。
初始化不僅僅是對(duì)目標(biāo)類(lèi)初始化,如果它有繼承的父類(lèi),它的父類(lèi)會(huì)都初始化,我們知道所有類(lèi)都是object的子類(lèi),object每次都會(huì)被初始化,這也解析了為什么你可以調(diào)用Object這個(gè)類(lèi)的方法,因此它也初始化了。
類(lèi)加載器
從上圖中我們可以得知哪些類(lèi)加載器以及它們主要負(fù)責(zé)加載哪些類(lèi),其中MyClassLoader1/2是自定義類(lèi)加載器,可以從指定目錄加載類(lèi),即.java文件不再classpath路徑下也可以加載,有時(shí)我們會(huì)遇到找不到類(lèi)的問(wèn)題,其實(shí)就是類(lèi)路徑寫(xiě)的不錯(cuò)。
類(lèi)加載器加載類(lèi)算法?
內(nèi)存管理、CUP調(diào)度等都有自己的算法,比如先進(jìn)先出、最早使用原則,寫(xiě)算法的目的是實(shí)現(xiàn)資源合理調(diào)配,從各種方案中找到一種可以解決實(shí)際問(wèn)題的思路,類(lèi)再加載類(lèi)時(shí)也存在這樣的問(wèn)題,如遇到一個(gè).class類(lèi)后,讓哪一個(gè)加載器加載?去哪里找加載的類(lèi)?等等,電腦是很傻的,不要把電腦想的很聰明,不要讓電腦去做選擇,它的選擇是我們給它指定的。
算法
依賴(lài)原則:當(dāng)一個(gè)類(lèi)加載時(shí),它所依賴(lài)的類(lèi)同時(shí)被加載。
尊老愛(ài)幼::針對(duì)加載器,每次加載類(lèi)都讓著長(zhǎng)輩,父加載器優(yōu)先。
緩存:所有加載好的類(lèi)放入一個(gè)緩存中,加載某個(gè)類(lèi)時(shí)先去緩存中查找,不存在的話才去加載(如果你修改了一個(gè)加載好的類(lèi),也是存在的不去重新加載),這也是為什么每次我們修改了一個(gè)類(lèi)后,需要重新啟動(dòng)tomcat即重啟JVM。
上面這些是實(shí)現(xiàn)反射的基礎(chǔ),總結(jié)就兩點(diǎn)一是Class對(duì)象;而是類(lèi)加載器;反射主要是依賴(lài)于java的這兩個(gè)特性實(shí)現(xiàn)的反射過(guò)程,下篇中將用一個(gè)實(shí)例來(lái)實(shí)現(xiàn)反射,通過(guò)從屬性文件或者配置文件中讀取類(lèi)的字符串信息來(lái)實(shí)例化類(lèi),Spring框架也是利用的這一個(gè)過(guò)程實(shí)現(xiàn)依賴(lài)注入的。
對(duì)于底層的一些東西覺(jué)的還是有必要理解、并可以使用,各種框架都依賴(lài)于底層,這對(duì)學(xué)習(xí)框架也是很有幫助的。
上一篇 POJ 3181 Dollar Dayz 01完全背包問(wèn)題
下一篇 Machine Learning―Mixtures of Gaussians and the EM algorithm