作者 | P6級程式設計師
責編 | 王曉曼
出品 | CSDN博客
首先學習反射之前,我要提出疑問:
反射是個什麼東西?它是用來做什麼的?平時的應用場景有哪些?為啥要用它?它有什麼優缺點?它的工作原理是什麼?我怎麼使用它?
這麼多的問題,這是在挑釁啊,既然如此,那麼我想起來宮本的那句:想挑戰的,一個一個來。先解決第一個問題。
此為何物
百度看了看反射的介紹:
超過二秒後,我表示看不下去了,就不能簡單點嗎?這是給人看的嗎?像我這種人,是看不下去的。
我們來一句話定義反射:
反射就是把 Java 類中的各種成分映射成一個個的 Java 對象。
不理解這句話什麼意思?沒關係,在我百度了幾分鐘後,找到三種解釋:
解釋一:一個類有:成員變量、方法、構造方法、包等等信息,利用反射技術可以對一個類進行解剖,把各個組成部分映射成一個個對象。
解釋二:說反射先聊聊正射。
反射機制是不知道類是什麼樣的,它是根據類的類名,去獲取一個實例,然後根據方法名去執行方法。好比說,一般情況下畫一隻老虎,問我得先知道老虎長什麼樣子才能畫出來;有了反射機制,我只要知道「老虎」這個名字就能畫出來。
解釋三:假如我們有兩個程式設計師,一個程式設計師在寫程序的時候,需要使用第二個程式設計師所寫的類,但第二個程式設計師並沒完成他所寫的類。那麼第一個程式設計師的代碼能否通過編譯呢?這是不能通過編譯的。利用Java反射的機制,就可以讓第一個程式設計師在沒有得到第二個程式設計師所寫的類的時候,來完成自身代碼的編譯。
解釋四:如果你是方法,快遞員是虛擬機。快遞員通過地址查地圖找你的叫反射調用。直接去找你的叫直接調用。
現在我們基本已經了解什麼是反射了,接著需要將第二個問題搞定。
該物用途
接著百度:
用途太多,概念也很多,我需要一句話就可以解釋它的作用或者用途:
反射可以賦予jvm動態編譯的能力。
看到又出現一個詞,動態編譯,來我們來嘮嘮這個詞。
Java中編譯類型有兩種:
如果不理解,那麼給個業務場景幫助你理解:比如開發一個閱讀器,支持txt,pdf,doc三種格式。我們把讀txt,讀pdf,讀doc定義為三個功能模塊。
顯然,動態編譯1速度快,2節省了系統資源,3利於今後拓展。
那麼這個JVM動態編譯常用的場景有哪些呢?或者說反射的使用場景(用途)有哪些?此物的用途?
場景一:在日常的第三方應用開發過程中,經常會遇到某個類的某個成員變量、方法或是屬性是私有的或是只對系統應用開放,這時候就可以利用Java的反射機制通過反射來獲取所需的私有成員或是方法。
場景二:當我們在使用IDE(如Eclipse,IDEA)時,當我們輸入一個對象或類並想調用它的屬性或方法時,一按點號,編譯器就會自動列出它的屬性或方法,這裡就會用到反射。
場景三:反射最重要的用途就是開發各種通用框架。很多框架(比如Spring)都是配置化的(比如通過XML文件配置JavaBean,Action之類的),為了保證框架的通用性,它們可能需要根據配置文件加載不同的對象或類,調用不同的方法。
為啥要用它?它有什麼優缺點?
Java的反射機制就是增加程序的靈活性,解耦。反射就是一種機制,可以讓你僅知道類的名字的情況下,可以了解整個類的內部的結構,並且訪問內部的成員和方法等。
解釋:對於大型的軟體,一個大公司的各個小組都有自己的分工,去實現不同的模塊,那麼各個小組之間如何協作就非常關鍵。例如A小組完成IPolicy接口的實現,而B小組需要使用A的實現,這時候就可以使用反射機制,B小組完全不用知道IPolicy是如何實現的,只需要知道實現後的類名即可,或者說,類名完全保存在一個xml或者屬性中,由A小組去填充,這樣B小組的代碼看上去就和A毫無瓜葛。
因此反射在一般框架中使用較多。因為框架要適用更多的情況。對靈活性要求較高。
優勢:
缺點:
相較直接調用在量大的情景下反射性能下降。
存在一些內部暴露和安全隱患。
針對它的缺點,我們聊聊反射到底慢在哪些地方。
反射的工作原理?反射技術的組成部分?
萬物皆對象,我們定義的類其實從面向對象的角度來分析,它其實也是一個具體的對象,它是一個描述類的實例。描述這個類中有哪些屬性,行為等等內容.。我們可以通過定義類,來描述一組具有相同屬性,行為的實例對象。比如我們創建Person 類。
Class Person {
String ID;
int age;Seven
void talk(){
}
}
我們可以基於這個類創建具體不同身份證號和姓名的 Person 實例(new Person)。每一個實例都具有身份證號,年齡,說話的行為。通過上面的簡單案例,我們可以這麼理解在Java 語言中Class 的定義,是創建對象的統一模板。
那麼我們可以思考這樣一個問題,既然不管是 Java 語言默認的類還是我們自定義創建的類都 是為了創建具有相同行為屬性的對象的模板。
那麼每一個類我們在定義的時候,是不是也可以抽取共性的東西,比如,每一個類都有包名,屬性定義,行為(方法),構造器等等。
那麼既然每一個類都會具備這樣的內容,那麼這些類對象實例,應該也可以抽取成一個公有的模板,用於創建類對象實例的模板。所以在java 中,這個類定義的創建模板就是我們 java 語言中的 java.lang.Class 類。在 Class 的模板中,我們也可以找到大家耳熟能詳的模板類如Method,Constructor,Field...
深入 Class 內部
通過上面的內容,我們已經了解到我們創建的每一個自定義的Class實例都是基於他的模板類java.lang.Class 類。在大家每一個編寫的類實例中,都會定義這個類的包名,類名,訪問域,特徵符,構造器,欄位,函數,父類,接口等等內容。這些內容在我們的 Class 類中都提供了對應的獲取方法進行獲取。
如何使用?
1、反射-基本信息操作
intmodifier = clazz.getModifiers(); //獲取類的修飾符
Packagepackage= clazz.getPackage();//獲取類的包名
StringfullClassName = clazz.getName();//獲取類的全路徑名稱
StringsimpleClassName = clazz.getSimpleName();//獲取類的簡單名稱
ClassLoaderclassLoader = clazz.getClassLoader();//獲取類的類加載器
Class[]interfacesClasses = clazz.getInterfaces();//獲取類實現的接口列表
Classfc= clazz.getSuperclass();//獲取類的父類
Annotation[]annotations= clazz.getAnnotations(); //獲取類的註解列表
2、反射-欄位操作
Field[]fields = clazz.getFields();//獲取類中所有的公有欄位包含繼承
Field[]declaredFields=clazz.getDeclaredFields();//獲取類中定義的欄位 內部
FieldnameField=clazz.getField("name");//獲取指定名稱的公有欄位
FieldlikeDescField=clazz.getDeclaredField("likeDesc");//獲取指定名稱類中定義的欄位
intmodifersFiled = likeDescField.getModifiers();//獲取欄位的修飾
nameField.setAccessible(true);//指定欄位強制訪問
nameField.set(person,"小皮皮");//成員欄位賦值(需指定對象)
descriptionField.set(null,"沒有結婚的都是男孩!");//靜態欄位賦值
3、反射-方法操作
Method[]methods = clazz.getMethods();//獲取類中所有的公有方法繼承
Method[]declaredMethods = clazz.getDeclaredMethods();//獲取類中定義的方法
MethodtalkMethod = clazz.getMethod("talk", String.class);//獲取類中指定名稱和參數的公有方法
MethodpugMethod = clazz.getDeclaredMethod("pickUpGirls") //獲取類中定義指定名稱和參數的方法
intmodifers = pugMethod .getModifiers();//獲取方法的修飾符
talkMethod.invoke(boy,"ILOVE SEVEN");//指定對象進行成員方法的調用
pugMethod.setAccessible(true);//指定方法的強制訪問
pickUpGirlsMethod.invoke(null);//靜態方法的調用
4、反射-構造器操作
Constructor[]cons = clazz.getConstructors();//獲取類中所有的公有構造器
Constructor[]cons = clazz.getDeclaredConstructors();//獲取類中所有的構造器
ConstructorconNoParam= clazz.getDeclaredConstructor();//獲取類中無參的構造器
Constructorcon= clazz.getDeclaredConstructor(String.class,String.class); //獲取類中有參構造
intmodifers = con.getModifiers();//獲取構造器的修飾符
conNoParam.newInstance();//構造器實例對象
con.setAccessible(true);//指定方法的強制訪問
con.newInstance('abc','def');//有參構造調用
class.newInstacne();//class直接調用默認無參構造
舉一反三
疑問一:現在我們基本解決上面提出的幾個問題了,有了一個基本的了解之後,有沒有想起我們常常被面試的時候,問到的Spring框架IOC控制反轉,是不是跟反射有那麼一些關聯?或者說SpringIOC容器它是怎麼做到控制反轉的?
疑問二:僅知道類的名字的情況下,可以了解整個類的內部的結構,並且訪問內部的成員和方法等。那麼針對私有的一些方法,或者構造器,豈不是可以破壞它,比如說:通過反射機制可以破壞單例模式,它為啥可以做到這一點的?通過反射機制可以破壞單例模式
版權聲明:本文為CSDN博主「P6級程式設計師」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本聲明。
原文連結:https://blog.csdn.net/java_programmer_liao/article/details/106013181
6月2日20:00,CSDN 創始人&董事長、極客幫創投創始合伙人蔣濤攜手全球頂級開源基金會主席、董事,聚焦中國開源現狀,直面開發者在開源技術、商業上的難題,你絕不可錯過的開源巔峰對談!立即免費圍觀:更多精彩推薦
☞大佬 Zed 玩轉跨界:不會繪畫的音樂家不是好程式設計師
☞航拍高手、吉他十級,6500+Star 開源項目作者,後浪程式設計師給力!
☞面試官:你的 SQL 一般有幾個 join?| 原力計劃
☞基於深度學習和傳統算法的人體姿態估計,技術細節都講清楚了
☞面試中遇到這 3 個SQL問題,最容易掉坑裡!
☞好撲科技結合區塊鏈行業發展趨勢,重磅推出「好撲區塊鏈合伙人」計劃
點擊閱讀原文,精彩繼續。