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