一 . 类加载机制
*.java(源文件)–>*.class(字节码文件)
类加载机制: 将一个类装载到jvm的过程.
1 . 加载
—连接操作-------
2 . 验证()
3 . 准备: 为静态成员变量设置默认值.
4 . 解释
—连接操作-------
5 . 初始化: 对类中静态成分执行初始化, 为静态变量赋值, 执行静态初始化块.
6 . 使用
7 . 卸载
java中只有两种成员与对象无关:
– 基本数据类型(char, byte, short, int, long, float, double).
– 静态成员(静态属性, 静态方法).
二 . Class类
java中的所有类都是Class类实例, Class代表在jvm中正在执行的一个类的实例.(Object也是Class的一个实例)
Class中包含了当前实例的运行信息, 内部构造(成员变量, 方法, 注解, 实现接口…)
反射: 将一个类中所有成分反射为对应的java类.
– 属性类
– 构造器类
– 方法类
– 注解类
一个正在运行的类可以通过反射机制获取到类自身中的具体信息(包含在Class实例中)
**动态绑定:**多态上有体现.
三 . 获取一个正在执行的类的Class(镜子)对象有三种方法
方法一: 调用类的class属性获取. 如: Student.class
方法二: 通过实例对象的getClass()方法获取, 如:
Student stu = new Student();
Class clz = stu.getClass();
方法三: 通过类加载器加载
//1.不会执行类中静态成分(不初始化)
Student.class.getClassLoader.loadClass("com.softeeem.dto.Student");
//2.会执行类中静态成分(初始化)
Class.forName("com.softeem.dto.Student");
具体的一些方法使用如下:
//利用Class实例化对象(注:该对象的类一定要有无参构造器)
Object obj = clz.newInstance();
//获取类中属性(public属性)
Field[] fields = clz.getFields();
//获取类中所有属性(包括私有private)
fields = clz.getDeclaredFields();
//获取其中某一个属性对象(sname)
Field f = clz.getField("sname");//会有异常(若是私有,则getDeclaredField("sname"))
//获取当前属性的类型(返回值是Class对象)
f.getType();
//获取当前属性的访问修饰符所表示的常量字段值
f.getModifiers();//如:public->1
//将当前属性(public)所在对象的值设置为指定值(注意类型要匹配)
f.set(obj,"狗蛋");
//获取当前属性在指定对象上的值
f.get(obj);//获得sname值
//获取属性名称
f.getName();
/****获取方法****/
//获取当前对象所在类的所有public方法(包括其父类方法)
Method[] methods = clz.getMethod();
//获取一个指定的方法(通过方法名称,属性类型获取方法对象)
Method method = clz.getMethod("setSex",clz.getField("sex").getType());
//方法执行(注:此方法返回值,无则null,有则Object[])
method.invoke(obj,"未知");//sex = "未知";
四 . 反射优化servlet请求
如下一个index.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<p><a href="goods?method=add">商品上架</a></p>
<p><a href="goods?method=update">商品修改</a></p>
<p><a href="goods?method=delete">商品下架</a></p>
<p><a href="goods?method=list">商品列表</a></p>
<hr>
<p><a href="category?method=add">类别添加</a></p>
<p><a href="category?method=update">类别修改</a></p>
<p><a href="category?method=delete">类别删除</a></p>
<p><a href="category?method=list">类别列表</a></p>
<hr>
<p><a href="user?method=register">用户注册</a></p>
<p><a href="user?method=login">用户登录</a></p>
<p><a href="user?method=logout">注销</a></p>
<hr>
<form action="user">
<input type="hidden" name="method" value="register">
<input type="text" name="username"/>
<input type="password" name="password" />
<button>注册</button>
</form>
</body>
</html>
此时我们可能需要三个servlet, 分别为GoodsServlet, CategoryServlet, UserServlet按照博主之前对servlet的掌控能力, 只能让jsp再多传一个flag(如: Servlet?flag=goods&method=add), 然后在Servlet中配合switch解决 , 这样虽然减少了重复代码, 但代码比较混乱, 同时也增加了jsp请求的难度, 如: 表单请求Servlet, flag需要隐藏域传值.
此时反射可以优化一下Servlet, 首先要建一个BaseServlet, 自然要重写HttpServlet的doGet和doPost方法, 之后我们三个目标Servlet只需要继承它既可(不需要再重写doGet和doPost), 听起来和反射没有关系? 来看一看BaseServlet, 如下:
package com.softeem.servlet;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class BaseServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
//获取客户端请求的方法名称
String methodName = req.getParameter("method");
//根据客户端请求的方法名称通过反射机制获取方法对象
Method method = this.getClass().getMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
//执行方法
method.invoke(this, req,resp);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
看到关系了吧? 运用反射调用方法.
为了表现一下这样优化的结果, 看一下GoodsServlet:
package com.softeem.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class GoodsServlet
*/
@WebServlet("/goods")
public class GoodsServlet extends BaseServlet {
public void add(HttpServletRequest request,HttpServletResponse response){
System.out.println("添加商品");
}
public void update(HttpServletRequest request,HttpServletResponse response){
System.out.println("商品修改");
}
public void delete(HttpServletRequest request,HttpServletResponse response){
System.out.println("商品下架");
}
public void list(HttpServletRequest request,HttpServletResponse response){
System.out.println("商品列表");
}
}
在GoodsServlet中只需要直接写出所需方法既可, 代码是不是简洁明了许多?
其实在Servlet中运用反射还可以做一个优化, 可以直接将来自jsp传递的数据转化为dto对象, 如上index.jsp中user注册信息表单提交, 看一下UserServlet中代码:
package com.softeem.servlet;
import java.util.Map;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.softeem.dto.User;
/**
* Servlet implementation class GoodsServlet
*/
@WebServlet("/user")
public class UserServlet extends BaseServlet {
private User user;
public void register(HttpServletRequest request,HttpServletResponse response){
// String name = request.getParameter("username");
// String pass = request.getParameter("password");
//该map中取得了来自表单中的所有数据
Map<String, String[]> map = request.getParameterMap();
//直接将jsp传来的数据转化为dto对象.
user = toDTO(map, User.class);
System.out.println("该对象为:"+user);
}
public void login(HttpServletRequest request,HttpServletResponse response){
System.out.println("登录");
}
public void logout(HttpServletRequest request,HttpServletResponse response){
System.out.println("注销");
}
}
其中的toDTO方法就是运用反射直接将表单提交的数据转换为dto对象. 具体实现如下:
protected <T> T toDTO(Map<String,String[]> map,Class<T> clz){
//获取指定对象的Class对象
// Class<T> clz = T;//Student.class
T newObj = null;
try {
//根据class对象创建当前类型的实例(空对象)
newObj = clz.newInstance();
//获取当前类中包含的所有属性
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
//拼接获取setter/getter方法的名称
String setMethodName = "set"+field.getName().substring(0, 1).toUpperCase()+field.getName().substring(1);
//根据方法名称获取方法对象
Method method_set = clz.getMethod(setMethodName, field.getType());//setter方法
//从map中获取属性值
String value = map.get(field.getName())[0];
//执行新对象的setter方法,将源对象的属性值设置给新对象
method_set.invoke(newObj, value);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return newObj;
}
要说明一下, 这可能不是Servlet最简洁最好的最终优化, 这只是我为了加深理解最近学习的reflect(反射).
博主认为学习应该是一个有过程的进步, 总想一步到位是学不好的.