好久不見(jiàn),在疫情的控制下,我急需一杯奶茶續(xù)續(xù)命寇蚊!
作者:王炸 |【堅(jiān)持1000篇原創(chuàng)】
2020.2.21 王炸的第60篇原創(chuàng)
??先贊后看是技術(shù)人的傳統(tǒng)美德??
有小朋友問(wèn)我笔时,我剛剛學(xué)Java,沒(méi)接觸過(guò)項(xiàng)目仗岸,也沒(méi)機(jī)會(huì)用Spring之類(lèi)的框架允耿,看書(shū)也知道反射都有什么內(nèi)容,哪本書(shū)都說(shuō)反射很有用扒怖,但到現(xiàn)在都不知道它用在啥上面较锡,所有,反射有啥用盗痒?
關(guān)于反射蚂蕴,我必須要吐槽一下,初學(xué)的時(shí)候如果靠看xxx從入門(mén)到精通系列的書(shū)俯邓,理解反射可太TM困難了骡楼,看的我懷疑人生,那個(gè)時(shí)候我一度懷疑稽鞭,我該不是個(gè)智障吧鸟整!
今天來(lái)分析下:圍繞Java反射,BAT的面試官可以問(wèn)出多少花樣朦蕴。
反射關(guān)系到Java的語(yǔ)言特性篮条,jvm的內(nèi)存細(xì)節(jié)
JVM:只能跑Java代碼的CPU
剛剛開(kāi)始我也不理解Java的虛擬機(jī)到底算個(gè)什么東西?
第一節(jié)接觸這東西一臉困惑吩抓,字節(jié)碼代碼是在CPU上執(zhí)行涉茧?在虛擬機(jī)上執(zhí)行?虛擬機(jī)又是個(gè)什么東西疹娶?
后來(lái)我慢慢試著理解:JVM就是跑才CPU上的一個(gè)虛擬CPU降瞳,但是這個(gè)CPU只能跑Java代碼
Java之所以能跨平臺(tái)就是因?yàn)檫@個(gè)東西,你可以理解成一個(gè)進(jìn)程,程序挣饥,只不過(guò)他的作用是用來(lái)跑你的代碼的除师。
上圖是java的內(nèi)存模型,我們關(guān)注的點(diǎn)扔枫,一個(gè)方法區(qū)汛聚,一個(gè)棧,一個(gè)堆短荐,初學(xué)的時(shí)候老師不深入的話只告訴你java的內(nèi)存分為堆和棧
假如你寫(xiě)了一段代碼:String s=new String();
運(yùn)行了起來(lái)倚舀!這里面發(fā)生了什么?
首先JVM會(huì)啟動(dòng)忍宋,你的代碼會(huì)編譯成一個(gè)xxx.class文件痕貌。
然后被類(lèi)加載器加載進(jìn)jvm的內(nèi)存中(虛擬CPU),你的類(lèi)Object加載到方法區(qū)中糠排,創(chuàng)建了Object類(lèi)的class對(duì)象到堆中舵稠,注意這個(gè)不是new出來(lái)的對(duì)象,而是類(lèi)的類(lèi)型對(duì)象入宦,每個(gè)類(lèi)只有一個(gè)class對(duì)象哺徊,作為方法區(qū)類(lèi)的數(shù)據(jù)結(jié)構(gòu)的接口。
??注意:jvm創(chuàng)建對(duì)象前乾闰,會(huì)先檢查類(lèi)是否加載落追,尋找類(lèi)對(duì)應(yīng)的class對(duì)象,若加載好涯肩,則為你的對(duì)象分配內(nèi)存轿钠,初始化也就是代碼:new Object()。
上面的流程就是你自己寫(xiě)好的代碼扔給jvm去跑病苗,跑完就over了谣膳,jvm關(guān)閉,你的程序也停止了铅乡。
為什么會(huì)發(fā)明反射這種技術(shù)继谚?
想想上面的程序?qū)ο笫亲约簄ew的,程序相當(dāng)于寫(xiě)死了給jvm去跑阵幸,程序員都是聰明的動(dòng)物花履,還有沒(méi)有更好的辦吧不用寫(xiě)死,不用new挚赊。
假如一個(gè)服務(wù)器上突然遇到某個(gè)請(qǐng)求哦要用到某個(gè)類(lèi)诡壁,哎呀但沒(méi)加載進(jìn)jvm,是不是要停下來(lái)自己寫(xiě)段代碼荠割,new一下妹卿,哦啟動(dòng)一下服務(wù)器旺矾,(腦殘)!
這個(gè)時(shí)候反射技術(shù)誕生了夺克,這在當(dāng)時(shí)箕宙,是一個(gè)非常先進(jìn)的理念,Java也因此得到更多人認(rèn)可铺纽。
反射是什么呢柬帕?
當(dāng)我們的程序在運(yùn)行時(shí),需要?jiǎng)討B(tài)的加載一些類(lèi)這些類(lèi)可能之前用不到所以不用加載到j(luò)vm狡门,而是在運(yùn)行時(shí)根據(jù)需要才加載陷寝,這樣的好處對(duì)于服務(wù)器來(lái)說(shuō)不言而喻。
Java的反射機(jī)制是在編譯并不確定是哪個(gè)類(lèi)被加載了其馏,而是在程序運(yùn)行的時(shí)候才加載凤跑、探知、自審叛复。使用在編譯期并不知道的類(lèi)仔引。這樣的特點(diǎn)就是反射。
有兩個(gè)程序員致扯,一個(gè)程序員在寫(xiě)程序的時(shí)候肤寝,需要使用第二個(gè)程序員所寫(xiě)的類(lèi)当辐,但第二個(gè)程序員并沒(méi)完成他所寫(xiě)的類(lèi)抖僵,這時(shí)第一個(gè)程序員不能通過(guò)編譯,但是利用反射就可以通過(guò)缘揪。原因是java的反射機(jī)制它知道類(lèi)的基本結(jié)構(gòu)耍群,這種對(duì)Java類(lèi)結(jié)構(gòu)探知的能力,我們稱(chēng)為Java類(lèi)的“自審”找筝。
舉個(gè)例子:
我們的項(xiàng)目底層有時(shí)是用Mysql蹈垢,有時(shí)用oracle,需要?jiǎng)討B(tài)地根據(jù)實(shí)際情況加載驅(qū)動(dòng)類(lèi)袖裕,這個(gè)時(shí)候反射就有用了曹抬。
假設(shè) com.java.dbtest.myqlConnection,com.java.dbtest.oracleConnection這兩個(gè)類(lèi)我們要用急鳄。
這時(shí)候我們的程序就寫(xiě)得比較動(dòng)態(tài)化谤民,通過(guò)Class tc = Class.forName("com.java.dbtest.TestConnection");
通過(guò)類(lèi)的全類(lèi)名讓jvm在服務(wù)器中找到并加載這個(gè)類(lèi),而如果是oracle則傳入的參數(shù)就變成另一個(gè)了疾宏。
這時(shí)候就可以看到反射的好處了张足,這個(gè)動(dòng)態(tài)性就體現(xiàn)出java的特性了!
舉多個(gè)例子坎藐,大家如果接觸過(guò)spring为牍,會(huì)發(fā)現(xiàn)當(dāng)你配置各種各樣的bean時(shí),是以配置文件的形式配置的,你需要用到哪些bean就配哪些碉咆,spring容器就會(huì)根據(jù)你的需求去動(dòng)態(tài)加載抖韩,你的程序就能健壯地運(yùn)行。
好吧吟逝,圍繞Java反射帽蝶,BAT的面試官可以問(wèn)出多少花樣
面試:Java反射機(jī)制在Spring IOC中的應(yīng)用
IOC:即“控制反轉(zhuǎn)”,不是什么技術(shù)块攒,而是一種思想励稳。
使用IOC意味著將你設(shè)計(jì)好的對(duì)象交給容器控制,而不是傳統(tǒng)的在你的對(duì)象內(nèi)部直接控制囱井。
IOC底層實(shí)現(xiàn)的原理(反射)驹尼,Bean容器的實(shí)現(xiàn),就不對(duì)IOC的概念進(jìn)行詳述了庞呕。
在Spring的配置文件中新翎,經(jīng)常看到如下配置:
<bean id="courseDao" class="com.qcjy.learning.Dao.impl.CourseDaoImpl"></bean>
那么通過(guò)這樣配置住练,Spring是怎么幫我們實(shí)例化對(duì)象地啰,并且放到容器中去了了?對(duì)讲逛,就是通過(guò)反射?髁摺!盏混!
面試:Java反射在動(dòng)態(tài)代理中的應(yīng)用
JDK動(dòng)態(tài)代理
Java動(dòng)態(tài)代理類(lèi)位于Java.lang.reflect包下蔚鸥,一般主要涉及到以下兩個(gè)類(lèi):
Interface InvocationHandler:該接口中僅定義了一個(gè)方法Object,invoke(Object obj,Method method, Object[] args)许赃。在實(shí)際使用時(shí)止喷,第一個(gè)參數(shù)obj一般是指代理類(lèi),method是被代理的方法混聊,如上例中的request()弹谁,args為該方法的參數(shù)數(shù)組。這個(gè)抽象方法在代理類(lèi)中動(dòng)態(tài)實(shí)現(xiàn)句喜。
Proxy:該類(lèi)即為動(dòng)態(tài)代理類(lèi)预愤。
Cglib動(dòng)態(tài)代理
JDK的動(dòng)態(tài)代理機(jī)制只能代理實(shí)現(xiàn)了接口的類(lèi),而不能實(shí)現(xiàn)接口的類(lèi)就不能實(shí)現(xiàn)JDK的動(dòng)態(tài)代理藤滥,cglib是針對(duì)類(lèi)來(lái)實(shí)現(xiàn)代理的鳖粟,他的原理是對(duì)指定的目標(biāo)類(lèi)生成一個(gè)子類(lèi),并覆蓋其中方法實(shí)現(xiàn)增強(qiáng)拙绊,但因?yàn)椴捎玫氖抢^承向图,所以不能對(duì)final修飾的類(lèi)進(jìn)行代理泳秀。JDK代理要求被代理的類(lèi)必須實(shí)現(xiàn)接口,有很強(qiáng)的局限性榄攀。而CGLIB動(dòng)態(tài)代理則沒(méi)有此類(lèi)強(qiáng)制性要求嗜傅。簡(jiǎn)單的說(shuō),CGLIB會(huì)讓生成的代理類(lèi)繼承被代理類(lèi)檩赢,并在代理類(lèi)中對(duì)代理方法進(jìn)行強(qiáng)化處理(前置處理吕嘀、后置處理等)。在CGLIB底層贞瞒,其實(shí)是借助了ASM這個(gè)非常強(qiáng)大的Java字節(jié)碼生成框架偶房。
Javassist代理
一種是使用代理工廠創(chuàng)建,另一種通過(guò)使用動(dòng)態(tài)代碼創(chuàng)建军浆。使用代理工廠創(chuàng)建時(shí)棕洋,方法與CGLIB類(lèi)似,也需要實(shí)現(xiàn)一個(gè)用于代理邏輯處理的Handler乒融;使用動(dòng)態(tài)代碼創(chuàng)建掰盘,生成字節(jié)碼,這種方式可以非常靈活赞季,甚至可以在運(yùn)行時(shí)生成業(yè)務(wù)邏輯愧捕。
vx搜索:轉(zhuǎn)行程序員