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 {...}
该方法有两个作用:
- 启用 / 禁用访问安全检查开关: 值为 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();</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 < 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>"没有反射, 共消耗 <" + count + "> 毫秒"<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 < 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>"没有访问权限, 共消耗 <" + count + "> 毫秒"<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 < 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>"有访问权限, 共消耗 <" + count + "> 毫秒"<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+ 次 (而且服务器的性能也肯定比我的机器要高), 因此在我们的实际开发中, 其实是不用担心反射机制带来的性能消耗的, 而且禁用访问权限检查, 也会有性能的提升。