Java反射的API在JavaSE1.7的时候已经基本完善,但是本文编写的时候使用的是Oracle JDK11,因为JDK11对于sun包下的源码也上传了,可以直接通过IDE查看对应的源码和进行Debug。
本文主要介绍反射的基本概念以及核心类Class、Constructor、Method、Field、Parameter的常用方法。
本文极长,请准备一个使自己舒服的姿势阅读。
什么是反射反射(Reflection)是一种可以在运行时检查和动态调用类、构造、方法、属性等等的编程语言的能力,甚至可以不需要在编译期感知类的名称、方法的名称等等。Oracle关于Java反射的官方教程中指出反射是由应用程序使用,用于检查或修改在Java虚拟机中运行的应用程序的运行时行为,这是一个相对高级的功能,需要由掌握Java语言基础知识的开发者使用。
反射的优点有很多,前面提到可以检查或修改应用程序的运行时行为、抑制修饰符限制直接访问私有属性等等,这里主要列举一下它的缺点:
性能开销:由于反射涉及动态解析的类型,因此无法执行某些Java虚拟机优化。因此,反射操作的性能低于非反射操作,应避免在性能敏感应用程序中频繁调用反射操作代码片段。
安全限制:反射需要运行时权限,不能在安全管理器(security manager)下进行反射操作。
代码可移植性:反射代码打破了抽象,反射的类库有可能随着平台(JDK)升级发生改变,反射代码中允许执行非反射代码的逻辑例如允许访问私有字段,这些问题都有可能影响到代码的可移植性。
JDK中对和反射相关的类库集中在java.lang.reflect包和java.lang包中,java.lang.reflect包和java.lang包是开发者可以直接使用的,部分java.lang.reflect包中接口的实现类存放在sun.reflect包中,一般情况下sun包下的类库有可能跟随平台升级发生改变,一般尽量少用,否则有可能因为JDK升级导致原来的代码无法正常运行。还有部分反射相关的类库存放在jdk.internal.reflect包中,这个包是JDK内部使用的包,一般也不建议滥用其中的类库。可以理解为java.lang.reflect包和java.lang包中的类库就是面向开发者的类库。
图解反射核心类的体系java.lang.reflect包反射核心类有核心类Class、Constructor、Method、Field、Parameter,它们的基础体系如下:
java.lang.Class类继承体系:
java.lang.reflect.Constructor类继承体系:
java.lang.reflect.Method类继承体系:
java.lang.reflect.Field类继承体系:
java.lang.reflect.Parameter类继承体系:
由它们的类继承图可以看出:
Class、Constructor、Method、Field、Parameter共有的父接口是AnnotatedElement。
Constructor、Method、Field共有的父类是AnnotatedElement、AccessibleObject和Member。
Constructor、Method共有的父类是AnnotatedElement、AccessibleObject、Member、GenericDeclaration和Executable。
下面会先简单分析AnnotatedElement、AccessibleObject、Member、GenericDeclaration、Executable几个类提供的功能,然后重点分析Class、Constructor、Method、Field、Parameter的常用方法。
这里先说一个规律,在Class中,getXXX()方法和getDeclearedXXX()方法有所区别。注解类型Annotation的操作方法例外,因为基于注解的修饰符必定是public的:
getDeclaredMethod(s):返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。对于获取Method对象,Method[] methods = clazz.getDeclaredMethods();返回的是clazz本类所有修饰符(public、default、private、protected)的方法数组,但是不包含继承而来的方法。
getMethod(s):返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。对于获取Method对象,Method[] methods = clazz.getMethods();表示返回clazz的父类、父类接口、本类、本类接口中的全部修饰符为public的方法数组。
getDeclaredField(s)和getField(s)、getDeclaredConstructor(s)和getConstructor(s)同上。
getDeclaredAnnotation(s):返回直接存在于此元素上的所有注解,此方法将忽略继承的注解,准确来说就是忽略@Inherited注解的作用。
getAnnotation(s):返回此元素上存在的所有注解,包括继承的所有注解。