学生信息管理系统 DAO 模式改造报告
1. DAO 接口方法
DAO(Data Access Object)接口定义了学生数据操作的统一规范,所有数据存取模式都需实现该接口,保证操作方式的一致性。
public interface StudentDAO {void addStudent(Student student);void removeStudent(Student student);List<Student> getStudents();List<Student> searchByName(String name);List<Student> searchByMajor(String major);List<Student> searchByGpa(double gpa);}
1.1 addStudent
- 
作用:向数据存储中添加一个学生对象 
- 
说明:接收 Student实体对象,将其存入对应的数据载体(List 或文件)中
1.2 removeStudent
- 
作用:从数据存储中移除指定学生对象 
- 
说明:接收 Student实体对象,从数据载体中删除该对象
1.3 getStudents
- 
作用:获取所有学生数据 
- 
说明:返回存储中所有学生的列表,用于展示全部数据 
1.4 searchByName
- 
作用:按姓名搜索学生 
- 
说明:接收姓名字符串,返回所有姓名匹配的学生列表 
1.5 searchByMajor
- 
作用:按专业搜索学生 
- 
说明:接收专业字符串,返回所有专业匹配的学生列表 
1.6 searchByGpa
- 
作用:按 GPA 搜索学生 
- 
说明:接收 GPA 数值,返回所有 GPA 匹配的学生列表 
2. List 模式(内存存储)
2.1 List 模式的核心逻辑
List 模式以内存中的ArrayList作为数据载体,数据仅在程序运行时存在,程序退出后数据丢失。
public class StudentListDAO implements StudentDAO {private List\<Student> students;  // 核心数据存储载体public StudentListDAO() {students = new ArrayList<>();  // 初始化内存列表}// 接口实现方法...}
2.2 List 模式的接口应用
通过实现StudentDAO接口,完成内存中数据的操作:
public class StudentListDAO implements StudentDAO {// 成员变量及构造方法见2.1@Overridepublic void addStudent(Student student) { ... }@Overridepublic void removeStudent(Student student) { ... }@Overridepublic List\<Student> getStudents() { ... }@Overridepublic List\<Student> searchByName(String name) { ... }@Overridepublic List\<Student> searchByMajor(String major) { ... }@Overridepublic List\<Student> searchByGpa(double gpa) { ... }}
2.3 List 模式方法说明
- addStudent
@Overridepublic void addStudent(Student student) {students.add(student);}
- 
作用:向内存列表中添加学生 
- 
逻辑:直接调用 ArrayList的add()方法存储学生对象
- removeStudent
@Overridepublic void removeStudent(Student student) {students.remove(student);}
- 
作用:从内存列表中删除学生 
- 
逻辑:调用 ArrayList的remove()方法移除指定学生对象
- getStudents
@Overridepublic List\<Student> getStudents() {return students;}
- 
作用:获取所有学生数据 
- 
逻辑:直接返回内存列表的引用 
- searchByName
@Overridepublic List\<Student> searchByName(String name) {List\<Student> result = new ArrayList<>();for (Student student : students) {if (student.getName().equals(name)) {result.add(student);}}return result;}
- 
作用:按姓名筛选学生 
- 
逻辑:遍历内存列表,将姓名匹配的学生存入结果列表并返回 
- searchByMajor
@Overridepublic List\<Student> searchByMajor(String major) {List\<Student> result = new ArrayList<>();for (Student student : students) {if (student.getMajor().equals(major)) {result.add(student);}}return result;}
- 
作用:按专业筛选学生 
- 
逻辑:遍历内存列表,将专业匹配的学生存入结果列表并返回 
- searchByGpa
@Overridepublic List\<Student> searchByGpa(double gpa) {List\<Student> result = new ArrayList<>();for (Student student : students) {if (student.getGpa() == gpa) {result.add(student);}}return result;}
- 
作用:按 GPA 筛选学生 
- 
逻辑:遍历内存列表,将 GPA 匹配的学生存入结果列表并返回 
3. 文本文件模式(持久化存储)
3.1 文本文件模式的核心逻辑
文本文件模式以students.txt文件作为数据载体,通过文件 IO 实现数据的持久化存储(程序退出后数据不丢失)。核心逻辑是 "加载 - 操作 - 保存":初始化时从文件加载数据到内存列表,操作后将内存列表同步到文件。
public class StudentFileDAO implements StudentDAO {private static final String FILE\_PATH = "students.txt";  // 文件路径private List\<Student> students;  // 内存缓存列表public StudentFileDAO() {students = loadFromFile();  // 初始化时从文件加载数据}// 从文件加载数据到内存private List\<Student> loadFromFile() { ... }// 将内存数据保存到文件private void saveToFile() { ... }// 接口实现方法...}
3.2 文本文件模式的接口应用
实现StudentDAO接口,通过文件 IO 完成数据的持久化操作:
public class StudentFileDAO implements StudentDAO {// 成员变量、构造方法及工具方法见3.1@Overridepublic void addStudent(Student student) { ... }@Overridepublic void removeStudent(Student student) { ... }@Overridepublic List\<Student> getStudents() { ... }@Overridepublic List\<Student> searchByName(String name) { ... }@Overridepublic List\<Student> searchByMajor(String major) { ... }@Overridepublic List\<Student> searchByGpa(double gpa) { ... }}
3.3 文本文件模式方法说明
- loadFromFile(工具方法)
private List\<Student> loadFromFile() {List\<Student> list = new ArrayList<>();File file = new File(FILE\_PATH);if (!file.exists()) {return list;  // 文件不存在则返回空列表}try (BufferedReader br = new BufferedReader(new FileReader(file))) {String line;while ((line = br.readLine()) != null) {// 解析文件内容为Student对象String\[] parts = line.split(",");if (parts.length == 6) {String name = parts\[0];int age = Integer.parseInt(parts\[1]);String gender = parts\[2];String id = parts\[3];String major = parts\[4];double gpa = Double.parseDouble(parts\[5]);list.add(new Student(name, age, gender, id, major, gpa));}}} catch (IOException e) {e.printStackTrace();}return list;}
- 
作用:从文件读取数据并转换为学生列表 
- 
逻辑:使用 BufferedReader读取文件,按行解析字符串为Student对象
- saveToFile(工具方法)
private void saveToFile() {try (BufferedWriter bw = new BufferedWriter(new FileWriter(FILE\_PATH))) {for (Student student : students) {// 将Student对象转换为字符串并写入文件String line = String.join(",",student.getName(),String.valueOf(student.getAge()),student.getGender(),student.getId(),student.getMajor(),String.valueOf(student.getGpa()));bw.write(line);bw.newLine();}} catch (IOException e) {e.printStackTrace();}}
- 
作用:将内存中的学生列表写入文件 
- 
逻辑:遍历学生列表,将对象属性拼接为字符串(逗号分隔),通过 BufferedWriter写入文件
- addStudent
@Overridepublic void addStudent(Student student) {students.add(student);saveToFile();  // 添加后同步到文件}
- 
作用:添加学生并持久化到文件 
- 
逻辑:先添加到内存列表,再调用 saveToFile()同步到文件
- removeStudent
@Overridepublic void removeStudent(Student student) {students.remove(student);saveToFile();  // 删除后同步到文件}
- 
作用:删除学生并更新文件 
- 
逻辑:先从内存列表删除,再调用 saveToFile()同步到文件
- getStudents
@Overridepublic List\<Student> getStudents() {return students;}
- 
作用:获取所有学生数据 
- 
逻辑:返回内存缓存的学生列表(已从文件加载) 
- searchByName、searchByMajor、searchByGpa
- 
实现逻辑与 List 模式完全一致(基于内存列表遍历筛选) 
- 
区别:数据来源是从文件加载的内存列表,而非直接初始化的空列表 
3.4 文本文件模式的特殊用法
- 
try-with-resources 语法 自动关闭文件流,避免资源泄露: 
try (BufferedReader br = new BufferedReader(new FileReader(file))) {// 读取操作} catch (IOException e) {e.printStackTrace();}
- 
字符串分割与解析 使用 split(",")分割文件中的字符串,并通过Integer.parseInt()、Double.parseDouble()转换数据类型:
String\[] parts = line.split(",");int age = Integer.parseInt(parts\[1]);double gpa = Double.parseDouble(parts\[5]);
- 
文件不存在处理 初始化时检查文件是否存在,不存在则返回空列表,避免空指针异常: 
File file = new File(FILE\_PATH);if (!file.exists()) {return list;}
4. 主程序修改部分
4.1 主程序的核心修改
- 
引入 DAO 接口依赖 主程序不再直接操作 List,而是通过 StudentDAO接口调用方法:
StudentDAO studentDAO;  // 声明接口类型,不依赖具体实现
- 
支持模式选择与切换 允许用户选择数据存储模式,并在运行中切换: 
// 初始化选择模式System.out.println("请选择数据存储模式:");System.out.println("1. 内存模式(List)");System.out.println("2. 文件模式");int modeChoice = scanner.nextInt();studentDAO = modeChoice == 1 ? new StudentListDAO() : new StudentFileDAO();// 运行中切换模式(菜单选项7)case 7:System.out.println("请选择数据存储模式:");modeChoice = scanner.nextInt();studentDAO = modeChoice == 1 ? new StudentListDAO() : new StudentFileDAO();System.out.println("模式切换成功");break;
- 
统一操作接口 所有数据操作通过 studentDAO接口完成,与具体存储模式解耦:
// 添加学生studentDAO.addStudent(student);// 搜索学生List\<Student> searchResults = studentDAO.searchByName(searchName);// 显示所有学生List\<Student> studentList = studentDAO.getStudents();
4.2 修改的好处与作用
- 
实现数据存储模式的自由切换 用户可根据需求选择内存模式(临时数据,速度快)或文件模式(持久化数据,可保存),无需修改核心逻辑。 
- 
降低代码耦合度 主程序仅依赖 StudentDAO接口,不关心具体实现,符合 "依赖倒置原则"。
- 
提高扩展性 若后续需添加数据库存储模式,只需新增 StudentDBDAO实现接口,主程序无需修改。
- 
统一操作方式 两种模式的操作接口完全一致,用户无需学习新的操作逻辑。 
5. 拓展:DAO 模式详解
5.1 DAO 模式的含义与作用
DAO 模式是一种数据访问层设计模式,用于隔离业务逻辑与数据访问逻辑。其核心思想是:
- 
定义 DAO接口:规范数据操作方法
- 
实现 DAO实现类:针对不同数据存储方式(如内存、文件、数据库)提供具体实现
- 
通过 接口调用:业务逻辑层仅依赖接口,不依赖具体存储方式
作用:
- 
分离数据访问与业务逻辑,降低耦合度 
- 
统一数据操作接口,简化业务层代码 
- 
便于切换数据存储方式,提高系统扩展性 
5.2 本系统中 DAO 模式的优势
- 
多存储模式支持:通过实现不同 DAO,轻松支持 List(内存)和文本文件(持久化)两种模式。 
- 
操作一致性:无论使用哪种存储模式,添加、删除、查询的方法名和参数完全一致。 
- 
可维护性提升:数据访问逻辑集中在 DAO 实现类中,修改存储方式时无需改动主程序。 
- 
可扩展性增强:如需添加数据库存储,只需新增 StudentDBDAO实现接口,主程序代码不变。
5.3 适合使用 DAO 模式的系统
- 
需要多种数据存储方式的系统:如同时支持内存缓存、文件存储、数据库存储的系统。 
- 
数据访问逻辑复杂的系统:通过 DAO 封装数据操作,简化业务层代码。 
- 
需要长期维护和扩展的系统:DAO 模式的低耦合特性便于后期功能迭代。 
- 
分层架构的系统:在 MVC 等分层架构中,DAO 作为数据访问层,可清晰划分各层职责。 
