学生管理系统 - 新增文本文件与二进制文件的存储系统
学生管理系统 - 新增文本文件与二进制文件的存储系统
目的:
- 新增文本文件与二进制文件的存储系统
- 存在乱码问题
- 分析优化解决该问题
一、系统存储设计核心思路
1. 双存储策略:二进制文件(精准存储类型)+ 文本文件(可读备份),兼顾数据精度与可读性。
2. 保存机制:添加/删除后自动保存 + 选项0手动保存,双重保障数据不丢失。
3. 优化目标:解决文本文件乱码、缓冲区未刷新、代码冗余、异常处理不清晰等问题,保持原功能不变。
二、原代码(文件保存相关核心截取)
1. 二进制文件保存(StudentManagementSystem.java)
public void saveStudentsBin() throws IOException {
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(filename))) {
dos.writeInt(students.size()); // 写入学生总数
for (Student s : students) {
dos.writeUTF(s.getName());
dos.writeInt(s.getAge());
dos.writeUTF(s.getGender());
dos.writeUTF(s.getId());
dos.writeUTF(s.getMajor());
dos.writeDouble(s.getGpa());
}
}
}
通过FileOutputStream在文件filename中关联文件,再包装成DataOutputStream,根据列表的长度遍历每个学生对象,按照“姓名,年龄,性别,id,专业,绩点”的顺序将每个属性对应的数据类型写入文件。
2. 文本文件保存(StudentManagementSystem.java)
public void saveStudents() throws IOException {
FileWriter fw = null;
try {
fw = new FileWriter(filename1);
for (Student s : students) {
fw.write(s.getName());
fw.write(s.getAge()); // 问题1:写入ASCII码,导致乱码
fw.write(s.getGender());
fw.write(s.getId());
fw.write(s.getMajor());
fw.write((int) s.getGpa()); // 问题2:强制转int丢失精度,且写入ASCII码
}
fw.flush(); // 已修复:最初遗漏导致写入失败
} catch (Exception e) {
throw new RuntimeException(e); // 问题3:异常信息不明确
}
}
使用字符流FileWriter关联filename1,准备写入文本,根据列表的长度遍历每个学生对象,按照“姓名,年龄,性别,id,专业,绩点”的顺序将每个属性对应的数据类型写入文件。
3. 保存触发逻辑(Main.java)
// 手动保存(case0)
case 0:
try {
sms.saveStudentsBin();
System.out.println("数据已成功保存到文件!(二进制)");
sms.saveStudents();
System.out.println("数据已成功保存到文件!(文本文件)");
} catch (IOException e) {
throw new RuntimeException(e);
}
break;
// 添加后自动保存(case1片段)
try {
sms.saveStudentsBin();
System.out.println("数据已成功保存到文件!");
sms.saveStudents();
System.out.println("数据已成功保存到文件!(文本文件)");
} catch (IOException e) {
throw new RuntimeException(e);
}
main类当中额外添加了一个选项0,做添加或者删除后的总体文件写入以及保存,当然我在添加或者删除学生信息的模块当中也有做信息的写入,在选项7退出系统后会看见文件成功出现在列表。
三、优化代码(文件保存相关核心截取)
1. 核心优化点说明
| 优化方向 | 原问题 | 优化方案 |
|---|---|---|
| 文本文件格式 | 数值写入ASCII码,无分隔符,乱码 | 转为字符串+分隔符,每行存储一个学生信息 |
| 资源关闭 | FileWriter未自动关闭,依赖flush | 改用try-with-resources自动关闭,无需手动flush |
| 代码冗余 | 重复调用两个保存方法 | 提取公共saveAll()方法,统一触发双存储 |
| 异常处理 | 异常信息模糊,掩盖问题 | 打印具体错误信息,保留原异常链 |
| 空值兼容 | 字符串为null时可能抛异常 | 添加null判断,避免NullPointerException |
2. 优化后的存储类代码(StudentManagementSystem.java)
public class StudentManagementSystem {
private List<Student> students;
String filename = "Students.data"; // 二进制文件路径
String filename1 = "Student.txt"; // 文本文件路径
// 初始化学生列表
public StudentManagementSystem() {
students = new ArrayList<>();
}
// 【优化1:二进制文件保存 - 增加空值兼容】
public void saveStudentsBin() throws IOException {
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(filename))) {
dos.writeInt(students.size()); // 写入学生总数
for (Student s : students) {
// 处理null值,避免NullPointerException
dos.writeUTF(s.getName() == null ? "" : s.getName());
dos.writeInt(s.getAge());
dos.writeUTF(s.getGender() == null ? "" : s.getGender());
dos.writeUTF(s.getId() == null ? "" : s.getId());
dos.writeUTF(s.getMajor() == null ? "" : s.getMajor());
dos.writeDouble(s.getGpa());
}
}
}
- 做对null值的处理
// 【优化2:文本文件保存 - 解决乱码、自动关闭、格式规范】
public void saveStudentsText() throws IOException {
// try-with-resources自动关闭FileWriter,无需手动flush/close
try (FileWriter fw = new FileWriter(filename1)) {
for (Student s : students) {
// 格式:姓名,年龄,性别,学号,专业,GPA\n(分隔符+换行,清晰可读)
String studentLine = String.format(
"%s,%d,%s,%s,%s,%.2f%n",
s.getName() == null ? "" : s.getName(),
s.getAge(),
s.getGender() == null ? "" : s.getGender(),
s.getId() == null ? "" : s.getId(),
s.getMajor() == null ? "" : s.getMajor(),
s.getGpa()
);
fw.write(studentLine); // 写入字符串,无乱码
}
} catch (IOException e) {
// 优化3:明确异常信息,便于调试
throw new IOException("文本文件保存失败:" + e.getMessage(), e);
}
}
- 使用了try-with-resources 的形式,以及定义了字符串格式并且添加了换行,使得保存的文本更美观,以及对null值做了特殊处理。
// 【优化4:提取公共方法,减少代码冗余】
public void saveAll() throws IOException {
saveStudentsBin(); // 先保存二进制文件(核心存储)
saveStudentsText(); // 再保存文本文件(备份)
}
// 其他业务方法(增删查)保持不变...
}
- 通过一次性调用两个方法构成一个新方法,可以直接在0选项中一次性调用,避免代码拖沓。
3. 优化后的保存触发逻辑(Main.java)
// 手动保存(case0)- 优化后:调用公共方法,代码简洁
case 0:
try {
sms.saveAll(); // 统一触发双存储保存
System.out.println("二进制文件(Students.data)和文本文件(Student.txt)保存成功!");
} catch (IOException e) {
System.err.println("保存失败:" + e.getMessage()); // 打印具体错误
}
break;
// 添加后自动保存(case1片段)- 优化后
try {
sms.saveAll(); // 统一调用,减少冗余
System.out.println("学生添加成功,数据已自动保存!");
} catch (IOException e) {
System.err.println("添加后保存失败:" + e.getMessage());
}
break;
// 删除后自动保存(case2片段)- 优化后
try {
sms.saveAll(); // 统一调用
System.out.println("学生删除成功,数据已自动保存!");
} catch (IOException e) {
System.err.println("删除后保存失败:" + e.getMessage());
}
break;
四、优化后效果验证
1. 文本文件可读性
- 原文本文件内容(乱码):
![image]()
- 原因:
- 数值写入ASCII码,无分隔符,乱码
- 字符串为null时可能抛异常
- 优化后文本文件内容(清晰):
![image]()
2. 代码可维护性
- 减少重复代码:原代码重复调用2次保存方法,优化后只需调用
saveAll()。 - 异常可调试:明确打印“文本文件保存失败:路径不存在”等具体信息,快速定位问题。
3. 兼容性提升
- 支持空值:若学生姓名、性别等为
null,会自动转为空字符串,避免程序崩溃。 - 精度保留:GPA以
%.2f格式保存(如3.8→3.80),不丢失精度。
五、关键注意细节
- 二进制文件:写入顺序(姓名→年龄→性别→...)必须与后续读取顺序一致,否则解析错乱。
- 文本文件:分隔符(如逗号)需避免出现在学生字段中(如姓名含逗号需改用其他分隔符,如
|)。 - 文件路径:默认生成在项目根目录,若需指定绝对路径(如
"d:/Students.data"),需确保路径存在。 - 原功能保留:优化后不改变“自动保存+手动保存”的核心机制,仅提升稳定性和可读性。
六、运行截图

添加学生信息
chen 19 female 1 sc 4.0
siying 19 female 2 biology 4.0
mali 18 male 3 law 3.8

删除学生信息 删除id为2的学生,输入选项0进行保存,随后7退出系统,查看文件。
文本文件

出现乱码
二进制文件


可见确实是删掉了id为2的学生信息,但是会发现绩点那一栏出现了乱码。
再根据AI提供的优化代码后进行修改,以及按照原步骤进行修改,输入原信息
文本文件

二进制文件


浙公网安备 33010602011771号