一分鐘瞭解反射

首先分享一篇關於反射的博文,因爲我發現這篇博文寫的很詳細,地址是:https://blog.csdn.net/sinat_38259539/article/details/71799078

然後開始我的表演:

首先學習反射之前,我要提出疑問:

反射是個什麼東西?它是用來做什麼的?平時的應用場景有哪些?爲啥要用它?它有什麼優缺點?它的工作原理是什麼?我怎麼使用它?

這麼多的問題,這是在挑釁啊,既然如此,那麼我想起來宮本的那句:想挑戰的,一個一個來

先解決第一個問題:

此爲何物

百度看了看反射的介紹:

超過二秒後,我表示看不下去了,就不能簡單點嗎?這是給人看的嗎?像我這種人,是看不下去的。

我們來一句話定義反射:

反射就是把java類中的各種成分映射成一個個的Java對象

不理解這句話什麼意思?沒關係,在我百度了幾分鐘後,找到三種解釋:

解釋一:一個類有:成員變量、方法、構造方法、包等等信息,利用反射技術可以對一個類進行解剖,把個個組成部分映射成一個個對象。

解釋二: 說反射先聊聊正射

反射機制是不知道類是什麼樣的,它是根據類的類名,去獲取一個實例,然後根據方法名去執行方法。好比說,一般情況下畫一隻老虎,問我得先知道老虎長什麼樣子才能畫出來;有了反射機制,我只要知道“老虎”這答個名字就能畫出來。

解釋三:假如我們有兩個程序員,一個程序員在寫程序的時候,需要使用第二個程序員所寫的類,但第二個程序員並沒完成他所寫的類。那麼第一個程序員的代碼能否通過編譯呢?這是不能通過編譯的。利用Java反射的機制,就可以讓第一個程序員在沒有得到第二個程序員所寫的類的時候,來完成自身代碼的編譯。

解釋四:如果你是方法,快遞員是虛擬機。快遞員通過地址查地圖找你的叫反射調用。直接去找你的叫直接調用。

現在我們基本已經瞭解什麼是反射了,接着需要將第二個問題搞定:

該物用途

然後接着百度:

 用途太多,概念也很多,我需要一句話就可以解釋它的作用或者用途:

反射可以賦予jvm動態編譯的能力

看到又出現一個詞,動態編譯,來我們來嘮嘮這個詞

Java中編譯類型有兩種:

 

  • 靜態編譯:一次性編譯。在編譯的時候把你所有的模塊都編譯進去。
  • 動態編譯:按需編譯。程序在運行的時候,用到那個模塊就編譯哪個模塊。

如果不理解,那麼給個業務場景幫助你理解:比如開發一個閱讀器,支持txt,pdf,doc三種格式。我們把讀txt,讀pdf,讀doc定義爲三個功能模塊。

  • 靜態編譯:我想看個txt,點擊應用程序圖標以後,三個功能都加載進來了。在這裏,另外兩個模塊的作用就是佔用系統資源。
  • 動態編譯:我想看個txt,點擊應用程序,判斷格式,只加載讀txt模塊,使用讀txt模塊。

顯然,動態編譯1速度快,2節省了系統資源,3利於今後拓展。

那麼這個JVM動態編譯常用的場景有哪些呢?或者說反射的使用場景(用途)有哪些?此物的用途?

  • 場景一:在日常的第三方應用開發過程中,經常會遇到某個類的某個成員變量、方法或是屬性是私有的或是隻對系統應用開放,這時候就可以利用Java的反射機制通過反射來獲取所需的私有成員或是方法。
  • 場景二:當我們在使用IDE(如Eclipse,IDEA)時,當我們輸入一個對象或類並想調用它的屬性或方法時,一按點號,編譯器就會自動列出它的屬性或方法,這裏就會用到反射。
  • 場景三:反射最重要的用途就是開發各種通用框架。很多框架(比如Spring)都是配置化的(比如通過XML文件配置JavaBean,Action之類的),爲了保證框架的通用性,它們可能需要根據配置文件加載不同的對象或類,調用不同的方法。

爲啥要用它?它有什麼優缺點?

java的反射機制就是增加程序的靈活性,解耦。反射就是一種機制,可以讓你僅知道類的名字的情況下,可以瞭解整個類的內部的結構,並且訪問內部的成員和方法等。

解釋:對於大型的軟件,一個大公司的各個小組都有自己的分工,去實現不同的模塊,那麼各個小組之間如何協作就非常關鍵。例如A小組完成IPolicy接口的實現,而B小組需要使用A的實現,這時候就可以使用反射機制,B小組完全不用知道IPolicy是如何實現的,只需要知道實現後的類名即可,或者說,類名完全保存在一個xml或者屬性中,由A小組去填充,這樣B小組的代碼看上去就和A毫無瓜葛。

因此反射在一般框架中使用較多。因爲框架要適用更多的情況。對靈活性要求較高。

優勢:

  • 增加程序的靈活性,避免將固有的邏輯程序寫死到代碼裏
  • 代碼簡潔,可讀性強,可提高代碼的複用率

缺點:

  • 相較直接調用在量大的情景下反射性能下降
  • 存在一些內部暴露和安全隱患

針對它的缺點,我們聊聊反射到底慢在哪些地方

  • 尋找類 Class 字節碼的過程
  • 安全管理機制的權限驗證等等
  • 若需要調用 native 方法調用時 JNI 接口的使用

反射的工作原理?反射技術的組成部分?

萬物皆對象,我們定義的類其實從面向對象的角度來分析,它其實也是一個具體的對象,它是一個描述類的實例。描述這個類中有哪些屬性,行爲等等內容.。我們可以通過定義類,來描述一組具有相同屬性,行爲的實例對象。比如我們創建 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 類中都提供了對應的獲取方法進行獲取。

如何使用?

反射-基本信息操作

  • int modifier = clazz.getModifiers(); //獲取類的修飾符
  • Package package= clazz.getPackage();//獲取類的包名
  • String fullClassName = clazz.getName();//獲取類的全路徑名稱
  • String simpleClassName = clazz.getSimpleName();//獲取類的簡單名稱
  • ClassLoader classLoader = clazz.getClassLoader();//獲取類的類加載器
  • Class[] interfacesClasses = clazz.getInterfaces();//獲取類實現的接口列表
  • Class fc= clazz.getSuperclass();//獲取類的父類
  • Annotation[] annotations= clazz.getAnnotations(); //獲取類的註解列表

反射-字段操作

  • Field[] fields = clazz.getFields();//獲取類中所有的公有字段 包含繼承
  • Field[] declaredFields=clazz.getDeclaredFields();//獲取類中定義的字段 內部
  • Field nameField=clazz.getField("name");//獲取指定名稱的公有字段
  • Field likeDescField=clazz.getDeclaredField("likeDesc");//獲取指定名稱類中定義的字段
  • int modifersFiled = likeDescField.getModifiers();//獲取字段的修飾
  • nameField.setAccessible(true);//指定字段強制訪問
  • nameField.set(person,"小皮皮");//成員字段賦值(需指定對象)
  • descriptionField.set(null,"沒有結婚的都是男孩!");//靜態字段賦值

反射-方法操作

  • Method[] methods = clazz.getMethods();//獲取類中所有的公有方法 繼承
  • Method[] declaredMethods = clazz.getDeclaredMethods();//獲取類中定義的方法
  • Method talkMethod = clazz.getMethod("talk", String.class);//獲取類中指定名稱和參數的公有方法
  • Method pugMethod = clazz.getDeclaredMethod("pickUpGirls") //獲取類中定義指定名稱和參數的方法
  • int modifers = pugMethod .getModifiers();//獲取方法的修飾符
  • talkMethod.invoke(boy,"I LOVE SEVEN");//指定對象進行成員方法的調用
  • pugMethod .setAccessible(true);//指定方法的強制訪問
  • pickUpGirlsMethod.invoke(null);//靜態方法的調用

反射-構造器操作

  • Constructor[] cons = clazz.getConstructors();//獲取類中所有的公有構造器
  • Constructor[] cons = clazz.getDeclaredConstructors();//獲取類中所有的構造器
  • Constructor conNoParam= clazz.getDeclaredConstructor();//獲取類中無參的構造器
  • Constructor con= clazz.getDeclaredConstructor(String.class,String.class); //獲取類中有參構造
  • int modifers = con.getModifiers();//獲取構造器的修飾符
  • conNoParam.newInstance();//構造器實例對象
  • con.setAccessible(true);//指定方法的強制訪問
  • con.newInstance('abc','def');//有參構造調用
  • class.newInstacne();//class直接調用默認無參構造

舉一反三:

疑問一:現在我們基本解決上面提出的幾個問題了,有了一個基本的瞭解之後,有沒有想起我們常常被面試的時候,問到的Spring框架IOC控制反轉,是不是跟反射有那麼一些關聯?或者說SpringIOC容器它是怎麼做到控制反轉的?

疑問二:僅知道類的名字的情況下,可以瞭解整個類的內部的結構,並且訪問內部的成員和方法等。那麼針對私有的一些方法,或者構造器,豈不是可以破壞它,比如說:通過反射機制可以破壞單例模式,它爲啥可以做到這一點的?通過反射機制可以破壞單例模式

先在這裏埋下伏筆,後面我再補上答案,當然也歡迎大家在我的評論區留下你的答案,最好能通俗易懂,通過舉例的方式最好。

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章