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()方法。
Class<?> clazz = date.getClass();
如: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、反射需要按名檢索類和方法,有一定的時間開銷。