最近在接觸 Android 的組件化原理的時候,發現本身對 Java 反射的機制理解並非很深,只能利用週末的時間把 Java 反射機制回爐重鑄。java
反射機制是 Java 語言提供的一種基礎功能,賦予了 Java 程序在運行時的自省(introspect,官方用語)的能力。經過 Java 的反射機制,程序員能夠在 Java 程序在運行態的時候操做任意的類或者對象的屬性、方法。利用 Java 的反射機制,能夠作到如下:程序員
對文中的"自省"的理解:"自省"應該僅指程序在運行時對自身信息(元數據)的檢測,而反射機制不單單須要在運行時對程序的自身數據進行檢測,還須要根據檢測到的數據修改程序的狀態或者方法。數組
用於操做反射的相關的 5 個類:ide
Constructor、Field、Method 這三個類都繼承 AccessibleObject,該對象有一個很是重要的方法 AccessibleObject#setAccessible(boolean flag)
,這裏的所謂 accessible 能夠理解成修飾成員的 public、protected、private,這意味着咱們能夠在程序運行時修改類成員的訪問限制。函數
在類 Object 中有 Object#getClass()
、Object#hashCode()
、Object#equals(Object obj)
、Object#clone()
、Object#toString()
、Obect#notify()
、Object#notifyAll()
、Object#wait()
等 public 權限的方法。而 Object#getClass()
方法則是返回程序運行時的 Class 類的對象實例。CLass 類也是一樣繼承 Object 類,擁有相應的方法。Class 類的類表示正在運行的 Java 應用程序中的類和接口。枚舉是一種類,一個註解是一個接口。每一個數組還屬於一個反映爲Class對象的類,該對象由具備相同元素類型和維數的全部數組共享。Java 的基本類型 boolean、byte、char、short、int、long、float、double,和關鍵字 void 也表示爲 Class 對象。組件化
Class 沒有 public 類型的構造器。Java 虛擬機會在加載類時以及經過在類加載器中調用 ClassLoader#defineClass()
方法來自動構造 Class 對象。3d
Modifier 類提供了 static 方法和常量來解碼類和成員訪問修飾符。修飾符集合被表示爲具備不一樣修飾符的不一樣位置的整數。表示的範圍有 abstract、final、interface、native、private、protected、public、strict、synchronized、transient、volatile。code
java.lang.reflect.Constructororm
Constructor 提供了一個類的單個構造函數的信息和訪問。Constructor 容許在將實際參數與 newINstance() 與底層構造函數的形式參數進行匹配時進行擴輾轉換,若是發生縮小轉換,則拋出 IllegalArgumentException
。cdn
方法 | 含義 |
---|---|
Constructor<?>[] getConstructors() | 返回包含一個 Constructor 對象的數組,元素表示爲所指定的類的全部的 public 權限的構造函數 |
getDeclaredConstructors() | 表示返回包含 Constructor 對象的數組,元素表示爲指定類的構造函數,包含非 public 權限的 |
getConstructor(class<?>... parameterTypes) | 返回一個 Constructor 對象,表示指定參數的類的 public 權限的構造函數 |
getDeclaredConstructor(class<?>... parameterTypes) | 表示返回一個表示 Constructor 對象,表示指定參數的構造函數,包含非 public 權限的 |
java.lang.reflect.Field
Field 提供有關類或接口的單個字段的信息和動態訪問。 反射的字段能夠是類(靜態)字段或實例字段。Field 容許在獲取或設置訪問操做期間擴輾轉換,但若是發生縮小轉換,則拋出 IllegalArgumentException
。
方法 | 含義 |
---|---|
getFields() | 返回包含一個數組 Field 對象,表示的類或接口的全部可訪問的公共字段 |
getDeclaredFields() | 返回包含一個數組 Field 對象,表示的類或接口聲明的全部字段 |
getField(String name) | 返回包含一個數組 Field 對象,表示的類或接口指定的可訪問的公共字段 |
getDeclaredField(String name) | 返回包含一個數組 Field 對象,表示的類或接口指定的任意權限的公共字段 |
java.lang.reflect.Method
Method 提供有關類和接口上單一方法的信息和訪問權限。 反映的方法能夠是類方法或實例方法(包括抽象方法)。
方法 | 含義 |
---|---|
getMethods() | 獲取類全部的 public 方法 |
getMethod(String name, class<?>... parameterTypes) | 獲取類特定的 public 方法 |
getDeclaredMethods() | 獲取類全部的方法 |
getDeclaredMethod(String name, class<?>... parameterTypes) | 獲取類特定的方法 |
Java 虛擬機能夠經過稱爲運行時類型信息(RTTI, Run Time Type Information)的技術在運行時檢查任何類,這是經過一種稱爲 Class 對象的特殊對象完成的,該對象包含有關類的信息。
虛擬機爲每一個類管理一個獨一無二的 Class 對象。也就是說,每一個類都有一個 Class 對象實例。在運行程序的時候,JVM 首先須要會去檢測所需加載的類的 Class 是否已經完成加載。若是沒有加載在 JVM 中,那麼 JVM 回去尋找對應類名的 .class 文件,完成對 Class 對象的加載。經過 Class 對象,咱們能夠實例化對應的 Class 類對象,調用其構造器(Constructor)、調用類的成員方法(Method)、訪問或者修改類的成員屬性(Field)。經過 AccessibleObject#setAccessible(boolean flag)
能夠訪問到類的非 public 權限的其餘成員,在上文提到經過 AccessibleObject#setAccessible(boolean flag)
能夠在程序運行時修改類成員的訪問限制。實際上,AccessibleObject#setAccessible(boolean flag)
關閉了權限的訪問檢查,使得經過 Class#invoke()
能夠訪問到任意權限的類成員。
Java 的內部類可分爲普通內部類、靜態內部類。JVM 在編譯含有普通內部類的時候,默認會在構造方法中傳入外部類對象的引用,這也是爲何內部類對象會持有外部類的引用。咱們能夠經過解析 .class 字節碼來驗證這一推論。
有上面的基礎,咱們能夠推斷出,在反射調用普通內部類的成員的時候,咱們須要在普通內部類的構造方法中傳入外部類的對象引用。而靜態內部類因爲不持有外部類的引用,於是不須要在其構造方法中傳入外部類的引用。
舉個例子。有一個類 OutClass
,而後 OutClass
含有一個普通內部類 InnerClass
、以及靜態內部類 StaticClass
。咱們經過反射分別實例化它們的時候,以下圖:
反射調用普通內部類和靜態內部類,只是在實例化的構造器的時候有區別,對於調用內部類的 Field、Method、Constructor,其過程是和調用普通類的過程時同樣的,在這裏就不一一細述了(主要由於我懶)。
Java 的反射機制用起來挺複雜的。但 Java 的反射機制在 Android 組件化中解耦合起到了很大的做用。能夠在程序運行時訪問類的成員屬性或修改屬性、執行方法、以及執行構造方法。而且在 Android 的許多源碼中,有不少的屬性、方法被標記了 @hide,但經過 Java 的反射,仍然能夠訪問這些屬性、方法。