什么是反射?
? 反射簡單來說,就是在代碼運(yùn)行期間,通過動態(tài)指定任意一個類,從而構(gòu)建對象,并了解該類的成員變量和方法,甚至可以調(diào)用任意一個對象的屬性和方法。以String對象為例子,傳統(tǒng)構(gòu)造方式和反射的實(shí)現(xiàn)方式如下:
//new 構(gòu)造方法
String s = new String();
//反射
Class<?> forName = Class.forName("java.lang.String");
? 從代碼中不難看到,第一種通過構(gòu)造函數(shù)構(gòu)造出來的對象,是在編譯前就確定好了的。由此編譯程序可以明確的知道當(dāng)前操作的類是哪個,包含哪些屬性、有什么方法。
? 然而對于第二種實(shí)現(xiàn)方式來說,通過反射映射出來的對象完全取決于你給定的字符串是什么。如果我們將這個字符串抽取成配置,那么生產(chǎn)出來的對象自然就會是動態(tài)的了。
為什么需要反射?
? 那么,你可能想問了,好好的new 對象不好嗎?為啥還要設(shè)計出來一個反射呀?他有什么好處呢?其實(shí),直白來講,反射犧牲掉了安全、效率,更多的是為了提供更多的靈活性。
? 假設(shè)當(dāng)前你所負(fù)責(zé)的代碼,要處理兩個不同的對象,那么為了完成工作,你可能需要通過多次的if-else判斷、處理,哪怕兩個對象內(nèi)的字段名稱、類型都是一樣的,受限于編譯器的限制,你也沒法共用同一段代碼。
if (i==1){
// ....執(zhí)行針對ObjectA的處理方法
ObjectA o = (ObjectA) input;
o.setData(xxx);
} else if (i==2) {
// ....執(zhí)行針對ObjectB的處理方法
ObjectB o = (ObjectB) input;
o.setData(xxx);
}
? 但是如果借助于反射,我們就可以將代碼充分利用起來,從而最大程度減少代碼開發(fā),提升日常效率。
if (i==1){
// ....執(zhí)行針對ObjectA的處理方法
Class<?> forName = Class.forName("com.example.demo.base.dto.ObjectA");
} else if (i==2) {
// ....執(zhí)行針對ObjectB的處理方法
Class<?> forName = Class.forName("com.example.demo.base.dto.ObjectB");
}
Object o = (Object) input;
//同一段處理邏輯,無需再寫兩次
Field resultStr = forName.getDeclaredField("resultStr");
resultStr.set(o, "1");
? 在日常代碼開發(fā)中,也有很多組件無形之中使用到了反射的設(shè)計原理。如我們熟悉的Bean拷貝組件BeanUtils,其方便的實(shí)現(xiàn)對象間的拷貝邏輯就是基于反射實(shí)現(xiàn)的。
?
如何實(shí)現(xiàn)反射?
? 首先我們知道,我們?nèi)粘K帉懙拇a,最后都是會變成.java格式的文檔。而.java格式的文檔是不可能被機(jī)器直接執(zhí)行的,因?yàn)闄C(jī)器只能夠看懂二進(jìn)制的文件。
? 因此,從.java文件到j(luò)vm中可執(zhí)行的對象,它其實(shí)經(jīng)過了如下的過程:
? 首先,我們的.java文件會被編譯器編譯成.class文件,對于每個.class文件,又會被JVM調(diào)用類加載的方式,加載到JVM內(nèi)存中并生成為一個Class對象。這里我們簡單看一下Class對象長什么模樣:
? 可以看到Class類對象中包含了許多用于描述類的屬性,如類名、所使用的類加載器等。包括通過Class類對象也可以搜索到當(dāng)前類的構(gòu)造器、特定的屬性域等。
? 而且對于這個特殊的類來說,我們是不能夠?qū)ζ溥M(jìn)行初始化的??梢钥吹?,它只提供了一個私有的構(gòu)造方法,且需要傳入的是類加載器。而且在方法的注釋上,也很明確的標(biāo)注出來,這個構(gòu)造方法只是提供給JVM創(chuàng)建實(shí)例用的,不應(yīng)該作為默認(rèn)的類構(gòu)造器。
? 通常來說,如果我們需要調(diào)用一個Class對象,那么通常只能通過以下的方式:
Class<?> clz = Class.forName("xxx.class");
Class<?> clz = Object.class;
Class<?> clz = Object.getClass();
? 而且通過不斷的往下搜索,我們也可以看到,這個方法的底層實(shí)現(xiàn)是通過native方法實(shí)現(xiàn)的。(部分java代碼通過c等語言實(shí)現(xiàn)的,此時會被標(biāo)記為native方法,是不可以直接調(diào)用的。)
? 雖然我們不能調(diào)用,但是本質(zhì)上這個方法就是通過類加載器獲取的過程。了解了這些之后,我們再將咱們的日常使用java類的過程梳理一下:
? 所以可以看到,反射本質(zhì)上,維護(hù)實(shí)例的流程同正常的new創(chuàng)建對象并無過多的差異,他們本質(zhì)都是基于class對象生成的實(shí)例,只是通過new的方式,用戶是能夠明確知道所需要使用的類的,因此編譯器也能方便的對代碼進(jìn)行檢查。
? 而使用反射的方式,則是利用了Class對象,創(chuàng)造出了編碼的靈活性。本質(zhì)思想就是:“你可以是任何類,但我的邏輯對任何類都適配。”通過這樣的犧牲掉一些安全性,從而獲得編碼的靈活性。
參考文獻(xiàn)
學(xué)習(xí)java應(yīng)該如何理解反射?文章來源:http://www.zghlxwxcb.cn/news/detail-575074.html
java的反射到底是有什么用處?怎么用?文章來源地址http://www.zghlxwxcb.cn/news/detail-575074.html
到了這里,關(guān)于【從零到Offer】反射那些事的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!