反射机制
在Java virtual Machine(JVM)中保存有Java编译之后生成的字节码文件,反射机制可以通过细节吗文件找到类对应的方法以及属性。
在程序的运行状态下:
-
对于任意一个类,通过反射都能知道这个类的所有属性和方法;
-
对于任意一个对象,通过反射都能够调用该对象的任意一个方法和属性;
-
在运行时动态获取类的信息和动态调用对象方法和属性的操作是Java的反射机制。
反射机制的优缺点
优点
-
可以在运行过程中自由的创建对象
-
反射可以构建出无法直接访问的类
-
可以设置和获取到无法访问的方法和属性
-
可以调用到无法直接访问的方法
缺点
通过字节码反射出对象的类型以及每个类对应的方法和属性的操作耗费的实践远远大于正常Java代码执行的时间,降低了程序的性能。
反射机制的应用
-
JDBC中加载数据库驱动
-
Spring IOC中工厂模式创建对象也使用到了反射机制
-
在开发工具中动态提示类型信息,方法解释的操作也类似Java的反射机制
反射机制的使用
1、获取类型(Class)信息
-
通过字面量直接获取, Class obj_class = XXX.class;
-
通过Object对象的getClass() 方法获取, Class obj_class = obj.getClass();
-
通过Class类的静态方法forName() ,方法获取,Class obj_class = Class.forName("全类名");
2、获取成员变量(Field)(属性)信息
-
通过getFields() 方法可以获取所有public声明的成员变量信息
Class obj_class = Class.forName("全类名"); Filed[] fs = obj_class.getFields(); for(Filed f : fs){ if(f.getName().equals("myPorperty")) System.out.println(f); }
-
通过getDeclaredFields() 方法可以获取所有成员变量信息
Class obj_class = Class.forName("全类名"); Filed[] fs = c.getDeclaredFields(); for(Filed f : fs){ if(f.getName().equals("myPorperty")) System.out.println(f); }
3、获取成员方法信息(Method)
-
通过getMethods() 方法来获取公有成员方法以及静态方法信息
-
通过getDeclaredMethods() 方法来获取成员方法以及静态方法信息
-
也可获取到抽象方法
-
通过invoke() 来完成方法的调用
Class obj_class = Class.forName("全类名");
Method[] ms = obj_class.getMethod()
for(Method m : ms){
if(m.getName().equals("myMethod")){
m.setAccessible(true); //设置访问标识(屏蔽Java语言的运行时访问检查)
String res = (String)m.invoke(obj, "方法参数");
System.out.println(res);
}
}
注意:
-
setAccessible()这个可访问标志表示是否屏蔽Java语言的访问检查,默认值是false
-
不论是公有成员还是私有成员,默认值都为false
-
setAccessible(true) 可以修改默认值,这样会屏蔽Java语言的(运行时)访问检查,使得对象的私有成员可以访问而不报错。
反射机制优化Servlet程序
在Servlet程序中为了减少类的创建数量,一个Servlet程序一般是用来处理一个模块的请求的,而不是一个Servlet程序处理一个请求,比如一个用户模块中有注册,登录,修改密码等不同的请求,此时我们就可以使用一个UserServlet程序来处理这一系列的请求。
这个时候我们可以通过input标签的隐藏域来标识处理该请求的方法名,请求提交到Servlet程序之后,我们通过隐藏域的值来判断具体该调用哪一个方法处理请求,在这里我们可以使用if-else分支来判断,但是if-else语句的使用会使得代码的扩展性降低。
我们知道设计模式中有策略模式和责任链模式都可以用来优化大量的if-else语句块,并且Java的反射机制也和策略模式的原理很相似,此时我们就可以使用Java的反射机制来优化这个功能。
当我们获取到input的隐藏域之后,通过隐藏域中方法名可以反射出具体的方法,然后通过invoke调用即可。
并且我们可以将反射调用具体方法的这个功能封装起来,作为一个BaseServlet。之后每次编写代码时就只需要在input隐藏域中指定处理请求的方法,然后编写的对应的Servlet程序继承BaseServlet类,再编写处理请求的具体方法即可。这样就很好的提高了代码的扩展性。