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;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main(String[] args) {
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {           
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获得指定方法参数泛型信息</span>
        Method m = Demo.<span style="color: rgba(0, 0, 255, 1)">class</span>.getMethod("test01", Map.<span style="color: rgba(0, 0, 255, 1)">class</span>,List.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
        Type[] t </span>=<span style="color: rgba(0, 0, 0, 1)"> m.getGenericParameterTypes();

        </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (Type paramType : t) {
            System.out.println(</span>"#"+<span style="color: rgba(0, 0, 0, 1)">paramType);
            </span><span style="color: rgba(0, 0, 255, 1)">if</span>(paramType <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> ParameterizedType){
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取泛型中的具体信息</span>
                Type[] genericTypes =<span style="color: rgba(0, 0, 0, 1)"> ((ParameterizedType) paramType).getActualTypeArguments();
                </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (Type genericType : genericTypes) {
                    System.out.println(</span>"泛型类型:"+<span style="color: rgba(0, 0, 0, 1)">genericType);
                }
            }
        }   

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获得指定方法返回值泛型信息</span>
        Method m2 = Demo.<span style="color: rgba(0, 0, 255, 1)">class</span>.getMethod("test02", <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
        Type returnType </span>=<span style="color: rgba(0, 0, 0, 1)"> m2.getGenericReturnType();
        </span><span style="color: rgba(0, 0, 255, 1)">if</span>(returnType <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> ParameterizedType){
                Type[] genericTypes </span>=<span style="color: rgba(0, 0, 0, 1)"> ((ParameterizedType) returnType).getActualTypeArguments();

                </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (Type genericType : genericTypes) {
                    System.out.println(</span>"返回值,泛型类型:"+<span style="color: rgba(0, 0, 0, 1)">genericType);
                }                   
        }       

    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (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();

    </span><span style="color: rgba(0, 0, 255, 1)">long</span> start =<span style="color: rgba(0, 0, 0, 1)"> System.currentTimeMillis();
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">long</span> i = 0; i &lt; Integer.MAX_VALUE; ++<span style="color: rgba(0, 0, 0, 1)">i) {
        user.getName();
    }
    </span><span style="color: rgba(0, 0, 255, 1)">long</span> count = System.currentTimeMillis() -<span style="color: rgba(0, 0, 0, 1)"> start;
    System.out.println(</span>"没有反射, 共消耗 &lt;" + count + "&gt; 毫秒"<span style="color: rgba(0, 0, 0, 1)">);
}


</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> testNotAccess() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    Person user </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Person();
    Method method </span>= Class.forName("reflection.Person").getMethod("getName"<span style="color: rgba(0, 0, 0, 1)">);

    </span><span style="color: rgba(0, 0, 255, 1)">long</span> start =<span style="color: rgba(0, 0, 0, 1)"> System.currentTimeMillis();
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">long</span> i = 0; i &lt; Integer.MAX_VALUE; ++<span style="color: rgba(0, 0, 0, 1)">i) {
        method.invoke(user, </span><span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
    }
    </span><span style="color: rgba(0, 0, 255, 1)">long</span> count = System.currentTimeMillis() -<span style="color: rgba(0, 0, 0, 1)"> start;
    System.out.println(</span>"没有访问权限, 共消耗 &lt;" + count + "&gt; 毫秒"<span style="color: rgba(0, 0, 0, 1)">);
}


</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> testUseAccess() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    Person user </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Person();
    Method method </span>= Class.forName("reflection.Person").getMethod("getName"<span style="color: rgba(0, 0, 0, 1)">);
    method.setAccessible(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);

    </span><span style="color: rgba(0, 0, 255, 1)">long</span> start =<span style="color: rgba(0, 0, 0, 1)"> System.currentTimeMillis();
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">long</span> i = 0; i &lt; Integer.MAX_VALUE; ++<span style="color: rgba(0, 0, 0, 1)">i) {
        method.invoke(user, </span><span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
    }
    </span><span style="color: rgba(0, 0, 255, 1)">long</span> count = System.currentTimeMillis() -<span style="color: rgba(0, 0, 0, 1)"> start;
    System.out.println(</span>"有访问权限, 共消耗 &lt;" + count + "&gt; 毫秒"<span style="color: rgba(0, 0, 0, 1)">);
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> main(String[] args) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    testNoneReflect();
    testNotAccess();
    testUseAccess();
}

}

输出结果: 
没有反射, 共消耗 <912> 毫秒 
没有访问权限, 共消耗 <4366> 毫秒 有访问权限, 共消耗 <2843> 毫秒

可以看到使用反射会比直接调用慢 2000 毫秒 , 但是前提是该方法会执行 20E+ 次 (而且服务器的性能也肯定比我的机器要高), 因此在我们的实际开发中, 其实是不用担心反射机制带来的性能消耗的, 而且禁用访问权限检查, 也会有性能的提升。