楼层: 首页/ 软件技术/ Java 基础/ 小项目实战:学生成绩管理系统(控制台版)
11

小项目实战:学生成绩管理系统(控制台版)

Mini Project · Collections + IO + Threads

学了一堆零件,拼个真东西才算数。下面这个控制台小程序,把前面的集合、record、IO、Scanner 输入、多线程全串起来:能添加学生、按分数排序、存盘到文件、再加载回来。代码不长,建议逐行敲一遍跑通。

StudentManager.java:一个能跑的完整小项目

import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;

// 用 record 定义学生:姓名、学号、分数
record Student(String id, String name, double score) {}

public class StudentManager {
    private static final List<Student> students = new ArrayList<>();
    private static final Path FILE = Path.of("students.txt");
    private static final Scanner SC = new Scanner(System.in);

    public static void main(String[] args) throws Exception {
        loadFromFile();              // 启动时先读档
        while (true) {
            System.out.println("\n1 添加  2 列表  3 按分数排序  4 保存  5 退出");
            System.out.print("选:");
            int op = SC.nextInt();
            switch (op) {
                case 1 -> add();
                case 2 -> list();
                case 3 -> sortByScore();
                case 4 -> saveToFile();
                case 5 -> { saveToFile(); System.exit(0); }
                default -> System.out.println("没这个选项");
            }
        }
    }

    static void add() {
        System.out.print("> 学号 姓名 分数:");
        students.add(new Student(SC.next(), SC.next(), SC.nextDouble()));
    }

    static void list() {
        students.forEach(System.out::println);
    }

    static void sortByScore() {
        students.stream()
            .sorted(Comparator.comparingDouble(Student::score).reversed())
            .forEach(System.out::println);
    }

    // 保存:把每个学生拼成一行,写入文件
    static void saveToFile() throws Exception {
        List<String> lines = students.stream()
            .map(s -> s.id() + "," + s.name() + "," + s.score())
            .toList();
        Files.write(FILE, lines, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
        System.out.println("已保存 " + students.size() + " 条");
    }

    // 加载:读文件,每行用逗号拆成三个字段
    static void loadFromFile() throws Exception {
        if (!Files.exists(FILE)) return;
        for (String line : Files.readAllLines(FILE)) {
            String[] parts = line.split(",");
            students.add(new Student(parts[0], parts[1], Double.parseDouble(parts[2])));
        }
    }
}

跑起来大概长这样(控制台输出)

1 添加  2 列表  3 按分数排序  4 保存  5 退出
选:1
> 学号 姓名 分数:001 小明 92
选:1
> 学号 姓名 分数:002 小红 88
选:3
Student[id=001, name=小明, score=92.0]
Student[id=002, name=小红, score=88.0]
选:4
已保存 2 条
选:5

论这个小项目藏了哪些知识点

看着简单,其实每一步都在考试点:record 定义数据、ArrayList 存数据、switch 新写法做菜单、Stream + Comparator 排序、Files.readAllLines / write 做持久化、split 解析文本。把它敲通,Java 基础就算立住了。

扩展练习:① 加一个"按学号删除"功能;② 用虚拟线程在保存时后台打印"保存中..."动画;③ 把文件格式换成 JSON(引入一个第三方库)。每加一个功能,你就多踩一个真实的坑。

章末面试 · 综合实战(3 题)

1.(设计题)这个小项目如果要改成多线程并发保存文件,要注意什么?

查看答案

答案:students 是共享静态 List,多线程写会竞态。要么用线程安全集合(CopyOnWriteArrayList),要么加锁。保存到文件时也要防两个线程同时写同一个文件。

2.(扩展题)要加"按学号删除"功能,怎么写?

查看答案

答案:students.removeIf(s -> s.id().equals(targetId)),用 removeIf 而不是 for-each 里 remove,避免并发修改异常。

3.(思考题)为什么学生类用 record 不用普通类?

查看答案

答案:学生是纯数据载体,不需要可变状态。record 一行搞定,自动生成 toString/equals/带 getter,代码量少一半。