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+次(而且服务器的性能也肯定比我的机器要高),因此在我们的实际开发中,其实是不用担心反射机制带来的性能消耗的,而且禁用访问权限检查,也会有性能的提升。

posted @ 2018-08-14 10:27  会偷袭的猫  阅读(2944)  评论(0编辑  收藏  举报