Java反射
什么是反射
Java反射是可以让我们在运行时获取类的函数、属性、父类、接口等Class内部信息的机制。通过反射还可以让我们在运行期实例化对象,调用方法,通过调用get/set方法获取和设置变量的值,即使方法或属性是类私有的也可以通过反射的形式调用,这种“看透Class“的能力被称为内省,这种能力在框架开发中尤为重要。有些情况下,我们要使用的类在运行时才会确定,这个时候我们不能在编译期就使用它,因此只能通过反射的形式来使用在运行时才存在的类(该类符合某种特定规范,例如JDBC),这是反射用得比较多的场景。
还有一个比较常见的场景就是编译时我们对于类的内部信息不可知,必须得到运行时才能获取类的具体信息。比如ORM框架,在运行时才能够获取类中的各个属性,然后通过反射的形式获取其属性名和值,存入数据库,这也是反射比较经典应用场景之一。
Class类
既然反射是操作Class信息的,那么Class又是什么呢?
当我们编写完一个Java项目之后,所有的Java文件都会被编译成一个.class文件,这些Class对象承载了这个类型的父类、接口、构造函数、方法、属性等原始信息,这些class文件在程序运行时会被ClassLoader加载到虚拟机中。当一个类被加载以后,Java虚拟机就会在内存中自动产生一个Class对象。我们通过new的形式创建对象实际上就是通过这些Class来创建,只是这个过程对于我们是不透明而已。
反射Class以及构造对象
获取Class对象
在你想检查一个类的信息之前,你首先需要获取类的Class对象。Java中的所有类型包括基本类型,即使是数组都有与之关联的Class类的对象。如果你在编译期知道一个类的名字的话,那么你可以使用如下的方式获取一个类的Class对象。
|
|
如果你已经得到了某个对象,但是你想获取这个对象的Class对象,那么你可以通过下面的方法得到:
|
|
如果你在编译期获取不到目标类型,但是你知道它的完整类路径,那么你可以通过如下的形式来获取Class对象:
|
|
在使用Class.forName()方法时,你必须提供一个类的全名,这个全名包括类所在的包的名字。例如String类位于java.lang包中,那么它的完整路径就是java.lang.String。如果在调用Class.forName()方法时,没有在编译路径下(classpath)找到对象的类,那么将会抛出ClassNotFoundException。
方法说明
|
|
通过Class对象构造目标类型的对象
一旦你拿到Class对象之后,你就可以为所欲为了,但获取Class对象只是第一步,我们需要在执行那些强大的行为之前通过Class对象构造出该类型的对象,然后才能通过该对象释放它的功能。我们知道,在java中要构造对象,必须通过该类的构造函数,那么其实反射也是一样的。但是它们确实是有区别的,通过反射构造对象,我们首先要获取类的Constructor(构造器)对象,然后通过Constructor来创建目标类的对象。
|
|
通过上述代码,我们就可以在运行时通过完整的类名来构建对象。
获取构造函数方法
|
|
注意,当你通过反射获取到Constructor、Method、Filed后,在反射调用之前将此对象的accessible标志设置为true,以此来提升反射速度。值为true则指示反射的对象在使用时应该取消Java语言访问检查。值为false则指示反射的对象应该实施Java语言访问检查。例如:
|
|
Student.java
|
|
Person.java
|
|
Examination.java
|
|
Breathe.java
|
|
反射获取类中函数
获取当前类中定义的方法
要获取当前类中定义的所有方法可以通过Class中的getDeclaredMethods函数,它会获取当前类中的public、default、protected、private的所有方法。getDeclaredMethod(String name, Class...<?> parameterTypes)
则是获取某个指定的方法。代码示例如下:
|
|
获取当前类、父类中定义的公有方法
要获取当前类以及父类中的所有public方法可以通过Class中的getMethods函数,而getMethod则是获取某个指定的方法。代码示例如下:
|
|
接口说明
|
|
这里需要注意的是 getDeclaredMethod 和 getDeclaredMethods 包含 private、protected、default、public 的函数,并且通过这两个函数获取到的只是在自身中定义的函数,从父类中集成的函数不能够获取到。而 getMethod 和 getMethods 只包含 public 函数,父类中的公有函数也能够获取到。
反射类获取类中的属性
获取当前类中定义的属性
要获取当前类中定义的所有属性可以通过Class中的getDeclaredFields函数,它会获取到当前类中的public、protected、private的所有属性,而getDeclaredField则是获取指定的属性。
|
|
获取当前类、父类中定义的公有属性
要获取当前类以及父类中的所有 public 属性可以通过 Class 中的 getFields 函数,而 getField 则是获取某个指定的属性。代码示例如下 :
|
|
接口说明
|
|
这里需要注意的是 getDeclaredField 和 getDeclaredFields 包含 private、protected、default、public 的属性,并且通过这两个函数获取到的只是在自身中定义的属性,从父类中集成的属性不能够获取到。而 getField 和 getFields 只包含 public 属性,父类中的公有属性也能够获取到。
反射获取父类与接口
获取父类
获取Class对象的父类
|
|
获取接口
|
|
获取注解信息
在框架开发中,注解加反射的组合使用是最为常见形式的。定义注解时我们会通过@Target 指定该注解能够作用的类型,看如下示例:
|
|
上述注解的@target 表示该注解只能用在函数上,还有 Type、Field、PARAMETER 等类型,可以参考上述给出的参考资料。通过反射 api 我们也能够获取一个 Class 对象获取类型、属性、函数等相关的对象,通过这些对象的 getAnnotation 接口获取到对应的注解信息。 首先我们需要在目标对象上添加上注解,例如 :
|
|
然后通过相关的注解函数得到注解信息,如下所示 :
|
|
输出结果为:
接口说明
后记
反射作为 Java 语言的重要特性,在开发中有着极为重要的作用。很多开发框架都是基于反射来实现对目标对象的操作,而反射配合注解更是设计开发框架的主流选择