反射是框架设计的灵魂,通过反射我们可以在程序运行时读取配置文件动态的创建一个类,也可以通过读取注解来达到我们想到的功能。与反射有关的类是Class类。
Class类对象的获取方法
Class类对象的获取方法有三种
- Class.forName(String name)
- 参数是类的全类名(包名+类名),可以获得该类的Class对象
- 类名.class
- 通过类的静态属性class可以获取Class对象
- 对象.getClass()
- 通过对象的getClass()方法可以获取Class对象
假设一个Animal类
public class Animal { |
现在要通过上面的三种方法获取该类的Class对象
//通过全类名获取Class对象 |
另外需要注意的是,通过上面三种方法获取的Class对象是同一个对象
System.out.println(cls1 == cls2); //true |
Class类的常见成员方法
与成员变量有关的方法
- getFields()
- 获取public修饰的所有成员变量,返回一个Field[]
- getField(String name)
- 获取指定名称被public修饰的成员变量
- getDeclaredFields()
- 同getFields(),不过任意修饰符修饰的都可以获取
- getDeclaredField(String name)
- 同getField(String name),不过任意修饰符修饰的都可以获取
Field[] fields1 = cls1.getFields(); |
可以通过set(Object obj, Object value)方法对指定的对象设定值,也可以通过get(Object obj)方法来获取值
Field field2 = cls1.getDeclaredField("name"); |
如果我们要对age进行赋值,因为age变量是private修饰的,是不能在类外面直接访问的,我们可以调用setAccessible(true)方法,来忽略访问修饰符的安全检查(暴力反射)
Field field1 = cls1.getDeclaredField("age"); |
与构造方法有关的方法
- getConstructors()
- 得到由public修饰的所有构造方法
- getConstructor()
- 获取指定参数的构造函数,如
- getConstructor():获取无参构造函数
- getConstructor(String.class, int.class):获得第一个参数类型为String类型和第二个参数类型为int类型的构造方法
- 获取指定参数的构造函数,如
- cls1.getDeclaredConstructors()
- 参照Field
- cls1.getDeclaredConstructor()
- 参照Field
//获得无参的构造方法 |
现在获得了构造方法,那么构造方法的作用就是创造对象,我们可以通过Contructor对象的的newInstance()方法创建一个对象,如
//newInstance方法返回的是一个Object对象 |
输出为
Animal{name='null', age=0} |
如果想创建一个无参的对象,可以直接通过Class对象的newInsatance()方法创建,如
Animal animal3 = (Animal) cls1.newInstance(); |
与成员方法有关的方法
- getMethods
- 获取所有public修饰的方法
- getMethod()
- 通过方法名和参数类型(区别重载的方法)获取public修饰的指定方法
- getDeclaredMethods
- 忽略修饰符
- getDeclaredMethod
- 忽略修饰符
//获取不带参数的eat方法 |
获取到了方法,那么接下来就是怎么使用的问题,我们可以使用invoke()方法来执行方法,需要传入相应的对象和需要的参数,如果方法不需要参数,那么可以不传,如
eat1.invoke(animal); |
输出为
eat ... |
我们还可以通过getName()方法获得方法名,如
System.out.println(eat1.getName()); //eat |
我们可以通过Class对象的getName()方法获得该类的全类名(包名+类名)
System.out.println(cls1.getName()); //Animal |
使用反射读取配置文件动态创建任意的对象
我们现在有这么一个需求,那就是希望创建任意一个类的对象,并且调用相应的方法,要求不能更改代码,而只需要更改配置文件即可。现在我们创建一个config.properties的配置文件,内容如下
className=Animal |
我们要做的就是读取配置文件,然后根据配置文件创建相应类的对象并且调用其方法
import java.io.FileNotFoundException; |
现在我们运行一下
eat ... |
现在我们要创建什么类的对象并且要调用什么方法,只需要修改配置文件就可以了,不用修改代码了。虽然还有很多的问题,比如只能使用无参构造方法创建对象,只能调用无参的方法,不过即使是这样也让我们感受到了反射的强大。