java基础——反射

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提供常用方法

  1. getClassLoader() :返回该类的类加载器
  2. isArray() :判断是否是数组
  3. getName():以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称
  4. newInstance():可以创建该类的示例
  5. 同时提供了获取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对象我们可以实现以下常见功能:

  1. String getName():获取字段名
  2. Class getType() 和 Type getGenericType() :获取类型和泛型
  3. int getModifiers():获取修饰
  4. Object get(Object obj):获取指定对象该字段对应值
  5. void set(Object obj, Object value):给指定对象的该段赋值

这里演示一下如何修改private的属性值:

java基础——反射_第1张图片
修改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对象以后,我们可以实现以下常见功能:

  1. Class[] getParameterTypes():获取该方法的所有参数类型
  2. Class getReturnType():获取该方法返回值
  3. T getAnnotation(Class annotationClass):获取方法注解
  4. Object invoke(Object obj, Object... args)执行该方法

同样这里演示如何执行一个private方法:

java基础——反射_第2张图片
执行private方法

同样在执行private方法之前需要执行method.setAccessible(true),关闭对private方法访问检查。

2.3 Constructor类

表示类的构造方法,其中一个构造方法对应一个Constructor对象。Class获取Constructor的方法如下:

  • getConstructors():获得类的public类型的构造方法
  • getDeclaredConstructors():获取所有构造方法
  • getConstructor(Class... parameterTypes):获得类的特定public构造方法,parameterTypes 参数指定构造方法的参数类型。
  • getDeclaredConstructor(Class... parameterTypes):同上范围扩大到所有构造方法
    同样获取Constructor对象以后,我们可以实现以下常见功能:
  1. Class[] getParameterTypes():构造方法参数列表
  2. T newInstance(Object ... initargs):实例化对应类

接下来示例展示如何利用Constructor对象实例化对应类

java基础——反射_第3张图片
Constructor实例化对象

2.4 反射与泛型

常见两种泛型使用场景:

  • 声明一个需要被参数化(parameterizable)的类/接口,例如:class MyClass
  • 使用一个参数化类,例如:List arrays;
2.4.1Type接口

在此之前我们需要介绍一下Type接口,Java编程语言中所有类型公共父接口,这里所说的所有类型包括:
原始类型 (raw types)对应Class,参数化类型 (parameterizedtypes)对应ParameterizedType, 数组类型 (array types)对应GenericArrayType,类型变量 (type variables)对应TypeVariable,基本数据类型(primitivetypes)仍然对应Class。

Class类实现了该接口,同时该接口的直接子接口包括以下4个:

  • ParameterizedType: 表示一种参数化的类型,比如Collection
  • GenericArrayType: 表示一种元素类型参数化类型或者类型变量数组类型
  • TypeVariable: 是各种类型变量公共父接口
  • WildcardType: 代表一种通配符类型表达式

配合后面的反射内容重点介绍一下java.lang.reflect.ParameterizedType接口

  • 含义:表示参数化类型比如:Map这种参数化类型
  • Type[] getActualTypeArguments():获取参数化类型<>中的实际类型(注意对于多次嵌套,该方法只返回脱去最外层的<>以后剩下内容返回)
  • 关于返回值是数组因为存在Map返回不止一种类型
  • 为什么返回父接口,因为返回值多态性,对于ArrayList>返回的是ArrayListParameterizedType类型的,而对于ArrayList返回的是E是TypeVariable类型,而对于 对于ArrayList返回的是E[]则对应的是GenericArrayType类型。
  • Type getRawType():获取承载该泛型信息的对象,该类型表示<>前面的类型比如Map,则返回Map
2.4.2参数化类型反射获取

讲述完Type相关内容以后,我们来看下Field、Method和Constructor相关类如何获取泛型参数或泛型返回值。这里我们以Method为例说明:

java基础——反射_第4张图片
方法返回值参数化类型获取

在这个示例中我们重点关注一下method.getGenericReturnType()方法,返回返回值Type,查看jdk源码:

java基础——反射_第5张图片
getGenericReturnType
java基础——反射_第6张图片
getReturnType

观察代码可以发现在获取泛型返回是首先判断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中泛型相关类型,使用步骤和给出示例类似:

  1. 调用相关Genric对应的方法
  2. 判断返回值instanceof ParameterizedType
  3. 步骤为true强制转换为ParameterizedType类型
  4. 后续调用getActualTypeArguments等方法获取真实参数

参考文档

http://lavasoft.blog.51cto.com/62575/15433/
http://developer.51cto.com/art/201103/250028.htm
http://blog.csdn.net/benjaminzhang666/article/details/9838937

你可能感兴趣的:(java基础——反射)