JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Class类和java.lang.reflect类库一起对反射机制进行了支持,该类库包含了Field、Method以及Constructor类。下面就从这两大方面对反射进行介绍:
1. Class类
类作为程序的一部分,每个类都拥有一个Class对象。每当编写并编译一个新类,就会产生一个Class对象(保存在一个同名的.class文件中)。为了生成这个类的对象,jvm使用了类加载器系统。
所有的类都是在第一次使用时,动态加载到jvm中的。类加载器首先检查这个类的Class对象是否已经加载,未加载就会查找.class文件(本地或网络获取)。
1.1 Class类获取
以下三种方式均可以获取Class类:
return reportInfo;
Integer num = new Integer(123);
Class c1 = num.getClass();
Class c2 = Integer.class;
Class c3 = Class.forName("java.lang.Integer");
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class cl4 = loader.loadClass("java.lang.Integer");
注意点
- .class方式不会引起类的初始化,而Class.forName会引起对应类进行初始化。
- 基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
- 每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
1.2 Class提供常用方法
- getClassLoader() :返回该类的类加载器
- isArray() :判断是否是数组
- getName():以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称
- newInstance():可以创建该类的示例
- 同时提供了获取Constructor、Method和Field的方法,后续会详细说明
1.3 Class使用技巧
- forName和newInstance结合起来使用,可以根据存储在字符串中的类名创建对象,例如:Object obj = Class.forName("xxxx").newInstance();
- 虚拟机为每种类型管理一个独一无二的Class对象。因此可以使用==操作符来比较类对象,例如:if(e.getClass() == Employee.class)
2. reflect包
reflect包中有三个类,Field,Method,Constructor,分别去描述类的域,方法,构造器。
通过Class类可以分别获取上述三个类的具体实例,下面进行分别讲述:
2.1 Field类
表示类的成员变量,其中一个成员变量对应一个Field对象。Class对象获取Field方法如下:
- getFields():获得类的public类型的属性
- getDeclaredFields():获得类的所有属性
- getField(String name):获取指定名称public类型属性
- getDeclaredFields(String name):获取指定名称属性
通过上述4种方法,我们可以获取指定的Field对象。通过Field对象我们可以实现以下常见功能:
- String getName():获取字段名
- Class<?> getType() 和 Type getGenericType() :获取类型和泛型
- int getModifiers():获取修饰
- Object get(Object obj):获取指定对象该字段对应值
- void set(Object obj, Object value):给指定对象的该段赋值
这里演示一下如何修改private的属性值:
这里需要注意的是在赋值给private属性之前需要调用field.setAccessible(true)方法关闭对private属性访问检查。
2.2 Method类
表示类的成员方法,其中一个成员方法对应一个Method对象。Class对象获取Method方法与Filed类似如下:
- getMethods():获得类的public类型的方法
- getDeclaredMethods():获得类的所有方法
- getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型,同时该方法为public的
- getDeclaredMethod(String name, Class<?>... parameterTypes):同上只是范围扩大到所有方法
同样获取Method对象以后,我们可以实现以下常见功能:
- Class<?>[] getParameterTypes():获取该方法的所有参数类型
- Class<?> getReturnType():获取该方法返回值
- <T extends Annotation> T getAnnotation(Class<T> annotationClass):获取方法注解
- Object invoke(Object obj, Object... args)执行该方法
同样这里演示如何执行一个private方法:
同样在执行private方法之前需要执行method.setAccessible(true),关闭对private方法访问检查。
2.3 Constructor类
表示类的构造方法,其中一个构造方法对应一个Constructor对象。Class获取Constructor的方法如下:
- getConstructors():获得类的public类型的构造方法
- getDeclaredConstructors():获取所有构造方法
- getConstructor(Class<?>... parameterTypes):获得类的特定public构造方法,parameterTypes 参数指定构造方法的参数类型。
- getDeclaredConstructor(Class<?>... parameterTypes):同上范围扩大到所有构造方法
同样获取Constructor对象以后,我们可以实现以下常见功能:
- Class<?>[] getParameterTypes():构造方法参数列表
- T newInstance(Object ... initargs):实例化对应类
接下来示例展示如何利用Constructor对象实例化对应类
2.4 反射与泛型
常见两种泛型使用场景:
- 声明一个需要被参数化(parameterizable)的类/接口,例如:class MyClass<T>
- 使用一个参数化类,例如:List<String> arrays;
2.4.1Type接口
在此之前我们需要介绍一下Type接口,Java编程语言中所有类型的公共父接口,这里所说的所有类型包括:
原始类型 (raw types)对应Class,参数化类型 (parameterizedtypes)对应ParameterizedType, 数组类型 (array types)对应GenericArrayType,类型变量 (type variables)对应TypeVariable,基本数据类型(primitivetypes)仍然对应Class。
Class类实现了该接口,同时该接口的直接子接口包括以下4个:
- ParameterizedType: 表示一种参数化的类型,比如Collection<String>
- GenericArrayType: 表示一种元素类型是参数化类型或者类型变量的数组类型
- TypeVariable: 是各种类型变量的公共父接口
- WildcardType: 代表一种通配符类型表达式
配合后面的反射内容重点介绍一下java.lang.reflect.ParameterizedType接口
- 含义:表示参数化类型比如:Map<String, Date>这种参数化类型
- Type[] getActualTypeArguments():获取参数化类型<>中的实际类型(注意对于多次嵌套,该方法只返回脱去最外层的<>以后剩下内容返回)
- 关于返回值是数组因为存在Map<String, Date>返回不止一种类型
- 为什么返回父接口,因为返回值多态性,对于ArrayList<ArrayList<Integer>>返回的是ArrayList<Integer>是ParameterizedType类型的,而对于ArrayList<E>返回的是E是TypeVariable类型,而对于 对于ArrayList<E[]>返回的是E[]则对应的是GenericArrayType类型。
- Type getRawType():获取承载该泛型信息的对象,该类型表示<>前面的类型比如Map<String,String>,则返回Map
2.4.2参数化类型反射获取
讲述完Type相关内容以后,我们来看下Field、Method和Constructor相关类如何获取泛型参数或泛型返回值。这里我们以Method为例说明:
在这个示例中我们重点关注一下method.getGenericReturnType()方法,返回返回值Type,查看jdk源码:
观察代码可以发现在获取泛型返回是首先判断getGenericSignature()是否为空该函数是native的没找到相关资料,从名称推断是标识是否是泛型,在Field相关源码中也有出现。如果返回不问空则返回ParameterizedType类型,否则调用getReturnType返回Class<?>类型。
查看Field、Constructor相关代码可以看出相似使用方法:
- Field类中属性类型 Class<?> getType()和Type getGenericType()
- Method类中方法返回值类型Class<?> getReturnType() 和Type getGenericReturnType()
- Method类中方法参数类型Class<?>[] getParameterTypes()和Type[] getGenericParameterTypes()
- Constructor类中方法参数和Method中方法参数完全一致
最后,如果需要解析Field、Method、Constructor中泛型相关类型,使用步骤和给出示例类似:
- 调用相关Genric对应的方法
- 判断返回值instanceof ParameterizedType
- 步骤为true强制转换为ParameterizedType类型
- 后续调用getActualTypeArguments等方法获取真实参数
参考文档
http://lavasoft.blog.51cto.com/62575/15433/
http://developer.51cto.com/art/201103/250028.htm
http://blog.csdn.net/benjaminzhang666/article/details/9838937