一、反射操作泛型(Generic)
Java采用泛型擦除機制來引入泛型。Java中的泛型僅僅是給編譯器Javac使用的,確保數據的安全性和免去強制類型轉換的麻煩。但是編譯一旦完成,所有和泛型有關的類型全部被擦除。
為了通過反射操作這些類型以迎合實際開發的需要,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType幾種類型來代表不能被歸一到Class類中的類型但是又和原始類型齊名的類型。
- ParameterizedType:表示一種參數化的類型,比如Collection< String >
- GenericArrayType:表示一種元素類型是參數化類型或者類型變量的數組類型
- TypeVariable:是各種類型變量的公共父接口
-
WildcardType:代表一種通配符類型表達式,比如?、? extends Number、? super Integer。(wildcard是一個單詞:就是”通配符“)
代碼示例:
package reflection; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; /** * 通過反射獲取泛型信息 * */ public class Demo{ //定義兩個帶泛型的方法 public void test01(Map<String,Person> map,List<Person> list){ System.out.println("Demo.test01()"); } public Map<Integer,Person> test02(){ System.out.println("Demo.test02()"); return null; } public static void main(String[] args) { try { //獲得指定方法參數泛型信息 Method m = Demo.class.getMethod("test01", Map.class,List.class); Type[] t = m.getGenericParameterTypes(); for (Type paramType : t) { System.out.println("#"+paramType); if(paramType instanceof ParameterizedType){ //獲取泛型中的具體信息 Type[] genericTypes = ((ParameterizedType) paramType).getActualTypeArguments(); for (Type genericType : genericTypes) { System.out.println("泛型類型:"+genericType); } } } //獲得指定方法返回值泛型信息 Method m2 = Demo.class.getMethod("test02", null); Type returnType = m2.getGenericReturnType(); if(returnType instanceof ParameterizedType){ Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments(); for (Type genericType : genericTypes) { System.out.println("返回值,泛型類型:"+genericType); } } } catch (Exception e) { e.printStackTrace(); } } }
輸出結果:
#java.util.Map< java.lang.String, reflection.Person >
泛型類型:class java.lang.String
泛型類型:class reflection.Person
#java.util.List< reflection.Person >
泛型類型:class reflection.Person
返回值,泛型類型:class java.lang.Integer
返回值,泛型類型:class reflection.Person
二、反射操作注解(Annotation)
Method/Constructor/Field/Element 都繼承了 AccessibleObject , AccessibleObject 類中有一個 setAccessible 方法:
具體使用可以就看我的之前的文章 注解處理器
好了,介紹了兩個簡單的反射的應用,在順便講一下Java反射機制的性能問題。
三、反射性能測試
Method/Constructor/Field/Element 都繼承了 AccessibleObject , AccessibleObject 類中有一個 setAccessible 方法:
public void setAccessible(booleanflag)throws SecurityException { ... }
該方法有兩個作用:
- 啟用/禁用訪問安全檢查開關:值為true,則指示反射的對象在使用時取消Java語言訪問檢查; 值為false,則指示應該實施Java語言的訪問檢查;
- 可以禁止安全檢查, 提高反射的運行效率.
測試代碼:
package reflection; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class TestReflect { public static void testNoneReflect() { Person user = new Person(); long start = System.currentTimeMillis(); for (long i = 0; i < Integer.MAX_VALUE; ++i) { user.getName(); } long count = System.currentTimeMillis() - start; System.out.println("沒有反射, 共消耗 <" + count + "> 毫秒"); } public static void testNotAccess() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Person user = new Person(); Method method = Class.forName("reflection.Person").getMethod("getName"); long start = System.currentTimeMillis(); for (long i = 0; i < Integer.MAX_VALUE; ++i) { method.invoke(user, null); } long count = System.currentTimeMillis() - start; System.out.println("沒有訪問權限, 共消耗 <" + count + "> 毫秒"); } public static void testUseAccess() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Person user = new Person(); Method method = Class.forName("reflection.Person").getMethod("getName"); method.setAccessible(true); long start = System.currentTimeMillis(); for (long i = 0; i < Integer.MAX_VALUE; ++i) { method.invoke(user, null); } long count = System.currentTimeMillis() - start; System.out.println("有訪問權限, 共消耗 <" + count + "> 毫秒"); } public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { testNoneReflect(); testNotAccess(); testUseAccess(); } }
輸出結果:
沒有反射, 共消耗 <912> 毫秒
沒有訪問權限, 共消耗 <4366> 毫秒 有訪問權限, 共消耗 <2843> 毫秒
可以看到使用反射會比直接調用慢2000 毫秒 ,但是前提是該方法會執行20E+次(而且服務器的性能也肯定比我的機器要高),因此在我們的實際開發中,其實是不用擔心反射機制帶來的性能消耗的,而且禁用訪問權限檢查,也會有性能的提升。