文本文件与基于二进制文件的存储的学生管理系统
文本文件与基于二进制文件的存储的学生管理系统
一、项目介绍
本项目旨在对学生管理系统进行优化,重点在于实现数据持久化。我们通过支持文本与二进制两种存储格式,为系统提供了灵活且可靠的长
期数据保存方案。
二、包结构
StudentsManagementSystemProject/
├── src/
│ └── code/
│ ├── Main.java // 程序入口,UI与主业务逻辑
│ ├── Student.java // 学生数据模型
│ ├── StudentManagementSystemInterface.java // 数据操作接口定义
│ ├── File.java // 文件操作工具类
│ ├── StudentManagementSystem1.java // 基于二进制文件的数据管理实现
│ └── StudentManagementSystem.java // 基于文本文件的数据管理实现
├── data.txt // 文本格式数据文件
└── data1.txt // 二进制格式数据文件
三、核心代码介绍
3.1 代码展示
File.java
该类采用了静态工具类模式,将所有文件读写操作封装起来,实现了业务逻辑与文件操作的解耦
点击查看代码
package code;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class File {
public static void appendStudentToTextFile(String filename,String data) {
try(FileOutputStream fos = new FileOutputStream(filename,true);
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
BufferedWriter bw = new BufferedWriter(osw)) {
bw.write(data);
} catch (IOException e) {
System.err.println("追加学生到文本文件失败: " + filename);
e.printStackTrace();
}
}
public static void writeAllStudentsToTextFile(String filename,List<Student> students) {
try(FileOutputStream fos = new FileOutputStream(filename);
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
BufferedWriter bw = new BufferedWriter(osw)) {
for(Student s : students) {
bw.write(s.StringBuild());
}
} catch (IOException e) {
System.err.println("写入所有学生到文本文件失败: " + filename);
e.printStackTrace();
}
}
public static List<Student> readAllStudentsFromTextFile(String filename) {
List<Student> students = new ArrayList<>();
try (FileInputStream fis = new FileInputStream(filename);
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
BufferedReader br = new BufferedReader(isr)) {
String line;
while ((line = br.readLine()) != null) {
String[] parts = line.split(",");
if (parts.length == 6) {
Student s = new Student(parts[0], Integer.parseInt(parts[1]), parts[2], parts[3], parts[4], Double.parseDouble(parts[5]));
students.add(s);
}
}
} catch (IOException e) {
System.err.println("从文本文件读取学生失败: " + filename);
e.printStackTrace();
}
return students;
}
public static void writeAllStudentsToBinaryFile(String filename,List<Student> students) {
try (FileOutputStream fos = new FileOutputStream(filename);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(students);
} catch (IOException e) {
System.err.println("写入所有学生到二进制文件失败: " + filename);
e.printStackTrace();
}
}
public static List<Student> readAllStudentsFromBinaryFile(String filename){
List<Student> students= new ArrayList<>();
try (FileInputStream fis = new FileInputStream(filename);
BufferedInputStream bis = new BufferedInputStream(fis);
ObjectInputStream ois = new ObjectInputStream(bis)) {
students = (List<Student>) ois.readObject();
} catch (EOFException e) {
//空白
}catch (IOException | ClassNotFoundException e) {
System.err.println("从二进制文件读取学生失败: " + filename);
e.printStackTrace();
}
return students;
}
}
3.2 代码分析
-
静态工具类模式
-
装饰器模式在IO流中的应用
-
FileOutputStream: 打开一个到文件的连接,并以字节的形式向文件中写入原始数据
-
OutputStreamWriter: 是一个字符到字节的桥梁,它装饰了 FileOutputStream,使其具备了处理字符的能力
-
BufferedWriter: 修饰了 OutputStreamWriter ,为其增加了缓冲区
- Java序列化机制
-
ObjectOutputStream: 将Java对象序列化,并写入到二进制文件中
-
ObjectInputStream: 反序列化的核心,将之前通过 ObjectOutputStream 序列化的字节流还原成原始的Java对象
3.3 对比分析
| 对比 | 文本文件 | 二进制文件 |
|---|---|---|
| 读取方式 | 人类可读,需要用文本编辑器或按字符编码读取 | 机器可读,人眼看过去是乱码,必须用专门的程序按二进制格式解析 |
| 文件形态 | 由字符编码(如ASCII, UTF-8)构成,内容是字符序列 | 由比特流构成,内容是字节序列,形态完全由程序定义 |
| 存储效率 | 较低 ,数字等类型需转为字符串,占用更多空间 | 非常高,数据按内存格式存储,紧凑无冗余 |
| 追加操作 | 天然支持,每行独立,追加不影响旧数据。 | 严格禁止。会破坏文件结构,导致读取失败 |
四、结果展示
- 文本文件存储 (data.txt)
数据以CSV格式存储,结构清晰,易于人工查看和编辑。

- 二进制文件存储 (data1.txt)
数据以二进制形式存储,内容无法直接阅读,但保证了数据的高效与安全。

五、过程回顾
5.1 问题一:二进制文件的“空”并非空白
现象:删除所有数据后,二进制文件大小不为零
分析与反思:
二进制序列化文件并非在无数据时变为空白。ObjectOutputStream 在创建或覆盖文件时,会首先写入一个魔数和版本标识符作为文件头。
这个文件头是反序列化时识别文件格式的依据,因此一个“空”的二进制文件实际上包含了元数据,而非零字节。

5.2 问题二:二进制文件的追加陷阱
现象:模仿文本文件追加数据后,读取时抛出 StreamCorruptedException
分析与反思:
在实现二进制文件存储时,一个常见的误区是模仿文本文件的追加模式。然而,这种做法对于Java序列化文件是致命的。
ObjectOutputStream 在初始化时会向文件写入一个序列化协议头(包含魔数和版本号)。当使用追加模式时,每次写入都会在文件末尾重
复添加这个协议头,从而在数据流中制造了非法的“断点”。这导致 ObjectInputStream 在反序列化时因无法解析预期的对象数据而抛出
StreamCorruptedException,使得整个文件数据无法被正确读取。
解决方案:采用“读-改-写”模式。每次更新数据时,都需将文件全部内容读入内存,修改后,再完整地覆盖写回文件。这确保了文件始终只有一个合法的文件头。


浙公网安备 33010602011771号