五周软件工程实习记录拆解:项目骨架与避坑清单 简介这份资源是一份本科软件工程实习记录文档面向需要撰写实习报告、实习日志的在校生以及刚进入软件开发岗位的初学者可用于参考格式、内容组织与知识点梳理也可直接在其基础上修改。压缩包内共1个doc文件约40KB按周次记录实习过程内容从公司动员、分组分工到具体功能开发依次展开。文档结合具体日期记录了文件查找与排序功能实现、Swing界面设计、HTML与CSS页面布局、JavaScript动态交互、Node.js项目搭建、Express请求响应、MySQL数据导入与增删改查等实践环节并穿插说明软件开发六大流程、项目经理职责、软件开发员与架构师等岗位分工以及前后端技术栈使记录不止于流水账。目前已有2924人学习下载适合作为毕业实习材料模板与开发流程入门参考使用。1. 从一份五周实习记录里能拆出多少可复用的东西很多人拿到「软件工程实习记录」这类文档第一反应是当成交作业的素材扫两眼就丢进回收站。我最初也这么干过直到自己带新人时被一个问题问住一个只会写单文件 CRUD 的学生进项目组第一个月到底会卡在哪翻回这份五周的记录答案其实写得很清楚——卡在环境、编码、端口、数据库连接这些「不是业务逻辑」的地方。这份资源是一份本科软件工程实习的逐日记录从 Eclipse 里用 Swing 画查找面板到 Express 接 MySQL 做增删改查再到 Redhat 下交叉编译、IIS 权限、Tomcat 部署五周时间把一条从桌面端到 Web 端、从开发到发布的链路走了一遍。它最适合两类人正在准备实习记录、需要一份有真实技术细节的参照模板的同学以及刚入行、想看看「一个功能从需求到上线到底要过几道坎」的初级开发。2. 技术栈与目录结构五周任务的模块化还原这份记录的价值不在文笔在于它无意中保留了一条完整的工程演进路径。第一周在 Eclipse 里做 Swing 桌面程序第二周转到 Node.js Express MySQL 的 Web 单页第三周补 Linux 与服务器运维第四、五周用 JSP Tomcat 做「网上书城」这类经典 JavaWeb 项目。三个阶段的技术栈差异很大但如果按模块重新组织会发现它其实是一份很标准的「从单体桌面应用到三层 Web 应用」的迁移样本。我在复现这份记录时习惯把它拆成四个可独立运行的模块目录每个目录对应记录里的一段任务这样无论是要写实习报告还是真的要跑代码都能一处一处对上。2.1 四个模块与技术选型对照记录里出现的工具有 Eclipse、Swing、Node.js、Express、MySQL、JSP、Tomcat、MyEclipse、Redhat Linux 9.0、IIS、SQL Server 2000。这些工具放在今天看有些年头但选型逻辑依然成立桌面端用 Swing 是因为要快速做出带输入框和按钮的本地面板Web 端用 Express 是因为它把路由和请求处理压到十几行代码就能跑通数据库选 MySQL 是因为安装门槛低、和 Node 的驱动生态成熟最后部署用 Tomcat 是因为 JSP 项目天然跑在 Servlet 容器里。我在还原时把目录按下面的方式组织每个模块都能单独启动互不干扰。software-practice/ ├── 01-swing-finder/ # 第一周文件查找与排序Swing 桌面 │ └── src/ │ ├── com/shixi/viwe/ # 视图层记录里包名拼作 viwe保留原样 │ └── com/shixi/main/ # 主程序入口 ├── 02-express-crud/ # 第二周Express MySQL 单页增删改查 │ ├── app.js │ ├── routes/ │ ├── public/ │ └── package.json ├── 03-linux-hello/ # 第三周Linux 下的编译与 Makefile │ ├── hello.c │ └── Makefile └── 04-jsp-bookstore/ # 第四、五周JSP 网上书城 ├── src/com/shop/control/ # 控制层 └── WebContent/ # JSP 页面与 web.xml这个结构不是记录里原样给出的而是我按任务边界重新划分的结果。记录中第一周和第四周都提到「新建 com.shixi.viwe 包」包名拼写有误我在还原时保留了原样因为这类细节恰恰是实习记录的真实感来源改成 view 反而丢掉了现场痕迹。每个模块对应一周左右的工时读者可以按需取用只想补实习记录的同学直接看每个模块的业务描述和技术点想真的跑起来的同学从 02 和 04 入手这两个模块的依赖最少、反馈最快。2.2 模块之间的依赖关系与复用边界四个模块表面独立实际有三处隐性依赖值得先说清楚。第一处是数据库02 模块用 MySQL 存学生信息04 模块用 SQL Server 2000 存图书信息记录里第三周还提到「jar 包和数据库版本不匹配导致 500 错误」说明这两套数据源在实习环境里是并存的还原时不要图省事统一成一个库否则复现不出那个经典的连接失败场景。第二处是字符集04 模块反复出现中文乱码从录入到回显都有根因是页面编码和数据库编码不一致这在 02 模块里同样存在只是记录里没写出来。第三处是端口Tomcat 默认 8080 被占用后改成 89这个细节在本地多服务并行时非常常见。复用边界也要提前划好。01 模块的查找与排序逻辑是纯 Java SE不依赖任何 Web 容器可以单独抽成一个工具类给别的项目用02 模块的 Express 路由写法可以迁移到任何 Node 项目但它的静态数组回显方式只是过渡态别照抄进生产代码03 模块的 Makefile 是交叉编译的最小示例换开发板时只需要改编译器前缀04 模块的 control 层结构是典型的「一个功能一个类」适合教学不适合中大型项目。把这条边界想明白后面读代码时就不会把教学代码当成工程范本。3. 核心模块拆解从文件查找到数据库增删改查的完整链路这一段是整份记录里技术含量最密集的部分。第一周的 Swing 查找和排序第二周的 Express 接口与 MySQL 读写第四周的 JSP 控制层三处代码覆盖了「本地程序 → 前后端通信 → 服务端渲染」三种形态。我在复现时把每处的关键代码都摘了出来并在每段后面写清楚它在整个链路里承担什么角色、哪些参数是需要按环境改的、跑不通时先看哪里。需要提醒的是记录里给出的代码是片段式的下面这些是我按它的描述补全后的可运行版本业务字段和表结构以记录里提到的编号、姓名、课程名、图书信息为准。3.1 Swing 文件查找模糊匹配与结果回显记录里 8 月 28 日的任务是在输入框输入文件名或商品名支持模糊查找并把结果回显到页面。实现上分视图和主程序两个包视图负责面板布局主程序负责扫描目录。模糊匹配我用的是文件名包含判断没上正则因为实习场景下用户输入的就是片段包含判断足够且不会因为特殊字符炸掉。// com/shixi/main/FileFinder.java public class FileFinder { // root 是查找根目录keyword 是用户输入的关键词 public ListFile search(String root, String keyword) { ListFile result new ArrayList(); File dir new File(root); if (!dir.exists() || !dir.isDirectory()) { return result; // 目录不存在直接返回空避免 NPE } File[] files dir.listFiles(); if (files null) return result; for (File f : files) { if (f.isDirectory()) { // 递归子目录这是记录里只能从固定地址查找被老师修正的关键点 result.addAll(search(f.getAbsolutePath(), keyword)); } else if (f.getName().contains(keyword)) { result.add(f); } } return result; } }这段代码的核心是递归。记录里 8 月 29 日明确写了「我做出的查找功能只能从固定的地址进行查找」老师修改后才支持任意目录改法就是把根目录从硬编码变成方法参数并在遍历时对子目录递归调用。参数root建议由视图层的目录选择器传入不要写死成D:/data这类路径否则换台机器就跑不了。keyword为空时应当在校验层拦掉否则contains()恒为 true会把整个磁盘的文件都列出来这是新手最容易翻车的一处。结果回显交给 Swing 的JList或JTable用DefaultListModel装File对象重写toString只显示文件名避免路径过长撑爆面板。3.2 冒泡排序与排序字段的取舍8 月 30 日的任务是按文件名排序记录里指定用冒泡排序。教学场景下用冒泡没问题但有两个参数要先定清楚升序还是降序、比较时是否忽略大小写。我按文件名升序、忽略大小写来实现比较用compareToIgnoreCase这样Report.txt和report.txt会排在一起符合用户直觉。// 对查找结果按文件名排序list 来自上一节的 search 结果 public void sortByName(ListFile list) { int n list.size(); for (int i 0; i n - 1; i) { boolean swapped false; for (int j 0; j n - 1 - i; j) { String a list.get(j).getName(); String b list.get(j 1).getName(); if (a.compareToIgnoreCase(b) 0) { Collections.swap(list, j, j 1); swapped true; } } if (!swapped) break; // 提前退出已经有序就不用再扫 } }swapped标志位是冒泡排序里最容易被省略又最有用的优化某一轮没有发生交换说明剩余部分已经有序直接跳出。时间复杂度仍是 O(n²)文件量上万时会明显卡顿实际项目里我会换成list.sort(Comparator.comparing(File::getName, String.CASE_INSENSITIVE_ORDER))但写实习记录时保留冒泡反而更贴合当时的水平。排序必须作用在查找结果上而不是全盘文件顺序是先查后排这一点在记录里没有明说但页面逻辑决定了它只能是这个顺序。3.3 Express 接口从静态数组到 MySQL 查询9 月 5 日的任务是实现页面数据的响应与请求先用静态数组回显学生信息9 月 6 日再接 MySQL。我在还原时把这个演进过程保留成两个路由方便对照。第一个路由直接返回内存数组第二个路由查库接口路径保持一致前端只需要改请求地址就能切换。// app.js const express require(express); const mysql require(mysql); const app express(); const pool mysql.createPool({ host: 127.0.0.1, user: root, password: your_password, // 按本地环境改 database: practice, charset: utf8mb4, // 中文乱码的第一道防线 connectionLimit: 10 }); // 过渡态静态数组回显对应记录里 9 月 5 日的做法 app.get(/api/students/static, (req, res) { res.json([ { id: 1, name: 张三, course: 软件工程 }, { id: 2, name: 李四, course: 数据库原理 } ]); }); // 正式态查库回显对应 9 月 6 日之后的实现 app.get(/api/students, (req, res) { pool.query(SELECT id, name, course FROM student ORDER BY id, (err, rows) { if (err) { console.error(查询失败, err); return res.status(500).json({ error: 数据加载失败 }); } res.json(rows); // mysql 驱动返回的就是对象数组可直接序列化 }); }); app.listen(3000, () console.log(listening on 3000));charset: utf8mb4这一行是整个文件里最关键的位置。记录里 9 月 21 日写「中文信息不能成功录入数据库」解决方式就是在连接代码处限定字符集这里的utf8mb4比utf8更彻底能兼容四字节字符。createPool而不是createConnection是因为单页应用会频繁请求连接池能避免反复握手。err一定要返回 500 并打日志记录里 9 月 13 日的 500 错误就是「数据加载失败」被吞掉后排查了半天日志写清楚能省很多事。前端用fetch(/api/students)拿到数组后渲染表格这一步和 9 月 7 日的增删改查按钮挂钩每个按钮对应一个POST或DELETE路由SQL 语句在服务端拼装不要把 SQL 写进前端 JS。3.4 JSP 控制层一个功能一个类的组织方式9 月 25 日的任务是图书商城的删除和编辑修改做法是先建两个 JSP 页面和对应的 control 类首页上加按钮、设超链接跳转。这种「一个功能一个类」的结构很朴素但把请求分发和业务处理分开了。下面是删除功能的控制类写法。// com/shop/control/BookDeleteServlet.java public class BookDeleteServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String idStr req.getParameter(id); if (idStr null || !idStr.matches(\\d)) { resp.sendRedirect(list.jsp?msg参数错误); return; } int id Integer.parseInt(idStr); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement( DELETE FROM book WHERE id ?)) { ps.setInt(1, id); ps.executeUpdate(); } catch (SQLException e) { log(删除图书失败 id id, e); resp.sendRedirect(list.jsp?msg删除失败); return; } resp.sendRedirect(list.jsp); // 重定向回列表页刷新即生效 } }PreparedStatement加占位符是必须的记录里没提防注入但用字符串拼 SQL 在删除场景下极其危险。id做了数字校验避免id1 or 11这类参数直接把整表删掉。删除后重定向而不是转发是为了避免用户刷新页面时重复提交这是「重定向后 GET」的常规做法。记录里 9 月 26 日提到用 JS 函数打开新窗口做编辑比超链接体验好原因也在这里编辑完关窗刷新列表不需要整页跳转。这里有一份踩坑血泪史值得提前说JSP 页面的pageEncoding、响应的contentType、数据库的连接字符集三处编码必须统一任意一处不一致回显时就是一片问号。4. 把项目跑起来三个环境的启动顺序与验证点这份记录横跨桌面、Web、服务器三种环境直接一把梭很容易在某个环节卡死又不知道卡在哪。我建议按「先本地 Web、再桌面、最后服务器」的顺序推进每一步都有明确的验证点通过了再往下走。下面给出的命令和配置都是最小可运行版本路径和端口按自己机器改记录里提到的软件版本较老新版本安装时选项名称可能略有不同但逻辑一致。4.1 Node MySQL 模块的启动与连通性检查先装 Node 和 MySQL然后在02-express-crud目录下初始化项目。记录里 9 月 4 日提到用npm init建项目这里补上依赖安装和建表语句。cd 02-express-crud npm init -y # 跳过交互生成 package.json npm install express mysql # 记录里用的就是这两个包 mysql -u root -p schema.sql # 先建库建表再启动服务 node app.js # 看到 listening on 3000 即启动成功建表语句要跟着写否则查库接口会直接 500。CREATE DATABASE practice DEFAULT CHARACTER SET utf8mb4; USE practice; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, course VARCHAR(100) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; INSERT INTO student (name, course) VALUES (张三, 软件工程), (李四, 数据库原理);验证分三步浏览器访问http://localhost:3000/api/students/static能看到两条 JSON说明路由通访问/api/students看到同样的两条说明查库通往表里插一条中文数据再访问能看到中文而不是问号说明字符集通。三步任何一步失败先看 Node 控制台的报错再看 MySQL 的SHOW VARIABLES LIKE character%不要急着改代码。记录里 9 月 20 日数据库装不上原因是安装时没选「开发者」选项这类问题看安装向导的选项说明就能避开。4.2 JSP 项目在 Tomcat 下的部署与端口调整04-jsp-bookstore是标准的 JavaWeb 项目需要 JDK、Tomcat 和 MyEclipse 或 IDEA。记录里 9 月 27 日用 MyEclipse 关联 Tomcat9 月 28 日把项目移到webapps目录下启动结果端口被占用。步骤我按现代工具重写逻辑不变。# 1. 编译打包产物是一个 war 或整个目录 # 2. 部署到 Tomcat 的 webapps 下 cp -r 04-jsp-bookstore $TOMCAT_HOME/webapps/bookstore # 3. 改端口记录里最后用的是 89 # 编辑 $TOMCAT_HOME/conf/server.xml找到 Connector 标签 # Connector port89 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 /验证点按顺序来启动startup.sh或startup.bat后控制台最后一行应出现Server startup in xxx ms浏览器访问http://localhost:89/bookstore/index.jsp能出页面进列表页能看到数据库里的图书点删除按钮能删掉并回到列表。端口被占用的排查命令是netstat -ano | findstr :8080Windows或lsof -i:8080Linux找到 PID 后决定是杀掉还是换端口。记录里直接换成 89 就能跑说明冲突源是另一个服务不是自身配置错误这个判断顺序很重要。4.3 Linux 环境下的编译与开发板验证第三周的03-linux-hello是交叉编译的最小闭环。记录里提到 Redhat Linux 9.0、ARM 开发库、Makefile、下载到开发板运行还原时关键是三样东西交叉编译器前缀、目标架构、烧录方式。# Makefile换开发板时只需要改 CC 这一行 CC arm-linux-gcc TARGET hello OBJS hello.o $(TARGET): $(OBJS) $(CC) -o $(TARGET) $(OBJS) hello.o: hello.c $(CC) -c hello.c clean: rm -f $(TARGET) $(OBJS)make之后用file hello检查产物架构输出里应出现ARM字样如果显示x86-64说明CC没生效多半是环境变量里的gcc优先级更高。把产物通过串口或网络传到开发板执行能看到hello world就说明工具链通了。这个环节最容易翻车的是开发板挂载和权限不是编译本身所以先验证编译产物、再验证传输通道、最后验证运行一层层排除。5. 避坑与排查五周里反复出现的那几类问题这份记录最值钱的地方是它把当年踩过的坑一条条写进了日期里。我把这些坑归成五类每条按「现象、原因、解决」写清楚因为这些问题在今天的新项目里一模一样会复现年份变了坑没变。读这部分时建议对照前面章节的代码看很多报错的根因就藏在那几行参数里。5.1 中文乱码编码三处必须对齐现象是页面回显中文变成问号或方块或者录入成功但查出来是乱码。原因是三处编码各自为政JSP 的pageEncoding、响应的contentType、数据库连接的字符集。记录里 9 月 21 日和 9 月 26 日两次遇到第一次在连接代码加限定字符集解决第二次把数据库和页面都改成 gbk 解决。我的做法是统一成 utf8mb4JSP 顶部写% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 %连接串带charsetutf8mb4建库建表也指定 utf8mb4三处一致就不会再出现。注意 gbk 能解决部分场景但遇到生僻字和 emoji 会再次翻车新项目别用它。5.2 数据库连接失败版本与驱动不匹配现象是 JavaWeb 项目启动后页面 500日志里写「数据加载失败」改 Tomcat 配置怎么改都没用。原因是 JDBC 驱动 jar 包版本和数据库版本不匹配记录里 9 月 13 日就是这么查出来的。解决方式是把驱动换成与数据库大版本对应的版本MySQL 5.x 用 5.1.x 驱动MySQL 8.x 用 8.x 驱动连接 URL 也要跟着加时区和 SSL 参数。排查顺序是先确认数据库本身能连上再确认驱动 jar 在WEB-INF/lib下且没有重复版本最后才怀疑代码。很多人一上来就改配置文件方向就错了。5.3 端口占用启动失败先看日志现象是 Tomcat 或 Node 启动时报Address already in use或者启动看起来成功但访问不通。原因是端口被别的进程占了记录里 9 月 28 日就是 8080 被占换成 89 后正常。解决方式是用netstat或lsof找到占用进程能杀就杀不能杀就换端口换端口后记得同步改前端请求地址和反向代理配置否则前端还在打旧端口。这里有个玄学现象明明改了口浏览器还是访问旧地址多半是缓存或者上一次的进程没退干净ps里搜一下同名进程确认只有一个在跑。5.4 IIS 本地站点无法访问权限与磁盘格式现象是在 IIS 里配好默认网站主目录浏览器输入地址却打不开。原因是本地虚拟访问需要给 Internet 来宾账户配读取权限记录里 9 月 12 日就是查资料后发现要设来宾账户权限且只有 NTFS 格式的磁盘才能在资源管理器里看到安全选项卡。解决方式是确认磁盘是 NTFS给IUSR账户加读取权限需要后台管理的目录再加修改权限。这条坑在今天用 Nginx 或 Tomcat 的人可能遇不到但只要还在 Windows 上做本地站点逻辑是一样的先看服务是否在跑再看目录权限最后看防火墙。5.5 重复提交与重复显示先删后加不是最优解现象是点添加按钮后同一条数据在页面上出现两次刷新后更多。原因有两种前端每次添加都往数组里 push 而没有清空或者表单重复提交导致后端插了两条。记录里 9 月 3 日的做法是「添加信息前先删除原来的信息」这能解决显示重复但治标不治本数据其实已经重复入库了。正确做法是前端每次先清空容器再渲染后端在表单提交后重定向或者给表单加一次性 token 防重。排查时打开浏览器网络面板看请求次数一次请求说明是渲染问题两次说明是提交问题分清楚了再改。6. 二次开发把这份记录改成能用的项目骨架这份记录本质上是一份过程文档不是成品代码库所以「二次开发」的重点不是加功能而是把它变成一套能反复使用的脚手架。我做过一次改造把四个模块抽成三个可复用的基础件一个 Swing 的文件检索工具类、一个 Express MySQL 的 CRUD 模板、一个 JSP 控制层的基类。改造过程中最值得说的是参数化原始记录里大量路径、端口、账号是硬编码的抽出来之后换环境只需要改一个配置文件。比如数据库连接那段我把它挪到config/db.js用环境变量覆盖默认值。// config/db.js module.exports { host: process.env.DB_HOST || 127.0.0.1, user: process.env.DB_USER || root, password: process.env.DB_PASS || , database: process.env.DB_NAME || practice, charset: utf8mb4, connectionLimit: 10 };这样本地开发、测试环境、演示环境可以共用一套代码只换.env。Swing 那边同理把查找根目录和排序字段做成构造参数视图层通过下拉框选择。JSP 的 control 层可以抽一个BaseServlet处理公共的编码设置、参数校验和异常跳转每个具体功能只写 SQL 和业务判断代码量能砍掉一半。验证改造是否成功的方法很直接把项目拷到另一台机器上不改任何一行代码只配环境变量能跑通就算过。我一般会额外检查三个点中文能不能正常存取、删除操作有没有防注入、端口冲突时日志里能不能一眼看出原因。还有一个技巧值得单独说给每个模块加一个README.md把启动命令、依赖版本、已知问题写进去尤其是「数据库安装时要选开发者选项」这种一句话能省半天时间的提示。原始的实习记录是按日期流水写的信息密度高但检索困难按模块重组后再配索引它的价值会从「交作业的材料」变成「新人上手手册」。从那以后我每次整理实践记录都强制走一遍「按模块拆目录、按环境抽配置、按坑位写排查」这三步不然过两个月自己都看不懂当时为什么那么改。希望这份拆解能帮到你把手里那份看起来只是流水账的记录变成真正能跑起来、能改得动的东西。本文还有配套的精品资源点击获取