Java 從 0 理解反射

一. 反射是什麼

1.1 概述

  • Java的反射機制:在程序的運行狀態中,可以任意構造一個類的對象,可以瞭解任意一個對象所屬的類,可以瞭解任意一個類的成員變量和方法,可以調用任意一個對象的屬性和方法。這種動態獲取程序信息以及動態調用對象的功能成爲Java語言的反射機制。反射被視爲動態語言的關鍵。
  • 什麼是動態語言:程序運行時,允許改變程序結構或者變量類型,這種語言成爲動態語言。
    • java不是動態語言,它卻有一個非常突出的動態相關機制:Reflection。 這個字的意思是“反射,映像,倒影”,用在Java身上指的是我們可以於運行時加載、探知、使用編譯期間完全未知的classes。
    • 換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。
    • 這種“看透class”的能力被稱爲introspection(內省、內觀、反省)。Reflection和introspection是常被提的兩個術語。
  • 用自己的話來講反射。就是我們平時在生成對象的時候,是通過new Class() 來創建一個對象,即我們先知道這個類是什麼,地址在哪裏,通過這個已經創建好了的類再來生成對象,去調用它的一個方法。即是對象先生成,我們再去調用。而反射則剛好相反。
  • 反射是一種強有力的工具,是面向抽象編程一種實現方式,它能使代碼語句更加靈活,極大提高代碼的運行時裝配能力。

1.2 反射與new對象的區別?

  • new只能用於編譯期就能確定的類型,而反射可以在運行時才確定類型並創建其對象。
  • 某些私有屬性或者方法在new中不能直接調用訪問,反射可以獲取此類中的所有屬性和方法。
  • new關鍵字是強類型的,效率高。反射是弱類型的效率低。
  • new關鍵字可以調用任何public的構造方法,而反射只能調用無參構造方法。
  • new創建對象是基於封裝,有序的。而反射可以破壞封裝,無視私有等修飾符。

1.3 反射提供了哪些功能?

  • 在運行時判定任意一個對象所屬的類。
  • 在運行時構造任意一個類的對象。
  • 在運行時判定任意一個類所具有的成員變量和方法。
  • 在運行時調用任意一個對象的方法。
  • 生成動態代理。

1.4 使用反射能做什麼

  1. 逆向代碼,反編譯
  2. 與自定義註解相結合,完成一些便捷功能。比如:redis自動緩存,PageQuery自動分頁,CheckToken自動校驗Token等各種操作(通過自定義註解,我們能拿到註解內的值,通過AOP的@Aound環繞通知功能,我們能進行切面。再通過反射調用其方法,可以進行一些預功能處理)
  3. 與業務需求相結合,能滿足一些new關鍵詞不能實現的功能,或者極大的減少開發量,提高開發效率。

1.5 反射帶來的後果

  • 性能問題:
    • Java反射機制中包含了一些動態類型,所以Java虛擬機不能夠對這些動態代碼進行優化。因此,反射操作的效率要比正常操作效率低很多。我們應該避免在對性能要求很高的程序或經常被執行的代碼中使用反射。而且如何使用反射決定了性能的高低。如果它作爲程序中較少運行的部分,性能將不會成爲一個問題。
  • 安全限制:
    • 使用反射通常需要程序的運行沒有安全方面的限制。如果一個程序對安全性提出要求,則最好不要使用反射。
  • 程序健壯性:
    • 反射允許代碼執行一些通常不被允許的操作,所以使用反射有可能會導致意想不到的後果。反射代碼破壞了Java程序結構的抽象性,所以當程序運行的平臺發生變化的時候,由於抽象的邏輯結構不能被識別,代碼產生的效果與之前會產生差異。

二. 反射的Api

2.1 Class類

  1. 概述:
    • Java在將.class字節碼文件載入時,JVM將產生一個java.lang.Class對象代表該.Class文件,從該Class對象中可以獲得類的許多基本信息,這就是反射機制。
      • 反射機制所需的類主要有java.lang包中的Class類和java.lang.reflect包中的Constructor類、Field類、Method類和Parameter類。
      • Class類是一個比較特殊的類,它是反射機制的基礎,Class類的對象表示正在運行的Java程序中的類或接口,也就是任何一個類被加載時,即將類的.class文件(字節碼)讀入內存的同時,都自動爲之創建一個java.lang.Class對象。
      • Class類沒有公共構造方法,其對象是JVM在加載類時通過調用類加載器中的defineClass()方法創建的,因此不能顯式地創建一個Class對象。通過這個Class對象,纔可以獲得該對象的其他信息。
  • 獲取Class對象的方式:

    • Class obj=Class.forName(String className);
      • 其中參數className表示所需類的全名;

      如果通過此類的全名找不到該類時,會跑出 ClassNotFoundException異常。

    • Class obj=類名.class;
      • 用類名調用該類的class屬性來獲得該類對應的Class對象。
    • obj.getClass();
      • 通過對象調用getClass()方法來獲得該類對應的Class對象,即“類名.class”

    三種獲取Class對象的最終結果一致,我們可以根據實際來選擇合適的方法。forName適合只知道全路徑類名的地方,常常用在application.yml等配置文件中定義。類名Class用在已經知道此類。對象.getClass()適合用在已經有此類的實現對象的情況下。

  1. Class反射類中包含的方法:
常用方法 功能說明
public Package getPackage() 返回Class對象所對應類的存放路徑
public static Class<?> forName(String className) 返回名稱爲className的類或接口的Class對象
public String getName() 返回Class對象所對應類的"包.類名" 形式的全名
public Class<? super T> getSuperclass 返回Class對象所對應的父類的Class對象
public Class<?>[] getInterfaces() 返回Class對象所對應類所實現的所有接口
public Annotation[] getAnnotations() 以數組的形式返回該程序元素上的所有註解
public Constructor getConstrutor(Class<?>…parameterTypes) 返回Class對象所對應類的指定參數列表的public構造方法
public Constructor<?>[] getConstructors 返回Class對象所對應類的所有public 構造方法
public Constructor getDeclaredConstructor(Class<?>…parameterTypes) 返回Class對象所對應類的指定參數列表的構造方法,與訪問權限無關。
public Constructor<?>[] getDeclaredConstructors() 返回Class對象所對應類的所有構造方法,與訪問權限無關
public Field getField(String name) 返回Class對象所對應類的名爲name的public成員變量
public Field[] getFields() 返回Class對象所對應類的所有public成員變量
public Field[] getDeclaredFields() 返回Class對象所對應類的所有成員變量,與訪問權限無關
public Method getMethod(String name,Class<?>… parameterTypes) 返回Class對象所對應的指定參數列表的public 方法
public Method[] getMethods() 返回Class對象所對應類的所有public 成員方法
public Method[] getDeclaredMethods 返回Class對象所對應類的所有成員方法,與訪問權限無關
  1. 使用過程中需要注意的地方:
    1. 通過getFields()和getMethods()方法獲得權限爲public 成員變量和成員方法時,還包括從父類繼承得到的成員變量和成員方法;而通過getDeclaredFields()和getDeclaredMethods()方法只是獲得在本類中定義的所有成員變量和成員方法。
    2. 使用Declared關鍵字的方法,能獲取到本類中的一些私有變量或者方法。
    3. 通過類.class屬性獲取Class對象,會使代碼更安全,程序性能會更好,大部分情況下建議使用第二種方式。
    4. 獲取基本類型的Class對象,可以使用對應的打包類加上 .TYPE ,例如:Integer.TYPE可以獲得int的Class的對象,但要獲得Integer.class的Class對象,則必須使用Integer.class。
    5. 獲取到Class對象後,可以使用上方表格中的方法來操作對象。

2.2 Method類

2.3 Field類

2.4 Constructor類

三. 反射的使用

3.1 示例代碼,融匯方法

3.2

四. 深入反射源碼

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