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

  • 相关阅读:
    Python之os模块
    Python之加密模块
    Python之random模块
    Python之操作MySQL数据库
    Python之操作Excel
    Jmeter之发送请求入参必须使用编码格式、Jmeter之发送Delete请求可能入参需要使用编码格式
    PAT B1008 数组元素循环右移问题 (20 分)
    PAT B1007 素数对猜想 (20 分)
    PAT B1006 换个格式输出整数 (15 分)
    PAT B1005 继续(3n+1)猜想 (25 分)
  • 原文地址:https://www.cnblogs.com/cat520/p/9473046.html
Copyright © 2011-2022 走看看