JVM的反射實現


java的反射機制

java的反射機制是在運行狀態中,對於任意一個類(Class)都能知道他的屬性(Field)和方法(Method),對於任意一個對象都能夠調用它的方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。它允許正在運行的java程序觀測甚至是修改程序的動態行為。

在java中實現反射最重要的一步也就是第一步是獲得Class對象,得到該對象后可通過該對象調用相應的方法獲取該類中的屬性方法,以及調用該類中的方法。

Java中反射有如下幾種實現方式:

1、通過Class.forName()方法加載字符串,就可以得到該字符串做代表的Class對象。

如:Class<?> clazz = Class.forName("java.lang.String")就可以得到String類的Class對象。值得注意的是,字符串必須是類的全名,即包名+類名。

下邊的代碼是Struts配置文件struts.xml中的一個action的配置。

<action name="registe" class="cn.com.test.struts2.RegisteAction">
   <result>/registeResult.jsp</result>
   <result name="input">/registe2.jsp</result>
</action>

服務器就是通過class屬性給出的類的全命名class="cn.com.test.struts2.RegisteAction"的字符串來得到對象的,就是反射機制RegisteAction的。然后再去調用這個類中execute()方法。

2、通過類名調用class屬性得到該類的Class對象。

Class<?> clazz = String.class也可以得到String類的Class對象。

3、調用實例的getClass()方法。

如:Date date = new Date();
       Class<?> clazz = date.getClass();
4、如果是基本類型的包裝類,則可以通過調用包裝類的Type屬性來獲得該包裝類的Class對象。
如:Class<?> clazz = Integer.TYPE;

我們可以通過Class對象枚舉該類中的所有方法,還可以通過Method.setAccessible(位於java.lang.reflect包,該方法繼承自AccessibleObject)繞過java語言的訪問權限,在私有方法所在類之外的地方調用該方法。

反射在java中的應用

1、java集成開發環境,每當我們敲入點號時,IDE便會根據點號前的內容,動態展示可以訪問的字段和方法。

2、java調試器,它能夠在調試過程中枚舉某一對象所有字段的值。

3、web開發中,我們經常接觸到各種配置的通用框架。為保證框架的可擴展性,他往往借助java的反射機制。例如Spring框架的依賴反轉(IOC)便是依賴於反射機制。

反射調用的實現Mehod.invoke

 1 public Object invoke(Object obj, Object... args)
 2         throws IllegalAccessException, IllegalArgumentException,
 3            InvocationTargetException
 4     {
 5         if (!override) {
 6             if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
 7                 Class<?> caller = Reflection.getCallerClass();
 8                 checkAccess(caller, clazz, obj, modifiers);
 9             }
10         }
11         MethodAccessor ma = methodAccessor;             // read volatile
12         if (ma == null) {
13             ma = acquireMethodAccessor();
14         }
15         return ma.invoke(obj, args);
16     }

你會發現,它實際上是委派給 MethodAccessor 來處理。MethodAccessor  是一個接口,它有兩個實現:一個通過本地方法來實現反射調用,另一個使用了委派模式(這里我們稱委派實現)。

每個 Method 實例的第一次反射調用都會生成一個委派實現,他所委派的具體實現便是一個本地實現。當進入JVM內部后,我們便擁有了Mehod 實例所指向方法的具體地址。這時反射調用無非就是將傳入的參數准備好,然后調用進入目標方法。

反射調用先是調用了Method.invoke, 然后進入委派實現(DelegatingMethodAccessorImpl),再進入本地實現(NativeMethodAccessorImpl),最后到達目標方法。

調用了Method.invoke之后,先進行訪問權限檢查,再獲取MethodAccessor對象,並調用MethodAccessor.invoke方法。MethodAccessor被同名Method所共享,由ReflectionFactory創建。創建機制采用一種名為inflation的方式:如果該方法的調用<=15,會創建本地實現,他的實現就是直接調用native方法實現反射,如果該方法的累計調用次數>15,會創建由字節碼組裝而成的MethodAccessorImpl。(是否采用inflation機制和15這個數字可以在jvm參數中調整)。

性能

通過JNI(java本地接口)調用native方法初始化更快,但是對優化有阻礙作用。隨着調用次數的增多,使用動態實現可以直接以java調用的方式來實現反射,發揮了java即時編譯的優化作用。

java反射調用效率慢的原因:

1、接口的通用性,java的invoke方法是傳object和object[]數組的。基本參數類型需要裝箱和拆箱,產生大量額外的對象和內存開銷,頻繁促發GC。

2、編譯器難以對動態調用的代碼提前做優化比如方法內聯。

3、反射需要按名檢索類和方法,有一定的時間開銷。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM