反射是程序在运行状态下,动态的获取某个类的内部信息的一种操作。例如:类名,包名,所有属性的集合,所有方法的集合,构造方法的集合等。该操作发生在程序的运行时状态,所以编译管不着有关反射的一些代码,通常只有在运行时才能暴露出程序的内部错误。反射的核心在于‘Class’这个类,本篇将从Class这个类开始介绍有关反射的一些基本的概念,主要内容如下:
- 获取Class对象
- 从Class对象中读取字段信息
- 从Class对象中读取方法信息
- 获取Class对象中的所有构造方法并使用它们动态创建类对象
- 反射的一些其他细节
一、获取Class对象
此处的Class是一个具体的(java.lang.Class)并不是我们自定义一个类时所使用的关键class。这是一个泛型类,通常有两种方法可以获取该对象。第一种方式,使用类名.class来获取Class对象。
![c7c6579b1786e1d69600fe3f7a608b5a.png](https://img-blog.csdnimg.cn/img_convert/c7c6579b1786e1d69600fe3f7a608b5a.png)
无论是基本数据类型,还是一般的class类型,或是接口类型,都是可以通过.class的方式来获取与之对应的Class对象。第二中获取Class对象的方法是,通过getClass方法,当然不是所有类都提供了这个方法的,例如:Object类就提供这么一个方法:
![8f258f85edd5c6a33bd6745df0293d9d.png](https://img-blog.csdnimg.cn/img_convert/8f258f85edd5c6a33bd6745df0293d9d.png)
还有我们的数组类型:
![6094ea487d417fdb1bed249df9ebabee.png](https://img-blog.csdnimg.cn/img_convert/6094ea487d417fdb1bed249df9ebabee.png)
下面看看有关我们获取到的这个Class对象的一些基本信息。首先我们可以获取有关该Class对象的名称,包,父类,父接口等信息。
![ce4bef1489892ef86f1e8fe17c040830.png](https://img-blog.csdnimg.cn/img_convert/ce4bef1489892ef86f1e8fe17c040830.png)
还有一些有关注解的信息,由于之前在介绍注解的时候已经说明过,此处不再赘述。上面的这些方法的使用还是比较简单,此处不再演示。
二、获取Class对象中字段信息
类中的字段包括实例域和静态域。在Java反射机制中,使用Field类管理字段信息。
![306b6ea6c60f07185dbed880a51ce8db.png](https://img-blog.csdnimg.cn/img_convert/306b6ea6c60f07185dbed880a51ce8db.png)
对以上四种获取字段Field的方法做一点说明,getFields或者getField方法可以看见该类所有的被public修饰的字段,包括从父类继承的,但是不可见非public修饰的字段。getDeclaredFields或者getDeclaredField方法可以看见该类所有的字段,包括非public修饰的,但是不可见父类中的字段。接下来,我们看看对获取到的field字段可以做哪些操作:
![6fb627c2216770febc600ca89e37935b.png](https://img-blog.csdnimg.cn/img_convert/6fb627c2216770febc600ca89e37935b.png)
对于以上的几个基本的方法,可以说见名知意,这里需要说明的是get和set方法,先看个例子:
![00a5a0676eb7edc463dc6fb9226054ef.png](https://img-blog.csdnimg.cn/img_convert/00a5a0676eb7edc463dc6fb9226054ef.png)
这里的 f 代表了 school这个实例属性,如果别的类中有相同的属性,我们是可以通过 f为该属性赋值的,当然也可以从某个具有该属性的类中获取该属性的值,前提是具备目标类中的该属性的访问权限。上述的getModifiers返回的是int值,该值代表了一个修饰符,想要转换成我们能看懂的字符串的形式需要使用 Modifier.toString(int a)方法,将刚刚返回的int值作为参数传入即可。
三、获取Class对象的方法信息
无论是静态方法还是实例方法,在Java反射机制中都是使用Method这个类来管理的,一个方法对应于一个Method对象。先看怎么通过Class对象获取其中的方法。
![dca0a412d8ad7402deb5ac2f90146415.png](https://img-blog.csdnimg.cn/img_convert/dca0a412d8ad7402deb5ac2f90146415.png)
对于以上四个方法需要说明一点的是,getMethod或者getDeclaredMethod在获取指定方法的时候,需要传入该方法的形参类型,如果没有传入或者传入错误就会抛出NoSuchMethodException异常。因为方法名和方法参数类型(个数)是唯一确定一个方法的凭据。对于获取到的Method方法有以下一些操作:
![c58b977c74c15858b21d7cc55252df76.png](https://img-blog.csdnimg.cn/img_convert/c58b977c74c15858b21d7cc55252df76.png)
上述方法中的invoke方法和之前介绍的字段的get/set方法一样,需要指定目的对象才能使用,因为他们只是代表了具体的一个字段或者一个方法。
![bde3fa2e12c6800f0ccd0c81d1598d2a.png](https://img-blog.csdnimg.cn/img_convert/bde3fa2e12c6800f0ccd0c81d1598d2a.png)
四、获取构造方法和创建实例对象
一旦我们获取到了Class对象,我们就可以通过以下的方法进一步获取其中的构造方法,并使用它们创建一个实例对象。
![ff6a5a803fcb65f65ac96e6a7ad10526.png](https://img-blog.csdnimg.cn/img_convert/ff6a5a803fcb65f65ac96e6a7ad10526.png)
以上的方法和之前介绍的字段Field,方法Method很是相似。下面我们看看如何使用Constructor做一些事情。
![c9786d366d52cb80277a26b48455f694.png](https://img-blog.csdnimg.cn/img_convert/c9786d366d52cb80277a26b48455f694.png)
我们主要看看如何通过 newInstance 创建一个实例对象:
![626ff6643ade20b2f995b586ad752289.png](https://img-blog.csdnimg.cn/img_convert/626ff6643ade20b2f995b586ad752289.png)
五、反射机制的一些细节
前面我们一直讨论的都是具体的类,我们可以从这些类中获取到字段,方法,构造器,注解等。java.lang.reflect包中对数组类型增添了专门的类Array来实现反射,这里的Array和数组中的Arrays是不同的。
![bc678741619793648c1c9e1e7c590454.png](https://img-blog.csdnimg.cn/img_convert/bc678741619793648c1c9e1e7c590454.png)
我们可以通过Array类在运行时动态创建数组和操作数组中的元素,而不必想之前一样必须在编译之前完成数组的创建。需要注意的是此处返回的数组类型是Object而非Object[],那时因为前者可以转化成具体类型的数组,后者则不能。
至此,反射的基本内容介绍完了,我们应该知道,虽然反射很是灵活,可以动态的读取类的信息,动态的创建实例对象和数组等,但是没有了编译器的一层检查,很容易导致运行是异常。如果能够用接口来完成的,尽量使用接口来完成。
最后,我自己是一名从事了多年开发的Java老程序员,辞职目前在做自己的Java私人定制课程,今年年初我花了一个月整理了一份最适合2019年学习的Java学习干货,可以送给每一位喜欢Java的小伙伴,想要获取的可以关注我的头条号并在后台私信我:01,即可免费获取。