Java进阶之reflection(反射机制)——通过反射操作泛型,注解

时间:2022-10-26 21:18:11

上篇文章我介绍了Java反射的基本知识,如果没看过的同学可以去看我的上一篇文章 反射概念与基础 ,今天这篇文章主要介绍一下反射地具体应用实例,分别是通过Java反射操作泛型,和反射操作注解(不了解”注解”的同学可以看我的另一篇文章java注解)。

一、反射操作泛型(Generic)


  Java采用泛型擦除机制来引入泛型。Java中的泛型仅仅是给编译器Javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是编译一旦完成,所有和泛型有关的类型全部被擦除。
  为了通过反射操作这些类型以迎合实际开发的需要,Java新增了ParameterizedType,GenericArrayType,TypeVariableWildcardType几种类型来代表不能被归一到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)


具体使用可以就看我的之前的文章 注解处理器

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