简介:这份面向软件工程专业学生与毕业实习材料整理者的实习记录文档,围绕本科阶段企业实习的完整过程展开,可作为撰写实习周记、实习报告和毕业实践材料的参考。文档以周为单位记录真实开发任务,涵盖可行性分析、需求分析、概要设计、详细设计、编码与项目验收等软件流程,也涉及项目经理职责、开发岗位分工和技术栈选择。具体内容包括Eclipse与Swing实现文件查找、排序,HTML、CSS、JavaScript完成教师信息单页,Node.js与Express搭建请求响应,MySQL数据导入及增删改查,以及Redhat Linux与ARM开发环境配置等实践环节,能帮助读者对照整理实习过程、补充技术细节和梳理岗位认知。压缩包共1个doc文件,约40KB,篇幅精炼便于修改复用;目前已有2924人学习,适合需要快速完成实习记录撰写与内容完善的高校学生参考。
1. 一份实习记录真正有价值的地方,不在那六步流程里
2018 年 8 月底到 9 月底,五周时间,我从一个只会写控制台打印语句的学生,变成能把一个带数据库的页面在自己机器上跑起来的人。手上这份软件工程实习记录,一开始我以为它只是交毕业材料用的,毕竟流程那六步——可行性分析、需求分析、概要设计、详细设计、编码、项目验收——网上抄一遍就能交差。但翻回去看,真正让我后来少走弯路的,恰恰是记录里那些“当时卡了大半天、事后一句话就解决”的细节:查找到的路径写死、添加信息重复回显、数据库安装漏选一个选项、Tomcat 报 500 其实是 jar 包版本不匹配。这篇不背流程定义,讲这五周我实际做了什么、在哪里踩坑、现在回头看该怎么判断。
2. 流程认知与岗位地图:第一天那句“编码只占 15%”是怎么被验证的
实习第一天的动员会上,带队经理讲完公司主营的网络软件开发和技术咨询业务,让我们轮流做自我介绍,然后甩出一句话:软件开发不是写代码,代码实现大概只占整个过程的百分之十五。当时我不信,觉得这不过是给新人打的预防针——不写代码那还能叫开发吗。五周之后我信了,而且能拿出自己身上的账来对。
2.1 六个阶段不是背诵清单,是六次责任交接
可行性分析、需求分析、概要设计、详细设计、编码、项目验收,这六步我在学校背过无数遍,考试默写从来不丢分。但真坐到工位上,才发现每一步的价值不在名字,在于它交付了什么、交给谁、下一个人能不能接着干。
我一般会这么判断一个阶段有没有真做完:看有没有留下别人能接着用的东西。可行性分析做完,留下的应该是“这个方向做不做得成、卡点在哪”的结论;需求分析做完,留下的应该是一份能指着说“就这些功能,多一个不做”的清单;概要设计做完,留下的应该是模块怎么切、谁负责哪块;详细设计做完,留下的应该是类怎么分、接口怎么定;编码做完,留下的应该是能跑的东西;项目验收做完,留下的应该是客户点头的确认。
实习里我印象最深的是分组那天。公司按我们各自的方向和意愿分组,指导老师辅助我们做开发,写代码过程中有不懂的可以直接问。当时我以为这一步就是“分活儿”,现在明白过来,这就是概要设计在真实项目里的样子——先把边界划清楚,再往下走。跳过这一步直接开写,最常见的结果是两个人写出了同一份工具类,或者接口对不上,改起来比重写还费劲。
| 阶段 | 实习中对应的具体动作 | 我用来判断“做完没”的标准 |
|---|---|---|
| 可行性分析 | 第一天经理介绍公司业务与过往项目 | 能不能说清这个方向的技术与人力卡点 |
| 需求分析 | 老师反复问“你要做成什么样” | 功能清单能不能一句话说清,边界明确 |
| 概要设计 | 按方向意愿分组、划分模块 | 每个人知道自己负责哪块、接口在哪 |
| 详细设计 | 定包结构 view / main 的分层 | 类名和方法职责能不能对上 |
| 编码 | 写 Swing 面板、写页面、连数据库 | 能跑通,且别人照着说明能跑起来 |
| 项目验收 | 老师当场跑一遍、指出问题 | 提出的问题改完且不再复现 |
2.2 那 15% 到底花在哪了:我五周的工时分布
第二周开始我记了个粗糙的账,每天下班前用两分钟写一句“今天时间花在哪”。五周下来大致是这个比例:真正敲代码的时间不到三成,剩下七成里,环境配置和依赖安装占一大块,查资料和读别人文档占一块,改 bug 和反复试错占最大一块。这跟第一天听到的“百分之十五”未必精确对得上,但量级是对的。
具体拆开看:第一周装 Eclipse、熟悉 Swing 插件、调面板布局;第二周装 Node.js、查 npm init 的用法、花半天读 Express 框架文档;第三周熟悉 Linux 环境、写 hello.c 和 Makefile、装 IIS 组件;第四周搭项目模型、装 MySQL 反复失败;第五周部署到 Tomcat、改端口。
这些事没有一件属于“写业务代码”,但少做一件项目就跑不起来。所以后来我带新人,第一句提醒都是先把环境跑通再做功能,别憋着一股劲写完了才发现跑不起来,那时候排查成本翻倍。
提示:记账不用精确到分钟,每天一句话足够。两周之后回看,你会很清楚自己卡在哪一类事情上,这类事情往往就是最该补的短板。
2.3 岗位地图:每个角色在流程的哪一段出力
和老师来回沟通的过程中,我第一次听全了 IT 行业的主要职位:软件开发员、项目经理、技术总监、架构师、总经理、售前售后。当时只是记下来,现在回头看,这张地图最大的用处不是让你挑一个头衔,而是让你知道自己此刻站在流程的哪一段。
软件开发员站在编码和详细设计这一段,产出是可运行的模块;项目经理站在需求和验收之间,负责计划、组织、协调、控制,还要有沟通能力,因为客户改需求的时候是他在中间扛;架构师站在概要设计那一段,决定模块怎么切、技术栈怎么选;技术总监往上看的是技术方向和团队能力;售前售后站在项目边界之外,一个负责把需求谈进来,一个负责把问题接住。
我当时做的“网上书城”信息录入功能,扮演的其实是项目经理加开发员两个角色:先和大家一起探讨系统要实现哪些功能,再自己编码实现。这个体验很值钱,因为它让你同时感受到“需求没定清楚会怎样”和“代码写歪了会怎样”。等你真正进了团队只负责其中一段,反而很难再有机会同时看到两头。
3. Swing 桌面小工具:文件查找与排序的两处真实卡点
第二周的任务是用 Java 做一个桌面端的小工具,两个功能:文件查找和文件排序。功能描述一句话就能说完,做起来却连着两天卡在同一个地方。这一段我写得细一点,因为当时踩的坑非常典型,几乎每个人都容易遇到:代码逻辑没错,错在数据从哪来。
3.1 视图与逻辑分层:com.shixi.view 和 com.shixi.main 为什么要分开
打开 Eclipse,新建 com.shixi.view 包,用 Swing 插件拖出查找面板:一个输入框、一个按钮、一块结果显示区,设置好各组件功能和相对位置。然后另建 com.shixi.main 包,按运行逻辑写主程序。
这两步分开看着像形式主义,其实是整个实习里最早教给我的分层意识。view 包里只放“长什么样、点了按钮触发什么”,main 包里只放“数据怎么来、怎么处理、怎么返回”。好处在第三天立刻显现:查找功能出问题时,我不需要在一堆布局代码里翻逻辑,直接进 main 包看数据来源就够了。
常见的误用是把业务逻辑直接写在按钮的监听器里,一个 actionPerformed 方法写两百行,布局、IO、排序全塞在一起。这种代码第一次能跑,第二次改需求就废了。我当时的做法是按钮只做一件事:从输入框取值,调 service 层方法,把返回结果塞回表格。
3.2 查找只能查固定目录:问题出在入口而不是算法
功能做出来能跑,但只能从写死的那个地址查。我拿着这个问题去问老师,改完之后才明白,问题根本不在查找算法上,而在“搜索起点”这个参数被硬编码了。把写死的路径换成方法入参,调用方传什么就从哪查,功能立刻就通了。
// com.shixi.main.FileSearchService // 关键改动:rootDir 作为入参传入,不再在方法内部写死某个盘符路径 public List<File> search(File rootDir, String keyword) { List<File> hits = new ArrayList<>(); // 用队列做广度遍历,避免深目录递归把调用栈打爆 Deque<File> queue = new ArrayDeque<>(); queue.offer(rootDir); while (!queue.isEmpty()) { File current = queue.poll(); File[] children = current.listFiles(); // 无权限目录 listFiles 会返回 null,跳过而不是抛异常中断整个查找 if (children == null) { continue; } for (File child : children) { if (child.isDirectory()) { queue.offer(child); } else if (child.getName().toLowerCase().contains(keyword.toLowerCase())) { // 模糊查找用 contains 而不是 equals,这是需求里明确要求的 hits.add(child); } } } return hits; }这段代码有三个地方值得说清楚。第一,rootDir 做成入参是整个功能从“能用”到“好用”的分水岭,需求说支持模糊查找,但没说只能查一个目录,硬编码等于偷偷砍掉了需求。第二,listFiles() 在无权限目录上返回 null 而不是空数组,不加判断会直接空指针,表现是“查到一半突然不动了”,非常难查。第三,用队列广度遍历而不是递归,是因为递归在系统盘这种深目录下容易栈溢出,广度遍历只占堆内存,稳得多。
参数怎么调:keyword 传空字符串等于列出全部文件,这个行为要在调用方拦住,否则用户点一下按钮就是全盘扫描,界面直接卡死。真实的桌面工具都会加一层后缀过滤和数量上限,我这里没加是因为数据量小,但心里要有这个数。
3.3 排序:冒泡在这种场景下够用,但要知道边界在哪
第四天的任务是给磁盘里现存的文件排序,按文件名排,算法用冒泡排序,同时支持模糊查找,结果回显到页面上。
// 冒泡排序:只比较文件名字符串,千级数据量以内完全够用 public void bubbleSort(List<File> files) { int n = files.size(); for (int i = 0; i < n - 1; i++) { boolean swapped = false; for (int j = 0; j < n - 1 - i; j++) { // compareToIgnoreCase 忽略大小写,避免大写文件名全排到前面 if (files.get(j).getName().compareToIgnoreCase(files.get(j + 1).getName()) > 0) { Collections.swap(files, j, j + 1); swapped = true; } } // 一轮下来没有交换说明已经有序,提前退出,最好情况 O(n) if (!swapped) { break; } } }为什么用 compareToIgnoreCase 而不是直接比字符串:文件系统里 A 和 a 经常混着出现,直接比较会得到一整段大写在前、小写在后这种看着像乱序的结果,用户第一反应是“排序坏了”。
冒泡的复杂度是 O(n²),一千个文件大概百万次比较,在桌面端毫无感知;十万个文件就要几秒,界面会明显卡住。所以这段代码的适用边界很清楚:本地目录、数量在千级以内、不需要实时刷新。真到了万级以上,正确做法是换 Collections.sort 配 Comparator,或者干脆把排序下推到数据库的 order by。当时用冒泡是因为教学任务要求,但知道边界在哪,比会写这个算法重要得多。
4. 从静态数组到 MySQL:一个单页的四步推进
第二周到第三周的主线是 Web:先做静态页面,再加交互,再接后端,最后连数据库。这条路线现在看非常标准,但每一步都埋了一个坑,而且坑都在“数据从哪来”这个问题上。
4.1 静态数组版:添加一次就重复一次
先用 HTML 标签搭布局,CSS 管样式,JavaScript 管动态效果,把教师信息(编号、姓名、课程名)存在数组里,用表格渲染到页面上。这一步跑通不难,难的是下一步:加三个 input 标签读用户输入,点添加按钮把新数据推进数组再重新渲染。
做完发现添加的信息会重复显示。原因很直白:每次添加都是往原表格后面追加行,而不是先清空再整体重绘。改成渲染前先清空容器,问题立刻消失。
// 每次渲染前先清空 tbody,否则旧行会和新行叠在一起 function render(list) { const tbody = document.querySelector('#teacherTable tbody'); tbody.innerHTML = ''; // 这一行是修重复显示的关键 list.forEach(function (t) { const tr = document.createElement('tr'); tr.innerHTML = '<td>' + t.id + '</td><td>' + t.name + '</td><td>' + t.course + '</td>'; tbody.appendChild(tr); }); }这段代码背后是个通用原则:渲染函数必须是幂等的——同一个数组渲染两次,页面上应该长得一模一样。凡是渲染函数带副作用的,早晚出重复。后来我做任何列表页,第一件事都是确认渲染入口有没有先清空。
4.2 Node.js + Express:get 和 post 到底该用哪个
第三周的思路是先装 Node.js,在桌面建目录,cmd 里 npm init 初始化项目,然后装 Express 做页面和浏览器之间的通信。
// server.js:一个最小的接口服务 const express = require('express'); const mysql = require('mysql'); const app = express(); // 连接池比单连接稳,避免并发请求时连接被占满 const pool = mysql.createPool({ host: '127.0.0.1', user: 'root', password: '******', database: 'school', charset: 'utf8mb4' // 字符集在连接层定死,别指望页面兜底 }); // 查询用 GET:只读、可缓存、参数走 URL app.get('/teachers', function (req, res) { pool.query('SELECT id, name, course FROM teacher ORDER BY id', function (err, rows) { if (err) return res.status(500).json({ ok: false, msg: err.message }); res.json({ ok: true, data: rows }); }); }); // 新增用 POST:会改数据,参数放请求体,不暴露在 URL 上 app.post('/teachers', express.json(), function (req, res) { const id = req.body.id, name = req.body.name, course = req.body.course; // 用 ? 占位符,既防注入也省得自己拼引号 const sql = 'INSERT INTO teacher (id, name, course) VALUES (?, ?, ?)'; pool.query(sql, [id, name, course], function (err) { if (err) return res.status(500).json({ ok: false, msg: err.message }); res.json({ ok: true }); }); }); app.listen(3000, function () { console.log('listen 3000'); });get 和 post 的选择当时我纠结了半天,判断依据其实很简单:这个请求会不会改服务器上的数据。查询不改,用 GET;新增、删除、修改会改,用 POST。另外 GET 的参数在地址栏里能被看到、能被收藏、能被浏览器缓存,POST 不会,涉及用户输入的内容优先 POST。
参数怎么改:charset 这一项在连接配置里写死 utf8mb4,比事后到处找乱码强一百倍;占位符?不是可选项,字符串拼接 SQL 在任何一个稍微认真的项目里都是硬伤。
4.3 MySQL 接入:Excel 导入、JSON 转换、四个按钮
装好 MySQL 之后,要做的是把 Excel 里的数据导进库,建库建表,然后让 Node 代码能把数据读出来。做法是装 node 的 MySQL 辅助包,读出来的结果用 JSON 工具转一道格式,前端拿到的就是标准 JSON。
增删改查的实现路径是:页面上的按钮点击 → 触发不同的 JavaScript 函数 → 函数发请求 → 后端执行不同的 SQL 语句 → 结果返回页面重新渲染。整套链路一旦跑通,你会发现后面加功能就是复制这套结构改 SQL。
这里有个容易被忽略的点:查询返回的 rows 里,字段名和数据库列名完全一致。前端渲染时如果直接写t.name,等于前端和后端被列名绑死了,改个列名前端就崩。当时的做法是在后端做一次字段映射,前端只用业务字段名。这个习惯我从实习带到现在,一直没改过。
4.4 JSP 什么时候该出场
到第四周,老师提醒我 JSP 是 JavaWeb 开发的必备知识点:在传统页面文件里加 Java 程序片段和 JSP 标签,实现动态网站所需的功能,好处是降低对客户端浏览器的要求,即使浏览器端不支持 Java 也能访问。
当时的判断标准是这样的:如果页面数据完全由服务端决定、不需要前端频繁交互,那就用 JSP 在服务端渲染好再吐出去;如果页面需要局部刷新、频繁交互,那就走前后端分离那套接口。两种都能用,区别在于你希望把复杂度放在哪一层。我们那个书城项目页面数量不多、交互简单,JSP 就没派上大用场,但了解它的存在意味着你知道还有另一条路。
5. 避坑与排查:五条我亲历的现象、原因和解决办法
这几条是我在五周里真实卡住过的,每一条都符合“现象看着像 A 问题,其实根因在 B”。我把它们按“现象 → 原因 → 解决”记下来,因为事后补的总结不可靠,当时手忙脚乱的样子才是有效的经验。
5.1 查找功能只能查指定目录,换个盘就说找不到
现象:功能能跑,输入文件名有结果,但永远只在同一个盘符下面找,换到别的盘就提示没有匹配。
原因:搜索的起始路径在方法内部写死了,而不是由调用方传入。这不是算法问题,是参数没设计成外部可控。同类的还有另一种表现:路径拼字符串时用了固定分隔符,换系统就出问题。
解决:把起始路径改成方法入参,视图层负责提供目录选择,业务层只负责按传入路径遍历。改完之后还要顺带处理权限目录——listFiles() 返回 null 时跳过,不要让整个查找中断。
5.2 添加一条信息,页面上变两条
现象:点一次添加按钮,表格里多出一行;再点一次,变成三行;刷新页面才恢复正常。用户视角就是“这个页面坏了”。
原因:渲染函数是追加式的,每次把新数据 push 进数组后,只是往表格后面 append 了一行,没有清掉基于旧数组渲染出来的内容。数据本身是对的,渲染不幂等。
解决:渲染入口第一行先清空容器再整体重绘。改完之后,我顺手把“渲染函数必须是幂等的”当成一条硬规矩,凡是列表页先检查这一点。
5.3 数据库装了三次失败三次,报错信息完全一样
现象:按教程一步步装 MySQL,走到最后启动服务失败,报错每次相同,重装、换路径都不行。
原因:安装过程中的配置类型选项没选对,选了个不适合本机开发的模式,导致服务起不来。当时我以为是系统兼容问题,几乎把网上能搜到的方案都试了一遍。
解决:重新跑安装程序,配置项选“开发者”模式,服务正常启动。这件事之后我形成了一个习惯:装任何环境类软件,先把安装向导每一步的选项含义读一遍,不确定的截图留着,出问题能对得上。
5.4 中文存不进去,或者存进去变问号
现象:英文信息录入完全正常,中文录入后页面上显示成乱码,或者直接变成问号。这个现象出现了两次,第一次是录入,第二次是商品信息回显。
原因:两次根因其实是一回事——编码不一致。第一次是连接数据库的代码没指定字符集,第二次是页面编码和数据库编码不在一个体系上。
解决:第一次在连接配置里加上字符集参数解决;第二次把数据库和页面编码统一后解决。判断顺序我总结成了三步:先确认连接层字符集,再确认表和字段字符集,最后确认页面声明的编码,从里往外查,比乱试快得多。
5.5 Tomcat 部署后访问失败,日志里写着“数据加载失败”
现象:项目部署到 Tomcat,浏览器打开首页报 500,服务器日志里有一条“数据加载失败”。第一反应是配置文件写错了,于是改了又改,越改越乱。
原因:不是配置问题,是连接数据库的 jar 包版本和数据库版本不匹配,导致连接建立不起来。配置改得再多也没用,因为根因在依赖版本上。
解决:换成匹配版本的驱动包,问题消失。另外还有一条同期踩的:服务器的端口号被本机其他程序占用,导致启动失败,换成一个空闲端口就正常了。这两条的共同教训是,日志里那句话往往已经把范围缩得很小,先按日志定位,别先按直觉改配置。
注意:本地虚拟访问网站时打不开,还可能是目录权限没给到位。当时排查了很久,最后发现是要设置来访账户的读取权限,而权限设置项只在特定磁盘格式下才会出现在属性页里。
6. 框架三件套与自测:判断自己能不能独立交付的一套办法
第四周开始学主流框架,Struts 管表示层、Spring 管业务逻辑层和对象管理、Hibernate 管持久层。当时我背下了这套分工,但真正理解它是在自己写完一遍增删改查之后:分工的意义不在技术本身,在于一个团队里所有人用同一套模式写代码,写出来的东西彼此能读懂,重复的部分由框架生成,省下来的时间用在做需求上。Spring 通过控制反转把对象的创建和依赖交给容器管理,还引入了面向切面编程;Hibernate 在 JDBC 之上又封了一层,不用手写 SQL 也能操作数据库。这些概念放在当时那个水平,我只能算知道,谈不上会用。
第五周做项目模型搭建的时候,我才算摸到一点边。模型是用户需求的简化实现,通过建模让客户直观看到软件功能的具体形式,对开发有现实指导意义。那次以模拟商城项目做详细操作,我扮演项目经理,和同伴一起探讨系统要实现哪些功能,比如图书信息录入,然后再自己编码实现、加删除和编辑按钮、给按钮绑超链接跳转到对应页面、在控制层写逻辑类。这一圈走下来,编写代码的能力比前几周有明显提升,不是因为学了多少新知识,是因为终于把“需求到代码”这条路完整走过一遍。
回过头看,判断自己有没有到“能独立交付”的程度,我一般用下面这套自测,四条全过才算摸到门槛:
| 自测项 | 判断标准 | 我这边的实际情况 |
|---|---|---|
| 应用能不能在本机从零启动 | 换一台机器,照着自己的说明能不能跑起来 | 第一周不行,第五周才行 |
| 数据链路能不能通到底 | 页面改一个值,库里能看到变化 | 第二周才通 |
| 出错时能不能定位 | 看到报错能先缩小范围,而不是乱改配置 | 第五周才勉强做到 |
| 能不能对别人讲清结构 | 不看代码说清分了几个模块、各自职责 | 第四周开始能说清 |
还有一个具体的技巧,是我后来一直用的:每天下班前花三分钟,把当天卡住的事按“现象、原因、解决”三行写下来,不写感受只写事实。这份实习记录里最有价值的部分全是这么攒出来的,而那些事后回想起来的流程定义,网上到处都有。
从那以后我每次接手一个陌生环境,都会强制自己先走一遍“换机器能否从零跑起来”这个流程,跑不通就先修环境,绝不动业务代码。这个习惯替我挡掉了很多后来才发现的返工。希望帮到你。
本文还有配套的精品资源,点击获取