期末复习这种事,最怕的不是内容多,而是不知道重点在哪里。Java Web 程序设计这门课,考来考去其实就几条主线:Servlet 生命周期和请求处理、JSP 内置对象与作用域、状态管理、JDBC 数据库访问、MVC 分层思想。把这几个模块串成一条完整的链路,考试基本就稳了一大半。下面这份总结是我带过几届学生复习后整理出来的核心知识点,按实战和考试两条线来讲,配合具体代码和易错点,拿去做考前冲刺和查漏补缺都很合适。
1. 期末考试前先搞清楚这门课到底考什么
很多同学复习 Java Web 的时候喜欢按教材目录一页页翻,结果越翻越乱,因为这门课的内容排布是“横向”的,Servlet、JSP、Filter、Listener 各讲各的,但考试真正考的是“纵向”的——一个 HTTP 请求从浏览器发出,到服务器响应回来,中间每一步发生了什么。所以复习的第一步不是背 API,而是先建立一条完整链路:浏览器发送请求,Tomcat 根据 web.xml 里的映射找到对应 Servlet,Servlet 处理请求和业务逻辑,需要数据时通过 JDBC 访问数据库,把结果放回 request/session 作用域,最后由 JSP 渲染成 HTML 返回给浏览器。
再来看考点分布。根据近几年的期末试卷经验,大致比重是这样的:Servlet 部分约占 30% 到 35%,包括生命周期、请求响应对象、路径映射和中文乱码处理;JSP 部分约占 20%,集中在脚本元素、三大指令、九大内置对象和四大作用域;状态管理也就是 Cookie 和 Session 约占 10% 到 15%;JDBC 数据库访问约占 10% 到 15%;剩下的就是 MVC 架构、配置部署这类综合题。选择、填空、简答、程序阅读、手写代码这几种题型里,最拉分的是最后两类,特别是手写一个完整的 Servlet 或者补全 JSP 页面,这类题考察的不是死记硬背,而是对请求处理流程的理解程度。
所以复习策略应该是:先按“请求处理链路”把所有知识点串起来,再把每种技术对应的核心 API 和代码模板练熟,最后专门处理历年高频易错点。按这个顺序来,哪怕复习时间只剩三天,也比漫无目的刷三遍教材有效得多。
1.1 课程主线:从静态页面到动态请求处理
Java Web 说白了就是解决一件事:让服务器根据用户的请求动态生成页面。传统 HTML 页面是死的,所有人看到的都是同一个内容;而 Java Web 通过 Servlet 和 JSP,让页面内容可以根据用户名、权限、表单输入、数据库记录而变化。这个“动态化”的过程,就是这门课的主线,也是所有知识点存在的根本原因。
理解这条主线之后,很多问题就有了解答的前提。为什么需要 web.xml 做映射?因为 Servlet 是 Java 类,浏览器不能直接执行类,必须通过映射路径来触发它。为什么要学 JSP?因为 Servlet 里拼 HTML 字符串太痛苦,JSP 允许直接在 HTML 中嵌入 Java 代码。为什么要用 MVC?因为如果 Servlet 里既写业务逻辑又写 HTML,代码会膨胀到没法维护。所有在期末试卷上出现的题目,本质上都是从这条主线里抽出来的某个环节。
1.2 分值占比与高效复习顺序
我这里给一个比较常见且实用的复习优先级排序,适合考前突击也适合平时总结:
| 优先级 | 知识点模块 | 典型考察方式 | 预估占比 |
|---|---|---|---|
| 高 | Servlet 生命周期与请求处理 | 简答、程序阅读、手写代码 | 30% 左右 |
| 高 | JSP 内置对象与作用域 | 选择、填空、程序阅读 | 20% 左右 |
| 中 | Cookie 与 Session 状态管理 | 简答、阅读题 | 10% 到 15% |
| 中 | JDBC 六步操作流程 | 程序填空、手写步骤 | 10% 到 15% |
| 中高 | MVC 架构与综合应用 | 综合设计题、项目分析 | 15% 左右 |
| 低 | 部署配置、web.xml 细节 | 选择、判断 | 5% 到 10% |
看懂这个表格你就明白了,复习的重心要放在 Servlet 和 JSP 上,这两块吃透了,剩下的是他们的“辅助零件”。部署配置和 web.xml 虽然分值不高,但它是 Servlet 运行的前提,也是很多选择题的陷阱来源,不能完全不看。
2. 从零搭起期末必考的 Web 项目标准目录结构
Java Web 期末考里有一类题特别坑,就是给你一个项目目录截图,问你哪个文件缺失、哪个目录放错了东西,或者手写如何部署一个项目到 Tomcat。这类题分值不高,但属于纯送分题,丢分太可惜。标准目录结构是 Java Web 里最基础也最容易被忽略的知识点。
2.1 标准目录结构为什么非背不可
一个 Java Web 项目的目录结构长这样:
项目根目录(WebRoot 或 webapp) ├── index.jsp // 项目入口页面 ├── static/ // 静态资源,css、js、图片等 ├── WEB-INF │ ├── web.xml // 核心配置文件,如 Servlet 映射、欢迎页面 │ ├── classes/ // 编译后的 .class 字节码文件 │ └── lib/ // 项目依赖的第三方 jar 包这里面最容易出错的就是 WEB-INF 目录的规则。WEB-INF 是服务器的“私有目录”,浏览器不能通过 URL 直接访问其中的文件,只能由服务器内部转发或者代码读取。它相当于一个保险柜,把不该暴露给外部的内容都关在里面。这意味着就算你知道 WEB-INF/secret.jsp 存在,在浏览器地址栏直接输入也只会得到 404 或者被拒绝访问。这个知识点衍生出的选择题几乎每届都出:如果你需要保护某个 JSP 页面不被直接访问,把它放在 WEB-INF 下面就可以了。
classes 目录和 lib 目录的分工也要搞清楚。classes 放的是你自己写的类编译后的字节码,包名要按“包路径一层层建目录”,比如 com.example.controller 包的类,编译后就要放在 classes/com/example/controller/ 下。lib 放的是第三方 jar 包,比如数据库驱动 mysql-connector-java.jar、JSTL 的 jar 包等。这两个目录不能混,很多人部署的时候把驱动 jar 放错位置导致运行找不到驱动,就是这个细节没搞明白。
2.2 部署方式与 JSP 编译产生的中间文件
Tomcat 部署项目的方式主要有三种:第一,直接把项目文件夹复制到 webapps 目录下;第二,把项目打包成 war 文件丢到 webapps 里,Tomcat 会自动解压;第三,在 Tomcat 的 conf/server.xml 里配置虚拟目录,这种方式实际上就是指定一个外部目录作为项目根目录。考试主要考第一种和第二种。用 IntellIDEA 或 Eclipse 开发时,开发工具会帮你完成部署,但你要知道背后其实就是在往 webapps 目录“拷贝文件 + 启动服务器”。
这里有个高频且有趣的细节:当项目已经启动、JSP 页面第一次被访问时,Tomcat 会把它编译成 Servlet 文件,也就是生成一个 Java 类和一个 class 字节码文件。很多同学只知道 JSP 本质是 Servlet,但不知道编译后的文件放在哪里。答案在 Tomcat 的work/Catalina/localhost/项目名/org/apache/jsp/目录下。假如你的 JSP 文件名为index.jsp,编译产物就是index_jsp.java和index_jsp.class。打开这个 Java 文件你会发现,JSP 里的所有内容都被编译成了_jspService(HttpServletRequest request, HttpServletResponse response)方法体的一部分。想确认 JSP 到底被翻译成了什么代码,去 work 目录看源文件是最直观的方式,这个现象在很多人第一次看到时会觉得醍醐灌顶。
3. Servlet 核心机制:生命周期、请求流程与路径映射
Servlet 是整个 Java Web 考试的重中之重,可以说学不懂 Servlet,后面 JSP、MVC、框架全部是空中楼阁。期末考一般会从三个角度考察:生命周期方法调用顺序、请求和响应对象怎么用、路径映射的匹配规则,外加一个从来不缺的中文乱码问题。
3.1 Servlet 生命周期方法与调用时机
Servlet 生命周期是所有 Java Web 简答题里的“钉子户”,必须背熟。整个生命周期有五个阶段:加载和实例化、初始化、服务、销毁、卸载。真正需要记忆的是三个核心方法,它们的调用时机和执行次数是高频考点:
| 方法 | 调用时机 | 调用次数 |
|---|---|---|
| init(ServletConfig config) | Servlet 实例创建后被容器调用,用于初始化资源 | 一次(默认第一次访问时调用) |
| service(ServletRequest req, ServletResponse res) | 每个请求到达时由容器调用,负责分发到 doGet/doPost 等 | 每个请求一次 |
| destroy() | 服务器关闭或项目卸载前调用,释放资源 | 一次 |
这里有个最经典的坑:Servlet 默认是懒加载,也就是容器启动时不创建实例,只有第一次请求到达时才执行构造方法和 init 方法。如果你配置了<load-on-startup>1</load-on-startup>这个标签,Tomcat 就会在启动阶段提前创建实例并执行 init,这个数字代表初始化顺序。考试常出选择题问“Servlet 在服务器启动时会不会自动执行 init?”,默认答案是不会,但加上 load-on-startup 配置后就可以。
service 方法内部会根据 HTTP 请求类型自动调用对应的 doGet 或 doPost 方法。很多人以为在 Servlet 里写业务逻辑要重写 service,实际上大部分场景只需要重写 doGet 和 doPost 就够了。如果两个方法里的逻辑相同,可以在一个方法里调用另一个,或者直接重写 service 方法自己处理请求分发,这两种写法在期末手写题里都算对,关键是逻辑清晰。
3.2 路径映射规则与 404 排查思路
Servlet 的 URL 映射在 web.xml 中通过<url-pattern>配置,常见的写法有三种:精确匹配/hello,路径匹配/hello/*,扩展名匹配*.do。考试常考它们的优先级:精确匹配最高,其次是路径匹配,最后才是扩展名匹配。原因是容器在查找 Servlet 时会遵循“最长匹配优先原则”——先找最具体的路径,找不到再继续放宽规则。举个例子,当请求路径是/hello/index.do时,容器会先看有没有精确匹配/hello/index.do的映射;没有再看有没有/hello/*的路径匹配;最后才考虑扩展名匹配*.do。
如果把项目部署后访问页面报 404,排查顺序一般是:第一,项目有没有正常启动,看控制台报错,看 webapps 下有没有部署成功的文件夹;第二,URL 地址有没有写对,包括项目上下文路径和 Servlet 映射路径;第三,web.xml 中 Servlet 类名和实际类的全限定名是否一致,这是新手最容易犯的大意事故;第四,编译后的 class 文件是否在 classes 目录的正确包路径下。考试做程序阅读题时遇到 404 相关的代码解释,基本都要从映射路径和编译产物这两点给出标准答案。
3.3 request 和 response 对象及乱码问题
HttpServletRequest 对象封装了浏览器发送过来的所有请求信息,最常用的三个方法要记牢:getParameter(String name)获取表单或 URL 参数、setAttribute(String name, Object value)在请求转发时携带数据、getRequestDispatcher(path).forward(req, resp)实现服务器内部转发。HttpServletResponse 是服务器响应给浏览器的结果,核心方法有setContentType(String type)设置响应内容类型和编码、getWriter()返回 PrintWriter 输出文本、sendRedirect(String location)实现重定向。
乱码问题是所有初学者都踩过的坑,也是每届必考的实操题。解决思路要从请求和响应两个方向分别处理。服务器向浏览器输出内容乱码时,要在获取输出流之前加上resp.setContentType("text/html;charset=UTF-8"),这一步同时设置了内容格式和字符编码。接收浏览器提交的中文表单数据时,POST 请求在读取参数前加一句request.setCharacterEncoding("UTF-8")能解决绝大多数场景;GET 请求的乱码根源在 URL 编码,最稳妥的办法是修改 Tomcat 的conf/server.xml中 Connector 节点,加上URIEncoding="UTF-8"属性,或者使用new String(param.getBytes("ISO-8859-1"),"UTF-8")转换编码。期末简答题只需要把这三种手段写出来,并说明分工即可。
4. JSP 语法与九大内置对象
JSP 全称是 Java Server Pages,从本质上讲,JSP 就是一个“披着 HTML 外衣的 Servlet”。这个点需要反复强调,因为很多同学写 JSP 的时候把它当纯 HTML 对待,完全忘了可以嵌入 Java 代码,或者以为 JSP 是独立的技术。弄清楚 JSP 和 Servlet 的关系,考试里很多概念题都能迎刃而解。
4.1 JSP 的本质与三大指令
JSP 页面被第一次访问时会被 Tomcat 翻译成 Java Servlet 源文件,再编译成 class 执行。页面上的一段普通文本,翻译后全部成了out.write()语句;你写的 Java 脚本片段<% %>会被原封不动地放进_jspService方法中;表达式<%= %>则被处理成out.print(...)。理解这个原理之后,再看 JSP 的三大指令就清楚了。
page 指令是最常用的,配置页面属性:<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.util.*" %>。这里 contentType 控制响应编码,pageEncoding 是 JSP 文件本身的保存编码,import 用来导入 Java 类。include 指令实现静态包含:<%@ include file="header.jsp" %>,它在编译阶段就把被包含文件的源码合并进来,本质上就是代码复用。taglib 指令引入标签库,最常见的用法是<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>。选择题经常考 include 指令和<jsp:include>标签的区别,前者是编译时静态合并,后者是运行时动态包含,两者存在本质差异。
4.2 九大内置对象与四种作用域
九大内置对象是 JSP 独有的核心考点,不需要 new 就能直接使用,因为容器在 JSP 翻译后的_jspService方法里已经帮我们创建好了。比较常用的有:request 请求对象、response 响应对象、session 会话对象、application 全局对象、out 输出对象、pageContext 页面上下文对象等。pageContext 对象比较特殊,它是其他隐式对象的“总入口”,能获取 request、session、application 等对象,还可以操作四种作用域中的数据。
四种作用域的优先级从小到大依次是:page、request、session、application。page 只在当前 JSP 页面有效,request 只在一次请求内有效,session 在一个浏览器会话内有效,application 在整个应用范围内有效。存储数据要用setAttribute,取出数据用getAttribute,考试常拿这四个作用域做优先级混淆题,特别要注意“请求转发之后 request 数据还有效,重定向之后 request 数据就没了”这种对比。因为请求转发是同一个请求在服务器内部继续处理,重定向则是服务器告诉浏览器“再重新发一个请求”,相当于浏览器发出了全新的请求字符串。
4.3 EL 表达式与标准标签库 JSTL
EL 表达式用${}来简化输出,比如${user.name}等价于在 JSP 里写<%=request.getAttribute("user").getName()%>,区别是如果 user 为 null,EL 不会报错而是输出空字符串。EL 可以直接访问作用域属性,它会按 page、request、session、application 的顺序依次查找。写试卷上的程序阅读题时,看到${name}第一步就是判断这个 name 存在哪个作用域里,找错作用域就会得出完全相反的答案。
JSTL 的 c:forEach 是迭代集合的利器,考试里比较常见的是配合 MVC 里的 List 数据显示。核心写法:
<c:forEach items="${userList}" var="user" varStatus="vs"> <tr> <td>${vs.count}</td> <td>${user.username}</td> <td>${user.email}</td> </tr> </c:forEach>items 是要遍历的集合,var 是每次循环取出的元素变量,varStatus 记录循环状态。这套写法和 Java 里的 for-each 循环一一对应,理解了映射关系,就算考试时不记得完整标签,也能根据上下文推断出大概含义。
5. 状态管理:Cookie、Session 与登录态保持
HTTP 协议本身是无状态的,同一个浏览器连续访问同一个服务器两次,服务器默认不会认为它们来自同一个用户。为了解决“记住你是谁”的问题,Java Web 提供了两种方案:Cookie 把状态存在浏览器端,Session 把状态存在服务器端。期末考试经常通过“用户登录后刷新页面怎么保持登录状态”这样的场景,来考察这两种机制。
5.1 Cookie 机制与常见陷阱
Cookie 是一小段文本数据,由服务器通过response.addCookie(Cookie cookie)发送给浏览器,浏览器把它保存在本地,下次访问时通过request.getCookies()把它带回来。创建和读取的代码模板要记熟:
// 创建并响应 Cookie Cookie cookie = new Cookie("username", "admin"); cookie.setMaxAge(60 * 60 * 24); // 设置有效期,单位秒,这里是一天 cookie.setPath("/"); response.addCookie(cookie); // 读取所有 Cookie Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie c : cookies) { if ("username".equals(c.getName())) { String value = c.getValue(); } } }Cookie 的常见陷阱有三个。第一,setMaxAge 不设置默认是会话级 Cookie,浏览器关闭就失效;设为 0 是删除 Cookie;设为正数才是持久保存。第二,编码问题,中文 Cookie 值需要先用 URLEncoder 编码,读取时用 URLDecoder 解码。第三,普通 Cookie 存在被篡改和窃取的安全风险,所以不要把密码这类敏感数据直接放 Cookie 里。选择题常考“Cookie 存哪一端”“谁创建谁销毁”,答案分别是浏览器端、服务器创建浏览器销毁。
5.2 Session 机制与常用方法
Session 解决的是“把数据保存在服务器端,给客户端一个唯一标识”的问题。这个唯一标识通常就是 JSESSIONID,服务器创建 Session 时把 JSESSIONID 写入 Cookie 发给浏览器,浏览器下次请求时带上这个 ID,服务器就能从自己的 Session 集合中找出对应的 Session 对象。第一次访问时request.getSession()会创建一个新 Session,之后的访问获取到同一个 Session,这就是登录状态能保持的原理。
Session 的核心方法不多但每个都是考点:request.getSession()获取或创建 Session;session.setAttribute("user", user)把用户对象存进会话;session.getAttribute("user")取出数据;session.invalidate()摧毁当前会话,最常见的用途是“退出登录”;session.setMaxInactiveInterval(30 * 60)设置超时时间,单位是秒。除了代码方式,也可以在 web.xml 里配置全局会话超时时间:
<session-config> <session-timeout>30</session-timeout> </session-config>这里的 30 代表 30 分钟。超过了这个时间没有请求,服务器就会销毁对应的 Session 对象,用户需要重新登录。
5.3 状态管理对比与登录场景应用
把 Cookie 和 Session 放在一起对比,更容易在考试里答高分:
| 维度 | Cookie | Session |
|---|---|---|
| 存储位置 | 浏览器端 | 服务器端 |
| 存储类型 | String 文本 | Object 对象 |
| 大小限制 | 单条约 4KB | 受服务器内存限制 |
| 生命周期 | 通过 MaxAge 控制 | 通过超时时间控制 |
| 安全性 | 较低,可被篡改 | 较高,保存在服务器 |
| 性能 | 不占服务器资源 | 占用服务器内存 |
实际项目里的登录模块一般是两者配合使用:用户成功登录后,把用户对象放进 Session,这样后续每个带 JSESSIONID 的请求都能识别用户身份,同时可以建立一个“记住我”的 Cookie 来记录用户标识,即便 Session 过期,用户下次打开浏览器也可以用 Cookie 里的信息重新找回,甚至自动续期。期末考试遇到这个场景,答题时先说明两种机制各自职责,再给出登录、校验、退出三个环节的代码逻辑,分数就稳了。
6. JDBC 数据库访问六步流程与常见坑
Java Web 的另一个必考点是 JDBC,它是 Java 程序访问关系型数据库的标准接口。考试一般不会让手写特别复杂的数据库操作,但“JDBC 连接数据库六步流程”这种简答题,以及给出一段代码让你填空的题,出现的频率极高。
6.1 JDBC 操作的标准步骤
以 MySQL 为例,一次标准 JDBC 操作需要七步代码,前两步实际上是驱动设置和获取连接:
- 加载并注册数据库驱动:
Class.forName("com.mysql.cj.jdbc.Driver") - 获取数据库连接:
Connection conn = DriverManager.getConnection(url, username, password) - 创建 Statement 或 PreparedStatement 对象
- 执行 SQL 语句得到 ResultSet 结果集
- 遍历 ResultSet,从中读取数据
- 处理完数据后,关闭 ResultSet、Statement、Connection 等资源
第 6 步和第 7 步合并的话就是六步。在代码里通常写成 try-with-resources 或者 finally 块中逆序释放资源。这里最容易被忽略的内存点是关闭顺序:先关 ResultSet,再关 Statement,最后关 Connection,因为 Connection 被关掉后,基于它创建的所有对象都失去意义。
连接数据库的 URL 也要背熟它的基本格式:jdbc:mysql://localhost:3306/数据库名?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai。测验经常把 URL 地址写错一两个参数让考生判断是否能连通,或者考 serverTimezone 配置缺失导致报错的原因。具体策略是,复习时打开数据库驱动包看一下版本对应的驱动类名,旧版是com.mysql.jdbc.Driver,新版是com.mysql.cj.jdbc.Driver,两者弄混会导致 ClassNotFoundException。
6.2 PreparedStatement 防 SQL 注入的原理
在执行 SQL 时,Statement 和 PreparedStatement 二选一,考试基本都会偏向 PreparedStatement。原因是 Statement 采用的是字符串拼接方式,存在 SQL 注入风险。举一个经典例子:登录验证的 SQL 如果这么写:
String sql = "SELECT * FROM user WHERE username='" + username + "' AND password='" + password + "'";用户输入的用户名为' OR '1'='1时,拼出来的 SQL 就变成:
SELECT * FROM user WHERE username='' OR '1'='1' AND password=''因为OR '1'='1'恒为真,整个条件直接失效,等于绕过了密码校验。而如果使用 PreparedStatement,SQL 骨架先被数据库预处理编译,用户输入的部分只作为纯字符串参数传入,不会再改变 SQL 语义:
String sql = "SELECT * FROM user WHERE username=? AND password=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); ResultSet rs = ps.executeQuery();?是占位符,setString 绑定的参数无论输入什么内容,都只会被当作字符串处理,不会破坏 SQL 原始结构。这就是 PreparedStatement 防注入的根本原理,理解这一点,简答题就能答出“预编译 + 参数化查询”两个关键词,比只背结论得分更高。
6.3 数据库连接池的思想
JDBC 的基础流程有一个明显缺陷:每次操作数据库都要创建新连接、用完再关闭,而数据库连接的创建和释放非常消耗资源,高并发场景下很容易拖垮服务器。连接池就是为了解决这个问题出现的技术思想。它的工作方式类似“共享单车”:提前创建一批连接放在池子里,需要时从池中取,用完后归还而不是销毁,池子里的连接可以被不断复用。
常见连接池有 C3P0、Druid、HikariCP,期末阶段不需要掌握具体 API,但必须理解它的核心概念:初始连接数、最大连接数、等待超时时间。后续在实际项目中使用 Spring 框架时,连接池基本都由框架自动管理,但在 Java Web 手写作业里,能在最后把连接池的优势写出来,是一个明显的加分项。答题话术一般为:连接池能够显著降低创建连接的开销,提高资源复用率,增强系统在高并发场景下的稳定性。
7. MVC 设计模式与综合编程题的答题套路
到了大三、大二的大作业阶段,几乎没有哪个项目能离开 MVC,期末考试的综合题也几乎全按 MVC 思想出题。MVC 是 Model-View-Controller 的缩写,它把程序拆成三层:Model 负责数据和业务逻辑,View 负责页面展示,Controller 负责接收请求、调度模型和视图。Java Web 技术栈里,JSP 常充当 View,Servlet 常充当 Controller,而 Model 由 JavaBean 和 DAO 类承担。
7.1 一个完整请求在 MVC 中的处理链路
理解 MVC 最有效的方式,是把一次用户请求从头到尾过一遍。假设有一个用户列表页面,用户访问http://localhost:8080/project/userList:
- Tomcat 根据 web.xml 的映射把请求交给 UserListServlet,Servlet 扮演 Controller。
- UserListServlet 调用 DAO 层 UserDao 的 findAll 方法,执行 SELECT 查询,得到 List 集合,这里发生了 JDBC 交互。
- Servlet 把 List 存进 request 作用域:
request.setAttribute("userList", list)。 - Servlet 调用
request.getRequestDispatcher("/userList.jsp").forward(request, response)转发到 JSP。 - JSP 页面通过 JSTL 的 c:forEach 遍历 userList,渲染成 HTML 表格返回浏览器。
这一步请求处理链路,涵盖了 Servlet、JSP、JDBC、作用域、转发、JSTL 几乎所有的期末考点。把这条链路想清楚,综合题就没有死角。
7.2 手写 MVC 编程题的三个关键步骤
期末手写题一般会给一个场景,比如“实现用户注册功能:JSP 页面提交用户名密码,Servlet 接收参数并验证非空,然后将数据存入数据库”。这类题不要慌,按 PS: I 的三个层级组织答案即可:
第一步,写 JSP 表单页面。核心就是<form action="registerServlet" method="post">,表单里要有对应 name 属性的输入框,method 要写 post 避免参数暴露在 URL。
第二步,写 Servlet 控制器。核心代码是:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); UserDao dao = new UserDao(); boolean flag = dao.insertUser(username, password); if (flag) { response.sendRedirect("success.jsp"); } else { request.setAttribute("msg", "注册失败"); request.getRequestDispatcher("register.jsp").forward(request, response); } }注意逻辑边界:Servlet 只做参数接收、流程调度和结果分发,不直接写 JDBC 代码。数据访问逻辑封装在 UserDao 里,符合职责单一原则。
第三步,写 DAO 类实现数据访问。DAO 类里是标准的 JDBC 操作,包含驱动加载、连接获取、预编译执行、资源关闭等代码。考试阅卷时,只要这三层职责没搞混,每个层次用的 API 正确,基本就能拿全大部分分数。
7.3 两个最常见的 MVC 误区
第一个误区:在 JSP 页面里直接写 JDBC 代码查数据库。不管作业要求是不是“做个简单页面”,一旦 JSP 里出现Class.forName或者Connection,就已经破坏 MVC 结构了。考试简答题问你“MVC 的好处是什么”,说“方便维护”“降低耦合”不够,要答到点子上:View 层只负责展示,不写业务逻辑,Controller 只做流程调度,Model 独立可测试,这样当数据库换成别的东西时,只需改 Model 而不用动页面。
第二个误区:分不清转发和重定向的适用场景。转发是服务器内部行为,URL 不变,request 域数据保留;重定向是浏览器再发起一次新请求,URL 变化,request 域数据丢失。新增操作成功后一般用重定向,避免刷新页面重复提交;查询操作跳转到详情页用转发更合适,可以携带数据。服刑卷子里的程序阅读题很喜欢把这个点作为陷阱,做题时看到 forward 和 sendRedirect 就要条件反射地分析数据还在不在。
8. 高频易错点整理与考前 24 小时速记清单
最后这部分是我多年来批改作业和自己带复习里反复见到错误的集锦。很多题目知识点本身不难,但出题人特别喜欢在细微处设置陷阱,把这些易错点拉通看一遍,比多做一套模拟题都管用。
8.1 我能想到的至少十个高频错误
第一,访问一个不存在的 Servlet 路径报 404,第一反应不是检查番茄,而是检查 web.xml 里<servlet-name>有没有写错。第二,XML 映射里<servlet-class>必须写类的全限定名(包括包名)。第三,JSP 的 page 指令里import属性只能出现在 page 指令上,其他指令不能设置。第四,在 Servlet 里输出中文忘记 setContentType,一定乱码;而在 JSP 页面顶部忘了加 page 指令也会乱码,解决方案是写全 contentType 和 pageEncoding。第五,重定向不能携带 request 作用域数据,只能把参数拼到 URL 后面用www.project/login?msg=error的方式传递。第六,Session 的 invalidate 之后,再调用 setAttribute 会抛 IllegalStateException。第七,Cookie 的 setPath 如果不设置,默认路径是“当前项目”,那么不同项目之间的 Cookie 不能互传。第八,DAO 查询一条记录返回 null 时,JSP 用${user.name}不会报错,但如果调用 Java 方法user.getName()会出现空指针。第九,Class.forName写错驱动类名会报 ClassNotFoundException,但 MySQL 8 以下版本驱动名和 8 以上版本不一致。第十,Tomcat 默认端口 8080,修改后如果不记得,下次访问要用新端口加项目路径。这些内容不一定全部以选择题形式出现,但每一个都可能藏在程序填空题的某个空格里。
8.2 简答题高频考点汇总
我把近几年经常出现的简答题整理成了可速记的问答对:
- Servlet 生命周期各个阶段是什么?调用几次?对应哪些方法?
- 请求转发与重定向的区别有哪些?
- request 和 session 有什么区别?
- JSP 有哪些内置对象?四大作用域分别是什么?
- Cookie 和 Session 有什么区别?各自适用于什么场景?
- 什么是 SQL 注入?PreparedStatement 为什么能防止注入?
- 什么是 MVC?说说 MVC 各层分别负责什么。
- web.xml 里 load-on-startup 的含义是什么?
这些题目背起来不难,但要打高分就一定要结合代码或实际场景举例,避免只写干巴巴的概念。比如回答“请求转发与重定向的区别”,把“地址栏是否变化”“request 数据是否保留”和“适用场景”三点都写上,再配一段使用代码示例,就非常完整了。
8.3 考前一天的实战操作检查清单
考试前如果有一点上机条件,哪怕只有半小时,按顺序把下面这些项目跑一遍,比看书效果好太多:
- 创建一个 Web 项目,配置好 Tomcat,访问首页。
- 写一个最简 Servlet,完成从请求参数到页面输出的一次完整流程。
- 写一个 JSP 页面,使用脚本片段、表达式、EL 表达式输出变量。
- 尝试 Servlet 转发到 JSP 页面,把 request 属性带过去。
- 使用 Session 完成一次“登录 -> 判断登录 -> 退出”的流程。
- 操作一次 JDBC 增删改查,留意驱动加载、连接、关闭三件事。
跑完这几个基础流程,你对 Java Web 的串联理解会比对照书本背十遍强得多。考试遇到综合题时,回忆的不再是零散 API,而是刚才跑通的那条链路,心里就会踏实很多。
最后分享一个我自己常用的复习小习惯:考前几天把容易混淆的两种技术放在同一张表里对比记忆,比如“转发 vs 重定向”“Cookie vs Session”“Statement vs PreparedStatement”“静态包含 vs 动态包含”,对比是记忆的大杀器。把这份总结里的表格和代码在草稿纸上各抄写一遍,考试时很多答案会自然而然地浮现在脑子里。祝期末顺利。