天天看點

Java反射機制詳解動态語言Class 反射機制建立對象通路成員變量使用反射擷取泛型資訊反射性能測試

動态語言,是指程式在運作時可以改變其結構:新的函數可以被引進,已有的函數可以被删除等在結構上的變化。比如衆所周知的ECMAScript(JavaScript)便是一個動态語言。除此之外如Ruby、Python等也都屬于動态語言,而C、C++等語言則不屬于動态語言。(引自: 百度百科)

指的是可以于運作時加載,探知和使用編譯期間完全未知的類.

程式在運作狀态中, 可以動态加載一個隻有名稱的類, 對于任意一個已經加載的類,都能夠知道這個類的所有屬性和方法; 對于任意一個對象,都能調用他的任意一個方法和屬性;

加載完類之後, 在堆記憶體中會産生一個Class

類型的對象(一個類隻有一個Class對象), 這個對象包含了完整的類的結構資訊,而且這個Class對象就像一面鏡子,透過這個鏡子看到類的結構,是以被稱之為:反射。

Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions(次元). The primitive Java types (boolean, byte, char, short, int, long, float, anddouble), and the keyword void are also represented as Class objects.

每個類被加載進入記憶體之後,系統就會為該類生成一個對應的java.lang.Class

對象,通過該Class

對象就可以通路到JVM中的這個類.Class對象的擷取

對象的getClass()方法;

類的.class(最安全/性能最好)屬性;

運用Class.forName(String className)動态加載類,className需要是類的全限定名(最常用).

從Class中擷取資訊

Class類提供了大量的執行個體方法來擷取該Class對象所對應的詳細資訊,Class類大緻包含如下方法,其中每個方法都包含多個重載版本,是以我們隻是做簡單的介紹,詳細請參考JDK文檔

擷取類内資訊

Java反射機制詳解動态語言Class 反射機制建立對象通路成員變量使用反射擷取泛型資訊反射性能測試

一些判斷類本身資訊的方法

Java反射機制詳解動态語言Class 反射機制建立對象通路成員變量使用反射擷取泛型資訊反射性能測試

使用反射生成并操作對象:

Method Constructor Field這些類都實作了java.lang.reflect.Member接口,程式可以通過Method對象來執行相應的方法,通過Constructor對象來調用對應的構造器建立執行個體,通過Filed對象直接通路和修改對象的成員變量值.

通過反射來生成對象的方式有兩種:

使用Class對象的newInstance()方法來建立該Class對象對應類的執行個體(這種方式要求該Class對象的對應類有預設構造器).

先使用Class對象擷取指定的Constructor對象, 再調用Constructor對象的newInstance()方法來建立該Class對象對應類的執行個體(通過這種方式可以選擇指定的構造器來建立執行個體).

通過第一種方式來建立對象比較常見, 像Spring這種架構都需要根據配置檔案(如applicationContext.xml)資訊來建立Java對象,從配置檔案中讀取的隻是某個類的全限定名字元串,程式需要根據該字元串來建立對應的執行個體,就必須使用預設的構造器來反射對象.下面我們就模拟Spring實作一個簡單的對象池, 該對象池會根據檔案讀取key-value對, 然後建立這些對象, 并放入Map中.

配置檔案

ObjectPool

Client

User

Bean

注意: 需要在pom.xml中添加如下依賴:

調用方法

當擷取到某個類對應的Class對象之後, 就可以通過該Class對象getMethod來擷取一個Method數組或Method對象.每個Method對象對應一個方法,在獲得Method對象之後,就可以通過調用invoke方法來調用該Method對象對應的方法.

下面我們對上面的對象池加強:可以看到Client擷取到的對象的成員變量全都是預設值,既然我們已經使用了JSON這麼優秀的工具,我們又學習了動态調用對象的方法,那麼我們就通過配置檔案來給對象設定值(在對象建立時), 新的配置檔案形式如下:

其中fields代表該Bean所包含的屬性, name為屬性名稱, value為屬性值(屬性類型為JSON支援的類型), ref代表引用一個對象(也就是屬性類型為Object,但是一定要引用一個已經存在了的對象)

ComplexBean

Spring架構就是通過這種方式将成員變量值以及依賴對象等都放在配置檔案中進行管理的,進而實作了較好地解耦(不過Spring是通過XML作為配置檔案).

通過Class對象的的getField()方法可以擷取該類所包含的全部或指定的成員變量Field,Filed提供了如下兩組方法來讀取和設定成員變量值.

getXxx(Object obj): 擷取obj對象的該成員變量的值, 此處的Xxx對應8中基本類型,如果該成員變量的類型是引用類型, 則取消get後面的Xxx;

setXxx(Object obj, Xxx val): 将obj對象的該成員變量值設定成val值.此處的Xxx對應8種基本類型, 如果該成員類型是引用類型, 則取消set後面的Xxx;

注: getDeclaredXxx方法可以擷取所有的成員變量,無論private/public;

為了通過反射操作泛型以迎合實際開發的需要, Java新增了java.lang.reflect.ParameterizedType java.lang.reflect.GenericArrayTypejava.lang.reflect.TypeVariable java.lang.reflect.WildcardType幾種類型來代表不能歸一到Class類型但是又和原始類型同樣重要的類型.

Java反射機制詳解動态語言Class 反射機制建立對象通路成員變量使用反射擷取泛型資訊反射性能測試

其中, 我們可以使用ParameterizedType來擷取泛型資訊.

Method/Constructor/Field/Element都繼承了AccessibleObject,AccessibleObject類中有一個setAccessible方法:

該方法有兩個作用:

啟用/禁用通路安全檢查開關:值為true,則訓示反射的對象在使用時取消Java語言通路檢查;值為false,則訓示應該實施Java語言的通路檢查;

可以禁止安全檢查, 提高反射的運作效率.

執行上面程式,在我的機器上會有如下結果:

Java反射機制詳解動态語言Class 反射機制建立對象通路成員變量使用反射擷取泛型資訊反射性能測試

機器配置資訊如下:

Java反射機制詳解動态語言Class 反射機制建立對象通路成員變量使用反射擷取泛型資訊反射性能測試

可以看到使用反射會比直接調用慢3000毫秒,但是前提是該方法會執行20E+次(而且伺服器的性能也肯定比我的機器要高),是以在我們的實際開發中,其實是不用擔心反射機制帶來的性能消耗的,而且禁用通路權限檢查,也會有性能的提升。

我有一個微信公衆号,經常會分享一些Java技術相關的幹貨。如果你喜歡我的分享,可以用微信搜尋“Java團長”或者“javatuanzhang”關注。