反射之深刻理解Constructor原理

知其然,知其因此然html

0. 前言

在上一篇《反射從入門到精通之深刻了解Class類》,咱們深刻分析了一下 Class 類的原理。在本篇文章,咱們分析一下 Constructor 使用方法的原理。c++

1. Constructor

經過反射調用構造函數有兩種方法:web

  • 調用無參構造函數:Class.newInstance()
  • 調用帶參數的構造函數:
  • 經過 Class 類獲取 Constructor
  • 調用 Constructor 中的 newInstance(Object … initarges) 方法

具體能夠詳見《反射從0到入門》,知道了這些咱們深刻了解下 Constructor 中的 newInstance(Object … initarges) 方法。app

1.1 newInstance

想要了解原理,第一步就是要看懂 jdk 的註釋,newInstance 的註釋以下:ide

(打擾了,看不懂這個,全劇終。。。)svg

別走,我來給大家翻譯(Google 翻譯真香)函數

使用 Constructor 表明構造函數,根據參數建立而且初始化一個實例。各個參數將自動拆箱以匹配原始形式參數,而且原始參數和引用參數必須根據須要進行方法調用轉換。ui

要獲取無參構造函數,參數長度能夠爲 0 或者是 nullurl

調用非靜態內部類,參數該。。。(此處不翻譯)spa

訪問經過而且參數檢查成功,將繼續進行實例化。若是構造函數的聲明類沒有初始化,須要初始化

構造函數完成,返回新建立而且初始化好的實例

根據小李這段粗糙的翻譯中,能夠獲得下面幾個關鍵的內容:

  • 根據參數建立初始化實例,參數有匹配的規則;
  • 獲取無參構造函數,參數長度能夠爲 0 或者 null;
  • 有訪問權限而且對參數進行檢查,須要獲取到構造函數的聲明類;

知道了這些咱們來解讀一下 newInstance() 的源碼,看下圖:

源碼能夠拆分爲三塊:

  1. 校驗權限:校驗權限就不在此分析了,你們能夠自行查看源碼
  2. 獲取構造函數的聲明類
  3. 建立實體

獲取構造函數的聲明類

構造函數聲明類 ConstructorAccessor 是一個接口,以下圖所示:

查看下接口的實現類以下結構(虛線表明實現接口,藍色線表明繼承,那白線是什麼鬼?)

從圖中可知實現類都是繼承了 ConstructorAccessorImpl 抽象類,而且實現了 newInstance() 方法。

那到底使用哪一個實現類那呢?我們繼續往下看

若是 ConstructorAccessor 已經被建立了,獲取並賦值。若是沒有則經過 newConstructorAccessor 方法建立 ConstructorAccessornewConstructorAccessor 方法以下:

newConstructorAccessor 分爲三部分:

  1. 檢查是否初始化

這是反射工廠(ReflectionFactory)檢查初始化狀態,若是沒有初始化會進行下面用紅線圈上的操做。

那大概猜一下這塊是作什麼呢?

首先,inflation 字面理解是通脹或者膨脹,那 noInflation 按字面理解也是不膨脹。

Threshold 字面理解是閾值,inflationThreshold 按字面理解是通脹閾值,就是一個通脹的界限值。

按照字面理解可知 noInflation 來判斷是否通脹,inflationThreshold 是一個通脹的界限值。

問問度娘,驗證下我們的結果:

JNI(Java Native Interface),經過使用 Java 本地接口書寫程序,能夠確保代碼在不一樣的平臺上方便移植。

猜的差很少,JVM 有兩種方法來訪問有關反射的類的信息,可使用 JNI 讀取器或者 Java 字節碼存取器。inflationThreshold 是使用 JNI 存取器的次數,值爲 0 表示永不從 JNI 存取器讀取。若是想強制使用 Java 字節碼存取器,能夠設置 noInflation 爲 true。

inflationThreshold 默認值是 15,若是不對 inflationThreshold 進行修改,JVM 訪問反射的類的信息會先從 JNI 存取器讀取 15次以後纔會使用 Java 字節碼存取器

這就能夠解釋通爲何要有一個初始化檢測的操做了。

從這部分能夠學到一些小知識:

咱們可使用 -D= 來設置系統屬性,經過 System.getProperty("屬性名稱") 來獲取屬性值。

  1. 獲取當前類的 Class 實例
  1. 根據條件獲取 ConstructorAccessor

    這麼多 if 條件判斷,不要慌,我來幫你分析一波:

  • 第 1 步,校驗在第二步獲取的 Class 實例是否是抽象類,若是是抽象類就拋出異常。

  • 第 2 步,判斷是不是 Class 實例,由於 Class 實例的構造函數是 private,因此這塊也須要拋出異常。

  • 第 3 步,判斷這個 Class 實例是否繼承 ConstructorAccessorImpl,若是是父子關係,就調用 BootstrapConstructorAccessorImpl 建立 ConstructorAccessor,這個方法是調用 native 方法,底層用 c++ 實現的本地接口。

  • 第 4 步,若是 noInflation 爲 true 而且 Class 實例不是匿名的,須要調用 MethodAccessorGenerator.generateConstructor() 建立 ConstructorAccessor,具體的細節就不分析了,原理仍是經過讀取二進制文件,將 Class 實例加載進來,而後根據一些條件獲取到想要的 Constructor

  • 第 5 步,上面的條件都不知足,就調用 NativeConstructorAccessorImpl,看下這個方法的源碼:

    ​ 調用次數和 inflationThreshold 比較,若是大於inflationThreshold(默認是 15 次),調用的方法是否是和第四步是相同的。

    ​ 若是小於等於 inflationThreshold ,就要調用 newInstance0 方法,newInstance0 是 native 方法,調用的就是本地接口。

    ​ 其實第一步初始化的時候就是爲了在這裏作鋪墊呢。

    ​ 到這裏尚未完事,還有一個 DelegatingConstructorAccessorImpl 方法。

    ​ 那這一塊使用了一手代理模式,把 NativeConstructorAccessorImpl 放入到 DelegatingConstructorAccessorImpl 的 delegate 中。newInstance 調用的是 delegate 的 newInstance 方法。

    ​ 還記得我最開始問白線是作什麼的,這回解惑了吧。

內容有點多,我畫個圖帶大家梳理一下:

圖片連接

建立實例

上一步獲取到了 ConstructorAccessor 的實現類,直接調用 newInstance 方法去建立實例。

2. 總結

咱們回顧下前面的內容:

首先咱們根據 jdk 提供的註釋知道 newInstance 能夠根據參數進行初始化並返回實例,想要獲取實例必需要獲取到構造函數的聲明類 ConstructorAccessor ,隨後咱們就深刻分析了 ConstructorAccessor

ConstructorAccessor 有一個抽象類 ConstructorAccessorImpl,其它的實現類須要繼承 ConstructorAccessorImpl,分別是下面幾個實現類:

  • InstantiationExceptionConstructorAccessorImpl:將異常信息存起來,調用 newInstance 會拋出 InstantiationException 異常。

  • BootstrapConstructorAccessorImpl:當須要建立的 Class 實例和 ConstructorAccessorImpl 是父子關係,就要返回 BootstrapConstructorAccessorImpl,調用的是底層的方法,經過 C++ 編寫。

  • SerializationConstructorAccessorImpl:也是一個抽象類,當 JVM 從 Java字節碼進行讀取,會返回這個實現類。

  • NativeConstructorAccessorImpl:調用本地接口方法建立 ConstructorAccessor ,須要根據調用次數和inflationThreshold 作比較,inflationThreshold 的默認值是 15,能夠經過-Dsun.reflect.inflationThreshold=來修改默認值。

    當調用次數大於 15次的時候,JVM 從 Java字節碼進行獲取。反之,從本地接口進行獲取。

  • DelegatingConstructorAccessorImpl:代理類,是 NativeConstructorAccessorImpl 的父類。

獲取到了 ConstructorAccessor,經過調用 newInstance() 方法來建立實例。

3. 彩蛋

反射相關文章

《反射從0到入門》

《反射從入門到精通之深刻了解Class類》

公衆號Java知識學堂,裏面有我最近整理的反射相關內容,但願能對你們有所幫助。

相關文章
相關標籤/搜索