java基础——反射

作者: 陈晨_Fly | 来源:发表于2016-12-23 10:38 被阅读321次

    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的属性值:

    修改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 extends Annotation> T getAnnotation(Class<T> annotationClass):获取方法注解
    4. Object invoke(Object obj, Object... args)执行该方法

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

    执行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对象实例化对应类

    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源码:

    getGenericReturnType 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基础——反射

        本文链接:https://www.haomeiwen.com/subject/scdnmttx.html