ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java入门第一个项目:学生信息管理系统设计与实现

Java入门第一个项目:学生信息管理系统设计与实现 学Java学到什么程度能做第一个项目这是我在新手群里被问得最多的问题。很多人视频看了一堆、笔记抄了一大本打开IDE却还是对着空白文件发愣。我的建议一直很固定去做学生信息管理系统别再空刷语法了。这个项目在Java入门圈子里火了十几年是有原因的——难度刚刚好功能完整闭环再加上它能覆盖JavaSE阶段几乎所有核心知识点类与对象、封装、构造方法、集合、控制流、字符串比较、模拟增删改查。这篇教程我打算写得格外细从需求拆解、环境准备、代码设计到每一个新手容易踩的坑都拎出来讲透争取让你照着敲一遍就能完全吃透。学生信息管理系统到底管什么先明确一下这个系统干的事。说白了就是管理学生的基础资料对应到业务上就是经典的四大操作添加、删除、修改、查询业内叫CRUD。项目以控制台菜单的方式运行打开程序后通过输入数字序号来选择操作。我把功能列成一张对照表看起来更直观。菜单选项对应功能涉及Java核心知识点1添加学生创建对象、ArrayList的add方法、学号查重2删除学生遍历结合、按学号匹配、ArrayList的remove方法3修改学生查找对象、对象的setter方法4查询所有学生遍历集合、toString重写5按学号查询学生遍历结合、字符串equals比较6退出系统return跳出死循环这五个操作看起来简单但你仔细品一下每一个都需要你真正写出代码去实现而不是背出来。比如添加学生你得考虑万一学号重复了怎么办删除学生你得先遍历列表找到这个人再执行删除修改就更绕一点得先定位到对象再改它的属性。这一整套流程和真实企业项目里写的业务代码逻辑是一致的只是规模缩小到了控制台而已。为什么说它是入门第一大项目因为一个合格的学习项目需要满足三个条件范围小、功能闭环、能综合知识。学生管理系统恰好三条都卡得死死的。范围小到不会让你产生畏难心理功能又完整到做完会有真实成就感而且它逼着你把零散的Java语法整合成一个能运行的软件这是只看书、只背题永远体会不到的。做之前你至少需要具备这些基础会定义类、会写字段和构造方法、知道封装是什么意思、能说出ArrayList的常用方法、会用Scanner做控制台输入、能写if/switch/for。如果这些还不太熟直接回头再翻一下不用精通知道大概怎么用就够因为这个项目本身就是一个帮你巩固这些知识点的大型练习。为什么控制台版够用以及我为什么让你用ArrayList很多新手一听做项目第一反应是“我要不要先学Spring Boot”。我的答案很直接先别。控制台程序是学习阶段最好的载体因为它的心智负担最小你只需要关心类怎么设计、逻辑怎么流转不需要被注释替代掉的配置文件、依赖注入、数据库连接池、前端页面这些东西分散精力。好比学游泳你先在浅水区把换气学会再谈下深水区直接跳深水池固然刺激但大概率会呛水呛到怀疑人生。存储方案我选择了ArrayList而不是直接上MySQL数据库理由有三个。第一ArrayList不需要额外安装环境启动就能用第二它本身就提供增删改查方法和业务操作天然对应第三它的内存模型能帮你直观理解“对象的引用”是什么意思。把ArrayList当作一个内存数据库来用学到的集合知识马上就有了用武之地。等整体流程跑通之后你再把ArrayList换成数据库其实是水到渠成的事我会在最后一章简单讲这个升级路径。类的划分上我的习惯是拆成两个类。一个是Student实体类负责描述“学生是什么”里面放学号、姓名、年龄、专业这些属性以及构造器、getter/setter、toString方法另一个是StudentManager管理类负责描述“怎么管理学生”提供菜单、添加、删除、修改、查询这些操作。这种实体类管理类的结构就是以后MVC里Model和Controller分工的雏形别小看这个小项目它把软件设计的种子都埋好了。动手前先花十分钟把环境收拾利索环境这块官方文档式的废话我就不多说了只讲必须做的三件事。第一安装JDK。版本选择上不用太纠结现在市面上主流教学用JDK 8和JDK 17都行。JDK 8是经典版本资料最多JDK 17是长期支持版本授权协议友好也都差不多的命。所谓安装其实就是解压或者点下一步把JDK装到某个目录下。二是记得配环境变量JAVA_HOME指向JDK安装目录PATH里加上%JAVA_HOME%\bin。配完之后直接在命令行敲java -version出现版本信息就说明环境OK。安装完毕后重要的是不要把下载的JDK目录到处乱挪我之前看到有人把JDK装在C盘临时目录里清理磁盘时顺手删了程序瞬间全崩这种低级错误一定别犯。第二选择IDE。我用的是IntelliJ IDEA社区版免费而且对Java支持极好Eclipse如今越来越少见。IDEA的安装几乎是纯点下一步不需要额外的激活流程。打开IDEA之后新建Project直接选择Java模块看到SDK就选刚装好的JDK版本。创建之后IDEA会生成一个src目录Java源码就放在这个目录下面。第三做一次环境自检。在src下新建一个测试类写个最简单的main方法跑起来输出一行“Hello”。如果控制台成功打印说明JDK、IDE、编译运行链路都是通的可以正式写学生管理系统了。这一步我坚持建议每个新手都做因为环境问题如果不提前排掉写代码时遇到报错会混淆到底是环境问题还是代码问题白白消耗心态。核心代码实现从实体类到管理类一一拆解下面进入整篇教程的主菜。我会先放出完整可运行的代码然后针对每一块关键代码做详细解读。你可以先照着敲敲完再回来看解释也可以边敲边看都行。先建第一个类Student.java这是学生实体类负责定义一条学生数据长什么样。public class Student { private String id; private String name; private int age; private String major; public Student() { } public Student(String id, String name, int age, String major) { this.id id; this.name name; this.age age; this.major major; } public String getId() { return id; } public void setId(String id) { this.id id; } public String getName() { return name; } public void setName(String name) { this.name name; } public int getAge() { return age; } public void setAge(int age) { this.age age; } public String getMajor() { return major; } public void setMajor(String major) { this.major major; } Override public String toString() { return 学号 id 姓名 name 年龄 age 专业 major; } }这个类的重点是理解封装。字段都用private修饰外界不能直接访问必须通过public的getter和setter来读写这是Java面向对象三大特性之一。构造方法提供了两种创建方式一种是无参构造先创建空对象再逐项set另一种是带参构造一步到位把所有属性赋值。toString方法一定要重写否则后面打印学生信息时控制台会输出一大堆带的乱码地址具体问题我在第五章会展开讲。接着建第二个类StudentManager.java。这是管理类里面包含了主控逻辑和所有业务方法。import java.util.ArrayList; import java.util.Scanner; public class StudentManager { private static ArrayListStudent students new ArrayList(); private static Scanner sc new Scanner(System.in); public static void main(String[] args) { while (true) { System.out.println( 学生信息管理系统 ); System.out.println(1. 添加学生); System.out.println(2. 删除学生); System.out.println(3. 修改学生); System.out.println(4. 查询所有学生); System.out.println(5. 按学号查询学生); System.out.println(6. 退出系统); System.out.print(请选择功能); int choice sc.nextInt(); sc.nextLine(); switch (choice) { case 1: addStudent(); break; case 2: deleteStudent(); break; case 3: updateStudent(); break; case 4: showAll(); break; case 5: findById(); break; case 6: System.out.println(感谢使用再见); return; default: System.out.println(无效输入请重新选择); } } } private static void addStudent() { System.out.print(请输入学号); String id sc.nextLine(); if (findStudentById(id) ! null) { System.out.println(学号已存在添加失败); return; } System.out.print(请输入姓名); String name sc.nextLine(); System.out.print(请输入年龄); int age sc.nextInt(); sc.nextLine(); System.out.print(请输入专业); String major sc.nextLine(); Student stu new Student(id, name, age, major); students.add(stu); System.out.println(添加成功); } private static void deleteStudent() { System.out.print(请输入要删除学生的学号); String id sc.nextLine(); Student stu findStudentById(id); if (stu null) { System.out.println(未找到该学生); return; } students.remove(stu); System.out.println(删除成功); } private static void updateStudent() { System.out.print(请输入要修改学生的学号); String id sc.nextLine(); Student stu findStudentById(id); if (stu null) { System.out.println(未找到该学生); return; } System.out.print(请输入新的姓名直接回车保持原值); String name sc.nextLine(); if (!name.trim().isEmpty()) { stu.setName(name); } System.out.print(请输入新的年龄直接回车保持原值); String ageStr sc.nextLine(); if (!ageStr.trim().isEmpty()) { int age Integer.parseInt(ageStr); stu.setAge(age); } System.out.print(请输入新的专业直接回车保持原值); String major sc.nextLine(); if (!major.trim().isEmpty()) { stu.setMajor(major); } System.out.println(修改成功); } private static void showAll() { if (students.isEmpty()) { System.out.println(当前没有学生信息); return; } System.out.println(----- 学生列表 -----); for (Student stu : students) { System.out.println(stu); } System.out.println(共 students.size() 名学生); } private static void findById() { System.out.print(请输入学号); String id sc.nextLine(); Student stu findStudentById(id); if (stu null) { System.out.println(未找到该学生); } else { System.out.println(stu); } } private static Student findStudentById(String id) { for (Student stu : students) { if (stu.getId().equals(id)) { return stu; } } return null; } }整段代码我按执行顺序拆成几个关键点来理解。主菜单的循环分支到底怎么写main方法里是一个while(true)死循环这是控制台程序最常见的菜单写法。每次循环都打印一次菜单然后读取用户的输入用switch决定执行哪个方法。用户选6时用return直接结束程序这样循环才真正退出。这个结构的关键在于菜单必须每执行完一次操作就重新出现因为while(true)会一直循环而return是唯一的出口。有新手问能不能写System.exit(0)也能行但用return更符合方法退出的直觉代码也好读。一个容易被忽略的细节是sc.nextLine()紧跟在sc.nextInt()后面。这里我单独开一小节讲因为十个新手有九个在这里翻车。nextInt()读取完数字后并不会消费掉输入缓冲区里的换行符。这个回车符还留在缓冲区里如果下一句直接写sc.nextLine()去读取姓名读到的是空字符串就会出现“刚输入完菜单序号姓名直接被跳过了”的现象。解决办法很简单就是在nextInt()之后补一次sc.nextLine()把这个缓冲残留清掉。所以主菜单代码里int choice sc.nextInt();后面立刻跟了一行sc.nextLine();加了这一句后面的姓名输入才不会被吃掉。这个坑我后续还会在常见问题里反复强调。添加功能为什么必须先查重添加学生的第一行先让用户输入学号接着立刻调用findStudentById(id)判断这个学号是否已经存在如果存在就提示添加失败并return。为什么要查重因为学号是学生的唯一标识如果允许重复后面删除和修改时按学号找人就不知道找的是哪一个了系统逻辑会彻底乱掉。这其实就是业务上说的唯一性约束。很多初学者会把查重这一步漏掉我建议你在第一版代码里就加上因为后面大概率会用到不如一步到位养成习惯。查重的实现方式写在了findStudentById方法里用增强for循环遍历students列表将每个学生的getId()和传入的学号做equals比较。这里不要写成这是Java初学阶段一个特别隐蔽的坑。对字符串比较的是内存地址而equals比较的是内容两个不同位置存放的相同字符串会得到false。你的学号每次都是通过sc.nextLine()输入产生的几乎不可能和集合里的字符串是同一个内存地址所以必须用equals。删除和修改为什么都要先找对象删除和修改这两个方法的逻辑其实如出一辙先按学号查出目标学生查不到就直接提示并返回查到了再继续操作。删除的代码是students.remove(stu)直接传入我们要删除的学生对象。这里有一个隐藏细节ArrayList.remove(Object)内部会调用对象的equals方法来判断哪个元素要删除。由于我们的Student类没有重写equals此时它使用的定义是Object中默认比较对象地址的实现。不过在实际运行中我们传进去的stu恰恰是从列表里拿出来的同一个对象引用地址必然相同所以remove能正常工作这算是此次学习阶段的一种幸运。修改功能则体现了“先定位再更新”的思路把用户输入的新姓名、新年龄、新专业通过setter方法直接设置到查到的对象上。因为对象在Java中是通过引用传递的这个对象的引用指向的内存位置就是列表里存储的那块内存所以改了对象属性列表里的数据也就跟着变了不需要把对象重新放回列表。更新的时候我又额外处理了一下回车保持原值的逻辑也就是用户直接按Enter时不覆盖原来的内容这个小交互让人用起来更舒服代码也演示了如何处理用户输入为空的情形。列表查询是最简单的增强for循环每次取出一个Student对象System.out.println(stu)会自动调用Student类里重写过的toString方法把学号、姓名、年龄、专业拼成一行熟悉的文本打印出来。最后再打印一个总数这个细节用到了students.size()能让你更清楚列表当前的状态。常见问题与排查实录写这个项目时新手大概率会碰到下面几个问题我把每个的成因和解决方案列出来你遇到了直接对症下药。编译报错“找不到符号”文件名与类名不一致IDEA里新建类时文件名必须和类的定义完全一致Student.java里定义的必须是public class Student。如果你在新建文件时起的名字是StudentInfo里面却写class Student编译直接报找不到符号。解决很简单统一文件名和类名。另外提醒一个细节Java是严格区分大小写的语言student和Student是两个完全不同的名字敲代码时手抖大小写不一致就会报错。打印学生信息出现开头的乱码运行查询功能后控制台输出的不是学号姓名而是类似Student1b6d3586这样的文本初学者第一次见通常吓一跳。原因就是Student类没有重写toString()方法。println在打印一个对象时会默认调用这个类的toString方法而Object类提供的默认实现就是返回“类名哈希码”这样的字符串。只要我们在Student类里按照自己的格式重写toString打印出来的就是正常的中文信息了。这也是为什么要强调实体类一定要手动重写toString。使用remove删除学生没反应或删错人在删除操作中如果打印提示“未找到该学生”说明学号匹配失败了。请先检查是不是把学号输入错了或者列表里还没有这个学生。但更隐蔽的一种情况是你把Student类建好之后没有重写equals和hashCode方法后来又自己手动new了一个内容相同但不是同一个地址的对象去调用remove这时remove会找不到要删的对象。简单处理的办法是始终用findStudentById返回的结果作为remove的参数不要自己去new新对象。输入数字后后面的姓名直接跳过读不到这是Scanner最经典的一个坑也是初学者必踩。现象是启动程序后输入1菜单没消失紧接着问你学号时程序不等待输入直接跳过了或者读取到空字符串。本质就是因为sc.nextInt()读完了数字但没消费掉换行符。解决方案我已经在之前的代码里写过了nextInt()后立刻补一行sc.nextLine()把换行符吃掉接下来再读字符串就没有任何问题。在做添加学生功能时年龄那段输入也同理。修改年龄时输入非数字报NumberFormatException修改功能里把ageStr用Integer.parseInt转成int如果用户输入的不是数字比如输入了一个字母程序会直接抛异常崩溃。这在基础版里不算bug因为没人会强行要求一个控制台程序对错误输入做完美防御。但如果你想做得好一点可以加一层try-catch捕获异常后提示用户输入不合法并重新输入。不过这属于进阶优化第一版不用太纠结先把核心流程跑利索。到这里完整的基础版本已经能运行了。项目做完我建议你对照着这个排查表检查一遍症状大概率原因解决方案编译报找不到符号文件名、类名不一致或大小写错误统一名称注意大小写打印一堆地址没有重写toString在Student中重写toString删除失败或删错学号匹配错误或remove参数不对检查equals用findStudentById的结果做参数姓名被跳过nextInt后残留换行符nextInt后补一行nextLine年龄输入乱字后崩溃Integer.parseInt遇到非数字加try-catch或规范输入提示代码完成后值得做的三个升级方向基础版跑通之后如果你觉得不过瘾我给你指三条明确的升级路径。第一个方向是把数据从内存搬到文件里。现在程序关闭后ArrayList里的数据全都没了下次启动又是一张空表。用FileWriter和BufferedWriter在添加、删除、修改后把数据写入一个文本文件启动时再用BufferedReader把文件读回来就能实现数据持久化。这个升级很简单但会让你第一次体会到“程序不只是在运行的时候有用”。第二个方向是接入JDBC和MySQL数据库。既然列表版的逻辑已经跑通你可以把ArrayListStudent换成操作一张数据库表学号和姓名存成字段用Statement或PreparedStatement执行SQL。这时候你会发现findStudentById这个方法变成了一个典型的按主键查询的SQL语句等于你不知不觉把业务逻辑从集合迁移到了关系数据库上后面再学SSM框架就顺理成章了。第三个方向是用接口把管理类的方法规范起来。定义一个StudentService接口把添加、删除、修改、查询四个方法声明进去再让管理类去实现它。这样调用方只依赖接口不依赖具体实现以后你要把内存版换成数据库版直接换一个实现类就行这就是依赖倒置思想的雏形。这一步做完你已经为学Spring打了非常扎实的地基。我自己的切身体会是第一次完整跑通这个项目时最大的成就感不是“我会用ArrayList了”而是“我能把一个需求从脑袋里变成能运行的程序了”。这种从无到有的正反馈特别重要。做完以后你可以试着在现有代码上继续加功能比如按专业筛选学生、统计平均年龄、按学号排序这些扩展既有趣又能倒逼你主动翻语法文档。学习Java没有捷径但用一个恰到好处的小项目给自己正反馈绝对是一条靠谱的路径。祝你在这个项目里卡好第一个壳后面越写越顺。
返回列表