文本文件与基于二进制文件的存储的学生管理系统

文本文件与基于二进制文件的存储的学生管理系统


一、项目介绍

本项目旨在对学生管理系统进行优化,重点在于实现数据持久化。我们通过支持文本与二进制两种存储格式,为系统提供了灵活且可靠的长

期数据保存方案。


二、包结构

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 代码分析

  1. 静态工具类模式

  2. 装饰器模式在IO流中的应用

  • FileOutputStream: 打开一个到文件的连接,并以字节的形式向文件中写入原始数据

  • OutputStreamWriter: 是一个字符到字节的桥梁,它装饰了 FileOutputStream,使其具备了处理字符的能力

  • BufferedWriter: 修饰了 OutputStreamWriter ,为其增加了缓冲区

  1. Java序列化机制
  • ObjectOutputStream: 将Java对象序列化,并写入到二进制文件中

  • ObjectInputStream: 反序列化的核心,将之前通过 ObjectOutputStream 序列化的字节流还原成原始的Java对象

3.3 对比分析

对比 文本文件 二进制文件
读取方式 人类可读,需要用文本编辑器或按字符编码读取 机器可读,人眼看过去是乱码,必须用专门的程序按二进制格式解析
文件形态 由字符编码(如ASCII, UTF-8)构成,内容是字符序列 由比特流构成,内容是字节序列,形态完全由程序定义
存储效率 较低 ,数字等类型需转为字符串,占用更多空间 非常高,数据按内存格式存储,紧凑无冗余
追加操作 天然支持,每行独立,追加不影响旧数据。 严格禁止。会破坏文件结构,导致读取失败

四、结果展示

  • 文本文件存储 (data.txt)
    数据以CSV格式存储,结构清晰,易于人工查看和编辑。

image

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

image

五、过程回顾

5.1 问题一:二进制文件的“空”并非空白

现象:删除所有数据后,二进制文件大小不为零

分析与反思:

二进制序列化文件并非在无数据时变为空白。ObjectOutputStream 在创建或覆盖文件时,会首先写入一个魔数和版本标识符作为文件头。

这个文件头是反序列化时识别文件格式的依据,因此一个“空”的二进制文件实际上包含了元数据,而非零字节。

image

5.2 问题二:二进制文件的追加陷阱

现象:模仿文本文件追加数据后,读取时抛出 StreamCorruptedException

分析与反思:

在实现二进制文件存储时,一个常见的误区是模仿文本文件的追加模式。然而,这种做法对于Java序列化文件是致命的。

ObjectOutputStream 在初始化时会向文件写入一个序列化协议头(包含魔数和版本号)。当使用追加模式时,每次写入都会在文件末尾重

复添加这个协议头,从而在数据流中制造了非法的“断点”。这导致 ObjectInputStream 在反序列化时因无法解析预期的对象数据而抛出

StreamCorruptedException,使得整个文件数据无法被正确读取。

解决方案:采用“读-改-写”模式。每次更新数据时,都需将文件全部内容读入内存,修改后,再完整地覆盖写回文件。这确保了文件始终只有一个合法的文件头。

a4c7591710fa71c6e8c3c352e05de4ad

posted @ 2025-11-25 00:07  穗和  阅读(9)  评论(1)    收藏  举报