1、理论
概念:动态获取或者动态调用对象的方法(对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性) 的功能我们称之为java语言的反射机制
程序的动态特性:大多数情况下,程序的功能是在编译的时候确定下来的,称之为静态特性;而如果程序的功能是在运行时才确定的称为动态特性。
程序动态:一般而言的动态定义是程序运行时,允许改变程序结构或变量类型,将类中的所有成员分别抽象为独立的对象,反射发生在类的加载过程
静态加载
静态加载:编译期间加载相关的类,如果没有该类就会报错,也就是我们在编写程序时是否有申明该类,所以这种方式对类与类之间的依赖关系太强
动态加载
动态加载:程序运行时按需加载要使用的类,也就是使用了懒加载机制,只有需要的时候才加载要使用的类进入内存,降低了类与类之间的依赖关系
2、Class对象
此Class非class,Class是一个类,而class是一个关键字。
获取Class对象的方式
1、类对象.getClass();------使用时期---->类运行阶段
特点:JVM将使用类装载器, 将类装入内存(前提是:类还没有装入内存),不做类的初始化工作.返回Class的对象
2、类名.Class;-----------使用时期------>Class对象时期(内存时期)
特点:装入类,并做类的静态初始化,返回Class的对象
3、Class.forName("类的权限定名");-----使用时期--->字节码文件时期
特点:对类进行静态初始化、非静态初始化;返回引用运行时真正所指的对象所属的类的Class的对象(因为:子对象的引用可能会赋给父对象的引用变量中)
3、反射机制图解
4、反射中的基本类
反射中主要用到的是前四个,最后一个是父类,所有类都是由此类扩展而来
1、Constructor类
该类用于获取指定类的构造方法
一、得到构造方法
//得到指定类的所有public修饰的构造方法
public Constructor[] getConstructors();
//得到指定类中所有的构造方法(包括 private)
public Constructor getDeclaredConstructor();
//得到指定的、被public修饰的构造方法
public Constructor getConstructor(Class ...paramType);
//得到指定的构造方法(包括private)
public Constructor getDeclaredConstructorr(Class ...paramType);
二、常用方法
getParamterTypes() //得到形参类型
newInstance(Object ...init) //实例化该对象,形参为值 : 对象.Class.getDeclaredConstructor(String.class,int.class).newInstance("向明",23);
setAccessible(boolean flag) //当得到的构造器是provate时就需要打开构造器的访问状态
2、Field类
该类用于得到指定类的字段
一、得到字段
public Field[] getFields()
//得到所有public修饰的字段
public Field getField(String fieldName)
//得到指定的、被public修饰的字段
public Field[] getDeclaredFields()
//得到所有的字段
public Filed getDeclaredFields(String fieldName)
//得到指定的字段
二、常用方法
getName() //得到字段名
getType() //得到字段类型
get(Object obj) //获取指定成员的值
set(Object obj , Object value) //将对象中指定的字段值设置成value
setAccessible(boolean flag) //设置私有属性的访问状态
3、Method类
该类用于得到指定类的方法
一、得到方法
public Method[] getMethods()
//得到所有public修饰的方法
public Method[] getDeclaredMehtods()
//得到所有的方法
public Method getMethod(String methodName,Class ...para)
//得到指定的、被public修饰的方法,methodName为方法名,para为方法中的参数,需要以Class对象的形式传入
public Methodd getDeclaredMethod(String methodName,Class ...para)
//得到指定的方法,methodName为方法名,para为方法中的参数,需要以Class对象的形式传入
二、常用方法
getName() //得到方法名称
getParameterTypes() //得到参数类型
getExceptioTypes() //得到抛出异常的类型
invoke(Object obj,Object ...param) //指定获取到了方法,obj为该方法所属的对象,param为传入的形参值
5、反射的使用
一、在后端的使用
public class BeanUtil {
public static Object toVO(Class classType, Object pojo) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class cl = pojo.getClass();//得到形参对象的Class对象
Field[] fArr = cl.getDeclaredFields();//通过形参的Class对象得到该类里的所有字段
Method[] methods = classType.getMethods();//得到第一个形参Class对象中的所有方法
Object object = classType.getConstructor().newInstance();//通过默认的构造器创建一个对象
//遍历字段
for (Field field : fArr) {
field.setAccessible(true);//将private修饰的字段设置成允许访问
String fieldName = field.getName();//得到字段的名称
String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
//遍历所有得到的方法
for (Method method : methods) {
if (methodName.equals(method.getName())) {
method.invoke(object,field.get(pojo));
}
}
}
return object;
}
}
二、在前端的使用
//前端jsp中的一个小代码块
<c:if test="${not empty loginUser}">
<a href="address?method=show" id="a_top">${loginUser.username}</a>
<li>|</li>
<a href="user?method=logOut" id="a_top">注销</a>
<li>|</li>
<a href="order?method=show" id="a_top">我的订单</a>
<li>|</li>
<a href="userservlet?method=getAddress" id="a_top">地址管理</a>
</c:if>
//后台处理代码
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取请求参数(标识符)
String methodStr = req.getParameter(Constants.TAG);//得到传入的参数
//2.如果method没有获取到值!我们就跳转到首页!(标识符异常处理)
if (methodStr == null && methodStr.equals("")) {//判断参数是非合法
methodStr = Constants.INDEX;
}
//3.反射调用对应的业务逻辑方法
Class clazz = this.getClass();//得到本类的Class对象
try {
//通过Class对象得到指定方法对象
Method method = clazz.getMethod(methodStr, HttpServletRequest.class, HttpServletResponse.class);
//执行通过反射得到的方法
Object result = method.invoke(this,req,resp);
//其中不重要的代码已省略。。。
} catch (Exception e) {
e.printStackTrace();
//controller 和 service dao 有异常都会到此处!
req.getSession().setAttribute("msg", "程序异常!请稍后再试!");
resp.sendRedirect("/message.jsp");
}
总结
使用反射的优缺点:
优点:可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等
缺点:反射使用不当会造成很高的资源消耗!