Spring快速入门
上QQ阅读APP看书,第一时间看更新

1.2 反射

本节首先介绍反射的基本概念,理解什么是反射,以及Class类和反射常用API,通过实例操作来学习反射的使用。

1.2.1 反射机制

在上面自定义注解时我们也有提到反射,要获取类方法和字段的注解信息,必须通过Java的反射技术来获取Annotation对象。那么什么是反射呢?在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。它有点类似照妖镜的作用,不管是什么妖魔鬼怪(类或对象)都能看到它的真面目(获取类的属性方法、调用对象的属性方法)。

1.2.2 理解Class类

反射机制可以动态获取类信息以及调用对象方法,那它是通过什么实现的呢?这就要介绍一下Class类了。首先明确Class也是一个类,只是它是一个描述类的类,也可以生成对象。对于每个类而言,在JRE中有且仅有一个不变的Class类型的对象,而这个Class类型的对象只能由系统建立,封装了当前对象所对应的类的信息,有哪些属性、方法、构造器以及实现了哪些接口等。每个类的实例都会记得自己是由哪个Class实例所生成的。

要获取类信息或调用对象方法,肯定首先要获取到该类或对象对应的Class类的实例。一般获取Class对象有3种方式。

● 通过类名获取,类名.class。

● 通过对象获取,对象.getClass()。

● 通过全类名获取,Class.forName(全类名)。

这里我们可以使用字符串来做验证。

输出结果:

     class java.lang.String
     class java.lang.String
     class java.lang.String

通过3种方式获取到Class实例后,再了解一下Class类常用的方法(见表1-1)。

表1-1 Class类常用的方法

1.2.3 反射的使用

这里要着重介绍一下上面API的使用,因为在后面要学习的Spring中IOC的原理就是反射加工厂模式。学好反射API有助于理解Spring框架内部实现。为了演示Class方法的使用,在注解demo的基础上对Person、Student类进行了修改。

Person类:

Student类:

1.描述方法Method

描述方法主要是4个获取方法(getMethods、getMethod、getDeclaredMethods、getDeclaredMethod)和1个调用方法(invoke)。

● getMethods:获取clazz对应类中的所有方法,不能获取private方法,且获取从父类继承来的所有方法,包括私有父类的私有方法。

● getMethod:获取clazz对应类中指定方法名和参数类型的方法,不能获取private方法,且获取从父类继承来的所有方法,包括私有父类的私有方法。因为存在同方法名不同参数这种情况,所以只有同时指定方法名和参数类型才能唯一确定一个方法。

● getDeclaredMethods:获取所有方法,包括私有方法,所有声明的方法,都可以获取到,且只获取当前类的方法。

● getDeclaredMethod:获取clazz对应类中指定方法名和参数类型的方法,包括私有方法,所有声明的方法,都可以获取到,且只获取当前类的方法。

● invoke:执行方法,第一个参数表示执行哪个对象的方法,剩下的参数是执行方法时需要传入的参数,私有方法的执行必须在调用invoke之前加上一句“method.setAccessible(true);”。

输出结果:

上面我们基本可以实现通过类名创建对象、通过方法名执行方法。类名和方法名都是字符串,我们可以把它们放到一个配置文件中,根据配置文件来执行方法,这样就有点类似基于XML的Spring了。

2.描述字段Field

描述字段Field方法的使用和描述方法Method中方法的使用有点类似,也是4个获取字段的方法(getFields、getField、getDeclaredFields、getDeclaredField)。

● getFields:获得某个类的所有公共(public)字段,包括父类中的字段。

● getField:获取某个类public成员变量中指定变量名的字段,包括基类。

● getDeclaredFields:获得某个类所有声明的字段,包括public、private和protected,但是不包括父类的声明字段。

● getDeclaredField:获取某个类的所有成员变量指定变量名的字段,不包括基类。

输出结果:

     ---------getDeclaredFields---------
     StudentId
     ---------getFields---------
     StudentId
     ---------getDeclaredField---------
     StudentId
     ---------getField--------
     StudentId

上面通过反射获取字段,得到字段之后就是获取或设置字段的值了。如果字段是私有的,那么不管是读值还是写值,都必须先调用setAccessible(true)方法,比如在Person类中,字段name字段是私有的。

               Class clazz = Class.forName("Reflection.Person");
               Person person = new Person("CYW");
       //获取私有字段的值
               Field field = clazz.getDeclaredField("Name");
       //由于是私有字段,因此需要使用setAccessible(true)
       field.setAccessible(true);
               Object val = field.get(person);
               System.out.println(val);
       //改变私有字段的值
       field.set(person, "ivan");
               System.out.println(person.getName());

输出结果:

     CYW
     ivan

3.描述构造器Constructor

先介绍一下描述构造函数Constructor用到的方法,主要还是4个:getConstructors、getDeclaredConstructors、getConstructor、getDeclaredConstructor。和前面Method、Field用的方法进行比较,举一反三,我们也能大概了解这几个方法的使用。其实,在编程中有好多体现哲学思想的地方,有正有反,有阴有阳,学会思考,这样可以以点带面、触类旁通。

● getConstructors:获取对应类中public类型的构造函数,且只获取当前类的构造函数。

● getConstructor:获取对应类中public指定参数类型的构造函数,且只获取当前类的构造函数。

● getDeclaredConstructors:获取对应类中所有构造函数,包括私有构造函数,且只获取当前类的构造函数。

● getDeclaredConstructor:获取对应类中指定参数类型的方法,包括私有构造函数,且只获取当前类的方法。

输出结果:

4.描述注解Annotation

描述注解主要用到getAnnotation(Class<A> annotationClass)方法,返回该元素指定类型的注解,否则返回null。

输出结果:

     description:学生
     description:人