反射是Java中最重要的概念之一,它可以构成各个框架,能快速的搭建需要的类。所以,学Java就必须掌握好反射的应用。
Java程序中各个Java类属于同一类事物,描述这类事物的Java类名就是Class。Class类是反射的基石,只有通过Class类才能实现反射的功能。
如何获得Class类:
1. 类名.class
2. 对象.getClass()
3. Class.forName("类名")
需要注意的是:
int.class == Integer.TYPE
数组类型的Class不是基本数据类型,Class.isArray()
反射:就是把Java类中的各种成分映射成相应的Java类,然后对这些类进行操作。
如:Contructor,Field,Method,Package等
Constructor类:(构造函数类)
通过反射的方式来构造一个函数,实现方式代码:
Constructor<String> constructor1 =
String.class.getConstructor(StringBuilder.class);
String str1 = constructor1.newInstance(new StringBuilder("abc"));
System.out.println(str1.charAt(2));
先用Class类调用getConstructor(参数.class)方法得到Constructor类,再用newInstance("参数")的方法来创建一个这个类的对象。
Field类:(成员函数)
只要得到类中成员函数的变量名,就可以知道其中所有的成员变量类型和初始值,甚至能得到private成员变量的类型和初始值,功能十分强大。
//这了是一个类,其中有两个变量 private x , public y
ReflectPoint pt1 = new ReflectPoint(3,5);
//得到public变量的方法
Field feildY = pt1.getClass().getField("y");
System.out.println(feildY.get(pt1));
//得到private变量的方法
Field feildX = pt1.getClass().getDeclaredField("x");
//必须强制设置可获取
feildX.setAccessible(true);
System.out.println(feildX.get(pt1));
先用Class类调用getField("变量名")方法得到Field类,再用get(对象)的方法来获取该变量的值。
Method类:(方法)
通过反射可以调用到该类中的方法,只需要知道方法的名称即可。
//得到String类中的charAt方法,反射调用之
Method charAt = String.class.getMethod("charAt", int.class);
System.out.println(charAt.invoke(str, 1));
先用Class类调用getMethod("变量名")方法得到Method类,再用invoke(对象,参数...)的方法来调用这个方法。
数组反射:
通过Array类中方法来完成。
private static void printObject(Object obj) {
if (obj.getClass().isArray()) {
int len = Array.getLength(obj);
for (int i = 0; i < len; i++) {
System.out.println(Array.get(obj, i));
}
}else {
System.out.println(obj);
}
将数组转化成一个Object类,传递进去。通过isArray()方法确定其是否为数组,用Array.get(obj,index)来调用。
这就是反射的基本实现方式了,那么用了反射到底有什么好处?我们为什么需要用反射来调用类中的构造函数、成员变量和成员方法呢?
这就是反射的最大好处了,它可以先与类而产生,即搭建起来某个构架体系,可以调用到没创建的类中的各种成员、方法等,等后面的类出现,只要将它的各种名称通过一个配置文件传进来即可。
如定义一个config.properties文件,在里面写上键值对:
className=" "
method_1=" "
通过Properties类加载进类中即可,这样就能实现先于类调用类中的方法等功能,这便构成了整个框架的体系。
内省:
在我看来,内省就是反射过程中,因为要设置、获取类中的成员变量时,而通过getField方法实现代码太过于复杂。仅仅是设置、获取两个操作就要写上很多代码,不利于工作,从而简化内容而出现了内省机制。
通过规定设置用setXXX方法,获取用getXXX方法来统一命名,这样就可以把getField中复杂的代码封装在内部,对外提供一些简单的方法即可实现设置、获取操作。
当然,其中的内容很多,不只是这两个简单的操作,但是其原理却是不变的,用统一的命名方式来简化代码。
下面写出2种最简单的用反射获取到get和set方法的例子:
//通过使用PropertyDescriptor类,接收成员变量的名称,该类的Class对象
PropertyDescriptor pd1 = new PropertyDescriptor(propertyName,pt1.getClass());
//调用PropertyDescriptor类中的getWriteMethod()方法来得到设置方法
//调用getReadMethod()来得到获取方法
Method methodSetX = pd1.getWriteMethod();
methodSetX.invoke(pt1, setVal);
这是使用jdk自带jar包中的类来获取。
后来,有人觉得这样获取还是太麻烦了,干脆把这些方法也封装成对象,只要传入参数就可以直接用调用set、get方法,这便是BreanUtils.jar包和Logging.jar包。
我们只要从Apache官网下载这两个jar包,并导入到工程中,就能直接调用里面的方法:
BeanUtils.setProperty(Object, propertyName, String);
BeanUtils.setProperty(Object, propertyName);
就能直接设置和获取,十分方便,我们在工作中,就是需要使用不同的jar包来简化代码,这正体现了封装带来的好处,只要我们看了jar包的说明文档,知道其中方法的作用即可,不需要知道其中的代码。