# 结课项目 学生学分管理系统 **Repository Path**: night-yang/Student-Credit-Management-System ## Basic Information - **Project Name**: 结课项目 学生学分管理系统 - **Description**: 小组实验报告 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-10-27 - **Last Updated**: 2023-11-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 结课项目 学生学分管理系统 ## 一、实验目的 建立了一个学分管理系统,包括学生、班级和课程信息的管理。提供基本用户界面,支持添加、查询学生信息,导入Excel数据等功能。使用序列化实现数据持久性。 ## 二、需求 学生选课并且考核后,获得成绩分数(按百分制),根据我校学习管理规定,对应至字母分制。因此可得某学生的 GPA,可得班级的 GPA。 ## 三、基本要求 分析系统角色、实体,定义相应的属性和方法,在系统中完成相应的实例化操作,支持运行过程中新录入学生成绩,也可以从 Excel或文件中导入 (可选),计算学生或班级 GPA。支持查询。其他功能可自行拓展。 呈现界面不做统一要求: 可采用运行期间在控制台输入交互,有能力的同学也可采用 GUI或web 页面设计 (非必须)。 ## 四、初期设计思路 1. 最初的的想法是利用JDBC连接MySQL然后通过sql指令实现功能,可这样感觉相当于 **写了个简易Excel** 一样,确实能完美实现所有功能,甚至运行效率更高,但无法体现出java的特性,不需要**分析系统角色、实体,定义相应的属性和方法,在系统中完成相应的实例化操作**,在最后一节课上听到关于利用StringBuffer实现储存实例化对象到本地的操作之后,是没用过的玩法,于是决定重新设计本系统。 2. 本程序要有GUI,要能根据班级、学号、姓名查询符合条件的学生。可以分为三种对象:班级、学生、课程。班级包含学生,学生包含课程。 3. swing布局不够好用且不够美观,在以往的实验中已经有过涉猎,希望在本次实验的过程中接触到一点新内容,因此选择使用javafx布局。 4. 处理导入的excel文件使用jxl的话过于老旧,对excel文档的版本有要求,因此利用maven导入Apache POI jar包更好的处理文档。 5. 类的设计 - course类 名字 学号 分数 绩点 字母分制 - Group类 名字 平均绩点 学生HashMap - Student类 名字 学号 性别 课程HashMap 平均绩点 - Main 类:包含了整个程序的入口和主要逻辑。维护了学生、班级、课程等数据的存储和操作。 - Items 类:用于展示在 JavaFX TableView 中的数据结构。 ## 五、解题思路 ### 1.数据持久化: SaveAndLoad 类:提供了数据的序列化保存和加载功能。使用 ObjectInputStream 和 ObjectOutputStream 实现对象的读写,将程序的状态保存到文件中,以便下次运行时加载。 ### 2.JavaFX 用户界面: 通过 JavaFX 创建了一个简单的图形用户界面,用于展示学生信息和提供一些操作接口。 使用 TableView 显示学生的信息,同时使用各种控件(TextField、Button、ComboBox)提供搜索、添加学生、导入学生等功能。 ### 3.功能实现: 实现了添加学生信息的功能,包括对班级、学生的检查和添加,同时更新数据后进行保存。 提供了从 Excel 文件导入学生信息的功能,通过 FileChooser 获取文件,使用 Apache POI 处理 Excel 文件。 ### 4.数据展示和查询: 在 TableView 中展示学生的各项信息,包括班级、学号、姓名、性别、课程号、课程名称、分数等。 提供了按照姓名、学号、班级、课程等不同条件进行查询的功能。 ### 5.图形界面事件处理: 使用 JavaFX 中的事件处理机制,通过按钮的点击等事件触发相应的操作。 对添加学生、搜索学生等操作进行了事件处理。 ### 6.数据统计和计算: 计算学生的 GPA、课程的 GP 等信息,并在表格中显示。 计算班级的 GPA,并在表格中显示。 总体思路是通过 JavaFX 提供的图形用户界面,实现了一个简单的学生学分管理系统,用户可以通过界面进行学生信息的添加、查询和导入。数据通过序列化保存到文件,方便下次运行时加载。界面使用 TableView 展示数据,通过事件处理实现不同功能的触发。核心逻辑包括数据的存储和管理,用户界面的交互以及数据的展示。 ## 六、流程图 ![输入图片说明](%E5%AD%A6%E7%94%9F%E5%AD%A6%E5%88%86%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F.png) ## 七、运行截图 1. 从excel文档中导入数据 ![输入图片说明](%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202023-11-20%20194445.png) 2. 文档有异常数据,导入失败 ![输入图片说明](%E8%BF%90%E8%A1%8C%E6%88%AA%E5%9B%BE%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202023-11-20%20194619.png) 3. 直接录入数据,输入框支持复制粘贴等操作,绩点实时更新 ![输入图片说明](%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202023-11-20%20194953.png) 4. 查询功能,仅演示按班级查询 ![输入图片说明](%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202023-11-20%20195136.png) 5.排序功能,如按照GPA高至低排序 ![输入图片说明](%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202023-11-20%20195242.png) 6. 导入时文件选择器,限制类型 ![输入图片说明](%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202023-11-20%20195503.png) 7. 更多功能请自行尝试 ## 八、关键代码 ### 1. 属性和构造方法: ``` public class Main extends Application implements Serializable { private HashMap groups; // 用于存储班级信息的 HashMap public Main() { groups = new HashMap<>(); } ``` Main 类定义了一个 HashMap 类型的属性 groups,用于存储班级信息。 构造方法 Main() 初始化了 groups,确保在创建 Main 对象时,groups 被正确初始化为空的 HashMap。 ### 2. JavaFX 界面初始化: ``` public void start(Stage primaryStage) { // 创建 TableView 对象,用于显示学生信息 TableView studentTable = new TableView<>(); // 设置表格的列和数据 TableColumn nameColumn = new TableColumn<>("班级"); // ... 设置其他列 studentTable.getColumns().addAll(nameColumn, studentIdColumn, /*...*/); // 更新表格数据 updata(studentTable); // 创建搜索框、添加学生信息的输入框等 UI 元素 ComboBox searchOptions = new ComboBox<>(); TextField searchField = new TextField(); Button searchButton = new Button("搜索"); Button alldataButton = new Button("显示所有数据"); TextField nameInput = new TextField(); // ... 设置其他输入框 Button addStu = new Button("添加学生"); Button addStuFile = new Button("从Excel导入学生"); // 设置事件监听器,实现搜索、显示所有数据、添加学生等功能 searchButton.setOnAction(event -> search(studentTable, searchOptions.getValue(), searchField.getText())); alldataButton.setOnAction(event -> updata(studentTable)); addStu.setOnAction(event -> { // ... 添加学生的具体实现 }); addStuFile.setOnAction(event -> { // ... 从Excel导入学生的具体实现 }); // 布局管理,设置各 UI 元素的排列方式 HBox searchBox = new HBox(10); HBox addStudentBox1 = new HBox(10); HBox addStudentBox2 = new HBox(10); VBox vbox = new VBox(10); vbox.getChildren().addAll(table, searchBox, addStudentBox1, addStudentBox2); // 创建 Scene 并设置给 Stage Scene scene = new Scene(vbox, 800, 500); primaryStage.setScene(scene); primaryStage.show(); } ``` 在 start 方法中,创建了 JavaFX 窗口和表格,以及其他 UI 元素,包括搜索框、添加学生的输入框等。 使用 TableView 类来创建表格,并设置了列和数据。通过 TableColumn 类设置每一列的名称,并通过 PropertyValueFactory 进行数据绑定。 通过 ComboBox 创建下拉框,用于选择搜索的规则。 设置了各按钮的事件监听器,通过 Lambda 表达式实现事件处理。 使用 HBox 和 VBox 进行布局管理,设置了各 UI 元素的排列方式,保证整体界面的美观性。 ### 3. 事件处理: ``` searchButton.setOnAction(event -> search(studentTable, searchOptions.getValue(), searchField.getText())); alldataButton.setOnAction(event -> updata(studentTable)); addStu.setOnAction(event -> { Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setHeaderText(null); try { main.addData(nameInput.getText(), genderInput.getText(), studentIdInput.getText(), classNameInput.getText(), courseInput.getText(), courseNumberInput.getText(), Float.parseFloat(scoreInput.getText())); updata(studentTable); alert.setContentText("添加成功"); } catch (NumberFormatException e) { alert.setContentText("添加失败"); } alert.showAndWait(); }); ``` 使用了 Lambda 表达式,将搜索按钮和显示按钮的点击事件与一个匿名的事件处理器绑定在一起。事件处理器调用了 search 方法,该方法接收三个参数: studentTable:JavaFX 中的表格视图,用于显示学生的信息。 searchOptions.getValue():表示搜索选项,即用户选择的按照何种条件进行搜索,比如按姓名、学号等。 searchField.getText():表示用户在搜索框中输入的搜索关键字。 事件处理器弹出一个对话框 Alert,提示用户添加学生的结果。在添加学生之前,会调用 main.addData 方法,该方法接收学生的各项信息,并将学生添加到系统中。之后,调用 updata 方法,更新表格中的数据,确保用户界面中显示的数据是最新的。 ### 4. 表格数据更新方法: ``` public void updata(TableView studentTable){ studentTable.getItems().clear(); main.groups.forEach((key,group)-> group.getStudents().forEach((i, student)-> student.getCourses().forEach((k, course)->{ Items item=new Items(group.getName(),student.getId(),student.getName(),student.getGender(), course.getId(),course.getName(),course.getScore(),course.getLevel(),course.getGP(), student.getGPA(),group.getGPA()); studentTable.getItems().add(item); }))); System.out.println("updata done"); } ``` 清空表格数据: 调用 studentTable.getItems().clear() 方法,清空表格中的所有数据。 遍历数据并更新表格: 使用嵌套的 forEach 循环,遍历所有的班级、学生和课程数据。 对于每个班级、学生和课程的组合,创建一个 Items 对象,该对象包含了要显示在表格中的各个字段的值。 调用 studentTable.getItems().add(item) 将创建的 Items 对象添加到表格中,实现表格数据的更新。 显示更新完成的消息: 打印输出一条消息,表示更新操作已完成。 ### 5. 添加学生的方法: ``` public void addData(String name,String gender,String id,String Group,String course,String cid,float score){ Group group=groups.get(Group); //检查是否已存在该班级 if (group==null){ group =new Group(Group); groups.put(Group,group); group=groups.get(Group); } Student student=group.getStudents().get(id); if (student==null){ group.addStudents(name, id, gender); student=group.getStudents().get(id); } student.addCourses(course,cid,score); saveMain(this); } ``` 该方法接收学生的姓名 name、性别 gender、学号 id、班级名称 group、课程名称 course、课程号 courseId 以及分数 score 作为参数。步骤如下: 获取班级信息: 通过班级名称 group 从 groups 中获取对应的班级对象 studentGroup。 检查班级是否存在: 如果 studentGroup 为 null,表示该班级尚未存在,因此创建一个新的班级对象并加入到 groups 中。 获取学生信息: 通过学号 id 从班级中获取对应的学生对象 student。 检查学生是否存在: 如果 student 为 null,表示该学生尚未存在于该班级,因此创建一个新的学生对象并加入到班级中。 添加课程信息: 调用学生对象的 addCourses 方法,将课程信息添加到学生对象中。在 addCourses 方法中,会创建课程对象并将其加入到学生的课程列表中。 保存更新后的数据: 调用 saveMain(this) 方法,将更新后的 Main 对象保存到文件中,以保留添加学生的变更。 该方法的执行过程主要涉及班级、学生和课程对象的创建和关联,确保数据的完整性和一致性。 ### 6. loadMain 方法: ``` public static Main loadMain() { Main main = new Main(); try { FileInputStream fis = new FileInputStream("StuCreditManagementSys_Data"); ObjectInputStream ois = new ObjectInputStream(fis); main = (Main) ois.readObject(); ois.close(); fis.close(); } catch (IOException | ClassNotFoundException ioe) { System.out.println(ioe.getMessage()); } return main; } ``` loadMain 方法用于从文件中加载 Main 对象。 通过 FileInputStream 和 ObjectInputStream 从文件中读取对象,并进行类型转换为 Main 类型。 捕获了 IOException 和 ClassNotFoundException 异常,并在控制台输出异常信息。 ### 7. saveMain 方法: ``` public static void saveMain(Main main) { try { FileOutputStream fos = new FileOutputStream("StuCreditManagementSys_Data"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(main); oos.close(); fos.close(); } catch (IOException ioe) { System.out.println("saveMain:" + ioe.getMessage()); } } ``` saveMain 方法用于将 Main 对象保存到文件。 通过 FileOutputStream 和 ObjectOutputStream 将对象写入文件。 捕获了 IOException 异常,并在控制台输出异常信息。 ### 8. readExcelFile 方法: ``` public static void readExcelFile(Main main, File file, TableView studentTable) { try (FileInputStream fis = new FileInputStream(file); Workbook workbook = WorkbookFactory.create(fis)) { // ... 读取 Excel 文件的逻辑 } catch (IOException | EncryptedDocumentException e) { System.out.println(e.getMessage()); } } ``` readExcelFile 方法用于从 Excel 文件中读取数据并添加到 Main 对象中。 使用 FileInputStream 打开文件,并通过 WorkbookFactory 创建 Workbook 对象。 捕获了 IOException 和 EncryptedDocumentException 异常,并在控制台输出异常信息。 ## 九、实验感想与体会 @叶阳 : 1. 在开发过程中,我想到这样一个问题,计算GPA是每个学生都要用到的方法,那实例化大量的学生是否会造成复制多份相同方法使得不必要的内存浪费,是否应该把计算GPA的方法使用static关键词修饰,然后通过传参使用。在探索后我得到了答案: 在Java中,实例方法并不会因为对象实例的创建而被复制一份到内存中。也就是说,无论你创建多少个对象,方法在内存中只有一份。当你调用一个对象的实例方法时,Java会将当前对象的引用传递给方法(这就是你在方法中可以使用"this"关键字的原因),这样方法就可以访问和操作该对象的状态。因此,让每个学生对象都有自己的计算GPA方法并不会导致内存开销过大。实际上,这是面向对象设计的基础,每个对象都应该拥有自己的状态(字段)和行为(方法)。 2. 尽量避免使用 'printStackTrace()' 方法打印日志,这是一个不可靠的方法,会导致内存浪费。 3.组员在进行UI开发过程中使用的TableColumn使我很惊喜,功能比原预想中的列表更加强大,不过我并不会使用,在查阅资料之后才决定专门设计了Items类,Items类中的get方法虽然在编译器中是灰色,但实际不能删除,以`new PropertyValueFactory<>("**")`的方式调用。 4.本次实验完成了我前期构想中的所有功能,要说还有什么优化方向,也就是异常处理和修改数据不够直观了。 @王先生123456 : 1.实际应用编程: 这个项目涉及了学分管理系统的实际应用,让我感受到编程不仅仅是写一些简单的程序,更是为解决实际问题而动手实践。这种实战经验对我今后的编程能力提升很有帮助。 2.JavaFX图形界面: 通过这个项目,我学到了如何使用JavaFX构建图形用户界面,包括如何设计UI、布局和事件处理。这使我对Java编程语言的全貌有了更清晰的认识。 3.文件操作和数据持久化: 在这个项目中,涉及到了文件的读写和对象的序列化,这让我学到了如何进行数据的持久化存储。这对于长期使用的软件系统来说是很重要的。 4.事件驱动编程: 学习了如何使用JavaFX的事件驱动编程模型,通过按钮点击等用户交互触发相应的操作,这让我更好地理解了程序和用户之间的交互方式。 5.异常处理和代码健壮性: 代码中加入了一些简单的异常处理,这使我认识到在实际项目中保证代码的健壮性和用户友好性的重要性。这也是良好的编码风格的一部分。 6.Excel文件处理: 学到了如何通过Java读取Excel文件,这在实际工作中可能会经常用到。这也提高了我对数据导入导出的理解。 7.团队协作与版本控制: 项目中代码的组织和结构让我思考到了在大型项目中的团队协作和版本控制的问题。这是软件工程中的一项重要技能。 @星约 : 1. JavaFX图形界面设计:掌握了如何使用JavaFX构建图形用户界面。从创建表格视图(TableView)、能够自由设置列和数据 搜索框 输入框和按钮等UI元素,这些让我更熟悉了JavaFX的界面布局和事件处理,也体验到了JavaFX的灵活性和在图形化应用中的优势。 2. 事件驱动编程:学习了JavaFX的事件驱动编程模型,通过设置按钮的点击事件、下拉框的选择事件等,实现了用户操作触发相应功能的方式。这种交互方式对于用户友好的应用程序至关重要。 3. 文件操作与数据持久化:在项目中涉及了文件的读写和对象的序列化,学会了如何持久化数据。这对于保持系统数据的完整性和长期使用是非常重要的一部分 4. 异常处理与健壮性:加入了简单的异常处理机制,可能体会到了在实际项目中保证代码健壮性的重要性。这有助于提高代码的稳定性,并保证用户体验。 5. 持续学习与拓展:了解了一些关于Excel文件的读取,这是一个实际工作中可能会用到的技能。 6. 数据持久化的应用:在实现数据持久化的过程中,我意识到了数据的长期保存对于软件系统的重要性。了解如何存储和读取数据,让我更好地理解了持久化的概念和应用。 @Nemeorum : 1. 本次实验中我学习并尝试了如何构建构建简单图形用户界面的过程,通过创建表格、文本框、按钮等 UI 元素,以及设置事件监听器和各式布局管理。同时还了解了'CellValueFactory'这一获取列中属性的值的方法,它将在被创建时从Items 对象中提取对应属性的值,可以有效地将数据模型与表格列的显示关联起来。 2. 大致了解了读取文件、读取对象的方法,序列化与反序列化的方法与作用,以及Serializable接口的应用。 ## 十、参与贡献及分工 逻辑代码设计及编写: @叶阳 UI设计及UI代码编写: @Nemeorum 实验报告编辑:@王先生123456 @叶阳 @星约