Java之reflection(反射機制)——通過反射操作泛型,注解


一、反射操作泛型(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 
{
    ...
}

該方法有兩個作用:

  1. 啟用/禁用訪問安全檢查開關:值為true,則指示反射的對象在使用時取消Java語言訪問檢查; 值為false,則指示應該實施Java語言的訪問檢查;
  2. 可以禁止安全檢查, 提高反射的運行效率.

測試代碼

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+次(而且服務器的性能也肯定比我的機器要高),因此在我們的實際開發中,其實是不用擔心反射機制帶來的性能消耗的,而且禁用訪問權限檢查,也會有性能的提升。


免責聲明!

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



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