深刻理解Java反射機制

前言

最近在接觸 Android 的組件化原理的時候,發現本身對 Java 反射的機制理解並非很深,只能利用週末的時間把 Java 反射機制回爐重鑄。java

什麼是Java反射

反射機制是 Java 語言提供的一種基礎功能,賦予了 Java 程序在運行時的自省(introspect,官方用語)的能力。經過 Java 的反射機制,程序員能夠在 Java 程序在運行態的時候操做任意的類或者對象的屬性、方法。利用 Java 的反射機制,能夠作到如下:程序員

  • 在程序的運行態能夠獲取對象所屬的類;
  • 在程序的運行態能夠構造類的對象實例;
  • 在程序的運行時能夠獲取,或者修改類的成員屬性;
  • 在程序的運行態能夠調用某個類,或者對象的方法;
  • 在程序的運行態能夠獲取類的其餘信息,好比描述修飾符、父類信息等;

對文中的"自省"的理解:"自省"應該僅指程序在運行時對自身信息(元數據)的檢測,而反射機制不單單須要在運行時對程序的自身數據進行檢測,還須要根據檢測到的數據修改程序的狀態或者方法。數組

用於操做反射的相關的 5 個類:ide

  • java.lang.Class:表明類;
  • java.lang.reflect.Constructor:表明類的構造方法;
  • java.lang.reflect.Field:表明類的屬性;
  • java.lang.reflect.Method:表明類的方法;
  • java.lang.reflect.Modifier:表明類、方法、屬性的修飾符;

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

Constructor

java.lang.reflect.Constructororm

Constructor 提供了一個類的單個構造函數的信息和訪問。Constructor 容許在將實際參數與 newINstance() 與底層構造函數的形式參數進行匹配時進行擴輾轉換,若是發生縮小轉換,則拋出 IllegalArgumentExceptioncdn

方法 含義
Constructor<?>[] getConstructors() 返回包含一個 Constructor 對象的數組,元素表示爲所指定的類的全部的 public 權限的構造函數
getDeclaredConstructors() 表示返回包含 Constructor 對象的數組,元素表示爲指定類的構造函數,包含非 public 權限的
getConstructor(class<?>... parameterTypes) 返回一個 Constructor 對象,表示指定參數的類的 public 權限的構造函數
getDeclaredConstructor(class<?>... parameterTypes) 表示返回一個表示 Constructor 對象,表示指定參數的構造函數,包含非 public 權限的

Field

java.lang.reflect.Field

Field 提供有關類或接口的單個字段的信息和動態訪問。 反射的字段能夠是類(靜態)字段或實例字段。Field 容許在獲取或設置訪問操做期間擴輾轉換,但若是發生縮小轉換,則拋出 IllegalArgumentException

方法 含義
getFields() 返回包含一個數組 Field 對象,表示的類或接口的全部可訪問的公共字段
getDeclaredFields() 返回包含一個數組 Field 對象,表示的類或接口聲明的全部字段
getField(String name) 返回包含一個數組 Field 對象,表示的類或接口指定的可訪問的公共字段
getDeclaredField(String name) 返回包含一個數組 Field 對象,表示的類或接口指定的任意權限的公共字段

Method

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反射實踐

Java反射調用內部類

Java 的內部類可分爲普通內部類、靜態內部類。JVM 在編譯含有普通內部類的時候,默認會在構造方法中傳入外部類對象的引用,這也是爲何內部類對象會持有外部類的引用。咱們能夠經過解析 .class 字節碼來驗證這一推論。

有上面的基礎,咱們能夠推斷出,在反射調用普通內部類的成員的時候,咱們須要在普通內部類的構造方法中傳入外部類的對象引用。而靜態內部類因爲不持有外部類的引用,於是不須要在其構造方法中傳入外部類的引用。

舉個例子。有一個類 OutClass,而後 OutClass 含有一個普通內部類 InnerClass、以及靜態內部類 StaticClass。咱們經過反射分別實例化它們的時候,以下圖:

反射調用普通內部類和靜態內部類,只是在實例化的構造器的時候有區別,對於調用內部類的 Field、Method、Constructor,其過程是和調用普通類的過程時同樣的,在這裏就不一一細述了(主要由於我懶)。

小結

Java 的反射機制用起來挺複雜的。但 Java 的反射機制在 Android 組件化中解耦合起到了很大的做用。能夠在程序運行時訪問類的成員屬性或修改屬性、執行方法、以及執行構造方法。而且在 Android 的許多源碼中,有不少的屬性、方法被標記了 @hide,但經過 Java 的反射,仍然能夠訪問這些屬性、方法。

相關文章
相關標籤/搜索