一、结对编程初体验
1.1 什么是结对编程?
结对编程(Pair Programming)是敏捷开发中的核心实践:两位程序员并肩坐在一台电脑前,一人担任“驾驶员”(负责敲代码),另一人担任“导航员”(负责思考方向、审查代码、发现潜在问题)。两人定期轮换角色。
这种看似“浪费人力”的方式,实则能带来:
代码质量提升:双人审查,bug更少
知识传递:经验共享,技能互补
减少干扰:两人互相督促,专注度更高

1.2 我们的组合

角色 成员 主要职责
驾驶员 2452621 代码实现、调试运行
导航员 2452622 架构设计、逻辑审查、文档记录

约定:每30分钟轮换一次角色,确保双方都能体验两种视角。

二、项目背景与需求
2.1 需求分析
我们要开发一个简易在线考试系统,核心功能如下:

deepseek_mermaid_20260417_290088
2.2 技术选型
语言:Java(简单、易上手)
存储:内存 + 文件持久化
交互:命令行界面(CLI)
开发工具:IntelliJ IDEA + Git

三、开发过程全记录
3.1 第一阶段:搭建骨架(30分钟)
导航员的思路:
先设计核心类的结构,使用面向对象的思想。题目作为抽象基类,三种题型分别继承。

点击查看代码
// 导航员画出类图,驾驶员实现代码
abstract class Question {
    protected String text;
    protected int score;
    protected Difficulty difficulty;
    public abstract boolean checkAnswer(String userInput);
    public abstract String getDisplayString();
}

class SingleChoice extends Question { /* 实现 */ }
class MultiChoice extends Question { /* 实现 */ }
class TrueFalse extends Question { /* 实现 */ }
结对收获:导航员提前发现了多选答案格式的问题,避免了后期返工。

3.2 第二阶段:题目管理模块(45分钟)
驾驶员小王实现了题目的增删改查功能。导航员小李发现了几个问题:

点击查看代码
// 导航员发现问题:选项和答案的存储格式需要统一
// 建议:使用List<String>存储选项,答案也统一为字符串

// 驾驶员修改后
public void addQuestion(Scanner scanner) {
    // 统一使用逗号分隔输入
    System.out.print("请输入选项(用逗号分隔): ");
    List<String> options = Arrays.asList(scanner.nextLine().split(","));
}
小插曲:测试时发现多选题答案顺序不一致导致判分错误,导航员建议使用Set进行比较:
点击查看代码
// 导航员建议使用Set,忽略顺序
Set<String> userSet = new HashSet<>(userAnswers);
Set<String> correctSet = new HashSet<>(correctAnswers);
return userSet.equals(correctSet);  // 完美解决!

3.3 第三阶段:考生答题模块(60分钟)
这是最核心的部分,我们遇到了几个挑战:

挑战1:限时功能如何实现?
导航员提议:使用ScheduledExecutorService实现倒计时自动交卷。

点击查看代码
// 驾驶员实现
timerTask = scheduler.schedule(() -> {
    if (currentUser != null) {
        System.out.println("\n时间到!系统自动交卷...");
        autoGradeAndReport();
    }
}, EXAM_DURATION_MINUTES, TimeUnit.MINUTES);
挑战2:实时保存答案 我们设计了答案暂存机制:
点击查看代码
// 每个考生的答案保存在Map中
private static Map<String, List<String>> userAnswers = new HashMap<>();

// 每次作答立即更新
currentAnswers.set(qid, answer.trim());
System.out.println("✓ 答案已实时保存。");
结对反思:这里导航员发现了一个潜在bug——如果考试中途程序崩溃,答案会丢失。我们决定增加文件备份功能。

3.4 第四阶段:自动判分模块(45分钟)
判分逻辑需要考虑不同题型的差异:

点击查看代码
// 导航员设计判分流程
for (int i = 0; i < questions.size(); i++) {
    Question q = questions.get(i);
    boolean isCorrect = q.checkAnswer(userAnswers.get(i));
    if (isCorrect) totalScore += q.getScore();
    // 记录错题用于报告
}
创意时刻:我们增加了成绩评级和错题解析:
点击查看代码
╔════════════════════════════════════════╗
║           考试成绩报告                  ║
║ 考生: 张三                              ║
║ 总分: 85 / 100                         ║
║ 正确率: 85%                             ║
║ 评级: 良好 (B)                          ║
╠════════════════════════════════════════╣
║           错题解析                      ║
║ 3. 哪些是质数?                         ║
║    你的答案: 2,4                        ║
║    正确答案: [2, 5]                     ║
╚════════════════════════════════════════╝

3.5 第五阶段:增强功能(60分钟)
基础功能完成后,我们决定自由发挥,增加以下特性:

功能 实现者 耗时 效果
难度等级 小李 15min ⭐⭐⭐
随机抽题 小王 20min 🎲
成绩排名 小李 15min 🏆
倒计时提醒 小王 10min
历史记录 共同 20min 💾

四、关键代码解析
4.1 抽象类设计(多态的魅力)

点击查看代码
abstract class Question {
    protected String text;
    protected int score;
    protected Difficulty difficulty;
    
    // 核心方法:判分逻辑由子类实现
    public abstract boolean checkAnswer(String userInput);
    public abstract String getCorrectAnswerDisplay();
}

// 三种题型各自实现
// 单选题:直接字符串比较
// 多选题:集合比较(忽略顺序)
// 判断题:处理多种输入格式(正确/对/true)
4.2 随机抽题算法
点击查看代码
private static List<Question> selectRandomQuestions() {
    List<Question> selected = new ArrayList<>();
    List<Question> available = new ArrayList<>(questionBank);
    Collections.shuffle(available);  // 打乱顺序
    
    int count = Math.min(EXAM_QUESTION_COUNT, available.size());
    for (int i = 0; i < count; i++) {
        selected.add(available.get(i));
    }
    return selected;
}
4.3 倒计时与自动交卷
点击查看代码
// 定时任务:自动交卷
timerTask = scheduler.schedule(() -> {
    if (currentUser != null) {
        autoGradeAndReport();
    }
}, EXAM_DURATION_MINUTES, TimeUnit.MINUTES);

// 同时设置提醒任务
scheduler.schedule(() -> {
    System.out.println("\n⚠️ 还剩1分钟!");
}, EXAM_DURATION_MINUTES - 1, TimeUnit.MINUTES);

五、遇到的问题与解决方案

问题 发现者 解决方案 时间成本
多选题答案顺序导致判分错误 导航员 使用Set集合比较 5min
程序崩溃答案丢失 导航员 增加文件持久化备份 20min
倒计时线程未正确关闭 驾驶员 使用ScheduledFuture.cancel() 10min
判断题输入格式不统一 驾驶员 支持多种同义词(对/正确/true) 10min

经验教训:导航员的角色至关重要——驾驶员专注于实现,导航员则能发现思维盲区。

六、结对编程的心得体会
6.1 做得好的地方
✅ 角色轮换:每30分钟轮换,双方都保持了新鲜感
✅ 即时沟通:遇到问题立即讨论,而不是各自埋头
✅ 测试驱动:每写完一个功能就手动测试,bug及时发现
✅ 代码审查:每段代码都被两人审视过

6.2 可以改进的地方
🔧 前期设计不足:应该花更多时间做架构设计
🔧 缺乏单元测试:手动测试效率较低,可以引入JUnit
🔧 代码注释不够:部分复杂逻辑缺少注释说明

七、项目成果展示
7.1完整代码

点击查看代码
// 文件:Main.java
// 简易在线考试系统 - 增强版(含历史记录、难度等级、随机抽题、成绩排名等功能)
import java.util.*;
import java.util.concurrent.*;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

// 考试系统主类
class ExamSystem {
    // 数据存储
    private static List<Question> questionBank = new ArrayList<>();
    private static Map<String, List<String>> userAnswers = new HashMap<>(); // 考生答案暂存
    private static Map<String, List<ExamRecord>> examHistory = new HashMap<>(); // 考试历史记录
    private static String currentUser = null;
    private static List<Question> currentExamQuestions = null; // 当前考试的题目(随机抽取)
    private static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private static ScheduledFuture<?> timerTask = null;
    private static long examStartTime = 0;
    private static final int EXAM_DURATION_MINUTES = 5; // 限时5分钟
    private static final int EXAM_QUESTION_COUNT = 5; // 每次考试题目数量

    public static void main(String[] args) {
        initSampleData(); // 初始化一些题目
        loadExamHistory(); // 加载历史记录
        Scanner scanner = new Scanner(System.in);
        boolean running = true;
        while (running) {
            if (currentUser == null) {
                System.out.println("\n╔════════════════════════════════╗");
                System.out.println("║     简易在线考试系统 v2.0     ║");
                System.out.println("╚════════════════════════════════╝");
                System.out.println("1. 管理员登录(题目管理)");
                System.out.println("2. 考生登录(参加考试)");
                System.out.println("3. 查看成绩排名");
                System.out.println("4. 退出系统");
                System.out.print("请选择: ");
                String choice = scanner.nextLine();
                switch (choice) {
                    case "1":
                        adminMenu(scanner);
                        break;
                    case "2":
                        studentLogin(scanner);
                        break;
                    case "3":
                        showRanking();
                        break;
                    case "4":
                        running = false;
                        saveExamHistory(); // 保存历史记录
                        System.out.println("感谢使用!");
                        break;
                    default:
                        System.out.println("无效输入,请重新选择。");
                }
            } else {
                // 考生考试界面
                examMenu(scanner);
            }
        }
        scanner.close();
        scheduler.shutdown();
    }

    // 初始化样例题目(小学数学,带难度等级)
    private static void initSampleData() {
        // 简单题目
        questionBank.add(new SingleChoice("1+1=?", Arrays.asList("1", "2", "3", "4"), "2", 2, Difficulty.EASY));
        questionBank.add(new SingleChoice("5-3=?", Arrays.asList("1", "2", "3", "4"), "2", 2, Difficulty.EASY));
        questionBank.add(new TrueFalse("3 + 5 = 8", true, 2, Difficulty.EASY));
        
        // 中等题目
        questionBank.add(new MultiChoice("下列哪些数字是偶数?", Arrays.asList("1", "2", "3", "4"), Arrays.asList("2","4"), 4, Difficulty.MEDIUM));
        questionBank.add(new SingleChoice("8÷2=?", Arrays.asList("2", "3", "4", "5"), "4", 3, Difficulty.MEDIUM));
        questionBank.add(new TrueFalse("7 × 6 = 42", true, 3, Difficulty.MEDIUM));
        
        // 困难题目
        questionBank.add(new MultiChoice("哪些是质数?", Arrays.asList("2", "4", "5", "6"), Arrays.asList("2","5"), 4, Difficulty.HARD));
        questionBank.add(new SingleChoice("12 × 13 = ?", Arrays.asList("144", "156", "146", "166"), "156", 5, Difficulty.HARD));
        questionBank.add(new TrueFalse("11 × 11 = 121", true, 5, Difficulty.HARD));
    }

    // 随机抽取题目
    private static List<Question> selectRandomQuestions() {
        List<Question> selected = new ArrayList<>();
        List<Question> available = new ArrayList<>(questionBank);
        Collections.shuffle(available);
        
        int count = Math.min(EXAM_QUESTION_COUNT, available.size());
        for (int i = 0; i < count; i++) {
            selected.add(available.get(i));
        }
        return selected;
    }

    // 显示成绩排名
    private static void showRanking() {
        System.out.println("\n========== 考试成绩排名 ==========");
        List<ExamRecord> allRecords = new ArrayList<>();
        for (List<ExamRecord> records : examHistory.values()) {
            allRecords.addAll(records);
        }
        
        if (allRecords.isEmpty()) {
            System.out.println("暂无考试记录");
            return;
        }
        
        // 按总分排序
        allRecords.sort((a, b) -> Integer.compare(b.totalScore, a.totalScore));
        
        System.out.println("排名\t考生\t总分\t正确率\t考试时间");
        System.out.println("----------------------------------------");
        int rank = 1;
        for (ExamRecord record : allRecords) {
            System.out.printf("%d\t%s\t%d\t%d%%\t%s\n", 
                rank++, record.studentName, record.totalScore, 
                record.correctRate, record.examTime);
        }
        System.out.println("========================================");
    }

    // 管理员菜单
    private static void adminMenu(Scanner scanner) {
        boolean admin = true;
        while (admin) {
            System.out.println("\n--- 题目管理 ---");
            System.out.println("1. 添加题目");
            System.out.println("2. 删除题目");
            System.out.println("3. 修改题目");
            System.out.println("4. 查看所有题目");
            System.out.println("5. 按难度查看题目");
            System.out.println("6. 返回主菜单");
            System.out.print("请选择: ");
            String choice = scanner.nextLine();
            switch (choice) {
                case "1":
                    addQuestion(scanner);
                    break;
                case "2":
                    deleteQuestion(scanner);
                    break;
                case "3":
                    modifyQuestion(scanner);
                    break;
                case "4":
                    listQuestions();
                    break;
                case "5":
                    listQuestionsByDifficulty(scanner);
                    break;
                case "6":
                    admin = false;
                    break;
                default:
                    System.out.println("无效选项");
            }
        }
    }

    private static void addQuestion(Scanner scanner) {
        System.out.println("选择题目类型: 1.单选题 2.多选题 3.判断题");
        String type = scanner.nextLine();
        System.out.print("请输入题干: ");
        String text = scanner.nextLine();
        System.out.print("请输入分值: ");
        int score = Integer.parseInt(scanner.nextLine());
        System.out.print("请输入难度(1.简单 2.中等 3.困难): ");
        int diffChoice = Integer.parseInt(scanner.nextLine());
        Difficulty difficulty = Difficulty.values()[diffChoice - 1];

        if (type.equals("1")) { // 单选
            System.out.print("请输入选项(用逗号分隔, 如: 1,2,3,4): ");
            String optsStr = scanner.nextLine();
            List<String> options = Arrays.asList(optsStr.split(","));
            System.out.print("请输入正确答案(选项内容): ");
            String correct = scanner.nextLine();
            questionBank.add(new SingleChoice(text, options, correct, score, difficulty));
            System.out.println("单选题添加成功!");
        } else if (type.equals("2")) { // 多选
            System.out.print("请输入选项(用逗号分隔): ");
            String optsStr = scanner.nextLine();
            List<String> options = Arrays.asList(optsStr.split(","));
            System.out.print("请输入正确答案(多个选项用逗号分隔): ");
            String correctStr = scanner.nextLine();
            List<String> correct = Arrays.asList(correctStr.split(","));
            questionBank.add(new MultiChoice(text, options, correct, score, difficulty));
            System.out.println("多选题添加成功!");
        } else if (type.equals("3")) { // 判断
            System.out.print("请输入正确答案(正确/错误): ");
            String boolStr = scanner.nextLine();
            boolean correct = boolStr.equals("正确");
            questionBank.add(new TrueFalse(text, correct, score, difficulty));
            System.out.println("判断题添加成功!");
        } else {
            System.out.println("无效类型");
        }
    }

    private static void deleteQuestion(Scanner scanner) {
        listQuestions();
        System.out.print("请输入要删除的题目编号: ");
        int idx = Integer.parseInt(scanner.nextLine()) - 1;
        if (idx >= 0 && idx < questionBank.size()) {
            questionBank.remove(idx);
            System.out.println("删除成功");
        } else {
            System.out.println("编号无效");
        }
    }

    private static void modifyQuestion(Scanner scanner) {
        listQuestions();
        System.out.print("请输入要修改的题目编号: ");
        int idx = Integer.parseInt(scanner.nextLine()) - 1;
        if (idx < 0 || idx >= questionBank.size()) {
            System.out.println("无效编号");
            return;
        }
        Question q = questionBank.get(idx);
        System.out.println("当前题目: " + q.getDisplayString());
        System.out.print("输入新题干(直接回车保留原样): ");
        String newText = scanner.nextLine();
        if (!newText.trim().isEmpty()) q.setText(newText);
        System.out.print("输入新分值(直接回车保留原值): ");
        String newScore = scanner.nextLine();
        if (!newScore.trim().isEmpty()) q.setScore(Integer.parseInt(newScore));

        // 根据类型修改选项/答案
        if (q instanceof SingleChoice) {
            System.out.print("新选项(逗号分隔, 回车跳过): ");
            String opts = scanner.nextLine();
            if (!opts.trim().isEmpty()) ((SingleChoice) q).setOptions(Arrays.asList(opts.split(",")));
            System.out.print("新正确答案(选项内容, 回车跳过): ");
            String ans = scanner.nextLine();
            if (!ans.trim().isEmpty()) ((SingleChoice) q).setCorrectAnswer(ans);
        } else if (q instanceof MultiChoice) {
            System.out.print("新选项(逗号分隔, 回车跳过): ");
            String opts = scanner.nextLine();
            if (!opts.trim().isEmpty()) ((MultiChoice) q).setOptions(Arrays.asList(opts.split(",")));
            System.out.print("新正确答案(多个选项逗号分隔, 回车跳过): ");
            String ans = scanner.nextLine();
            if (!ans.trim().isEmpty()) ((MultiChoice) q).setCorrectAnswers(Arrays.asList(ans.split(",")));
        } else if (q instanceof TrueFalse) {
            System.out.print("新正确答案(正确/错误, 回车跳过): ");
            String ans = scanner.nextLine();
            if (!ans.trim().isEmpty()) ((TrueFalse) q).setCorrect(ans.equals("正确"));
        }
        System.out.println("修改完成");
    }

    private static void listQuestions() {
        if (questionBank.isEmpty()) {
            System.out.println("暂无题目");
            return;
        }
        System.out.println("\n当前题库共 " + questionBank.size() + " 题:");
        for (int i = 0; i < questionBank.size(); i++) {
            System.out.println((i+1) + ". " + questionBank.get(i).getDisplayString());
        }
    }

    private static void listQuestionsByDifficulty(Scanner scanner) {
        System.out.print("选择难度(1.简单 2.中等 3.困难): ");
        int diff = Integer.parseInt(scanner.nextLine());
        Difficulty difficulty = Difficulty.values()[diff - 1];
        
        System.out.println("\n" + difficulty.getChinese() + "题目:");
        int count = 0;
        for (int i = 0; i < questionBank.size(); i++) {
            Question q = questionBank.get(i);
            if (q.getDifficulty() == difficulty) {
                System.out.println((i+1) + ". " + q.getBrief());
                count++;
            }
        }
        if (count == 0) {
            System.out.println("暂无此难度的题目");
        }
    }

    // 考生登录
    private static void studentLogin(Scanner scanner) {
        System.out.print("请输入考生姓名: ");
        String name = scanner.nextLine();
        currentUser = name;
        
        // 随机抽取题目
        currentExamQuestions = selectRandomQuestions();
        if (currentExamQuestions.isEmpty()) {
            System.out.println("题库为空,请管理员先添加题目!");
            currentUser = null;
            return;
        }
        
        userAnswers.putIfAbsent(currentUser, new ArrayList<>());
        List<String> ans = userAnswers.get(currentUser);
        ans.clear();
        for (int i = 0; i < currentExamQuestions.size(); i++) ans.add("");

        System.out.println("\n欢迎 " + currentUser + ",考试即将开始!");
        System.out.println("本次考试共 " + currentExamQuestions.size() + " 题,限时 " + EXAM_DURATION_MINUTES + " 分钟。");
        
        // 统计难度分布
        long easyCount = currentExamQuestions.stream().filter(q -> q.getDifficulty() == Difficulty.EASY).count();
        long mediumCount = currentExamQuestions.stream().filter(q -> q.getDifficulty() == Difficulty.MEDIUM).count();
        long hardCount = currentExamQuestions.stream().filter(q -> q.getDifficulty() == Difficulty.HARD).count();
        System.out.printf("难度分布:简单%d题 中等%d题 困难%d题\n", easyCount, mediumCount, hardCount);
        
        System.out.println("输入 'save' 可实时保存当前答案,输入 'submit' 提前交卷。");
        examStartTime = System.currentTimeMillis();

        // 启动倒计时提醒
        scheduleReminders();
        
        // 启动倒计时自动交卷
        timerTask = scheduler.schedule(() -> {
            if (currentUser != null) {
                System.out.println("\n⏰ 时间到!系统自动交卷...");
                autoGradeAndReport();
                currentUser = null;
                timerTask = null;
            }
        }, EXAM_DURATION_MINUTES, TimeUnit.MINUTES);
    }
    
    // 倒计时提醒
    private static void scheduleReminders() {
        scheduler.schedule(() -> {
            if (currentUser != null) {
                System.out.println("\n⚠️ 注意:考试还剩1分钟!请抓紧时间答题 ⚠️");
            }
        }, EXAM_DURATION_MINUTES - 1, TimeUnit.MINUTES);
        
        scheduler.schedule(() -> {
            if (currentUser != null) {
                System.out.println("\n⚠️ 最后30秒!⚠️");
            }
        }, EXAM_DURATION_MINUTES - 1, TimeUnit.MINUTES);
    }

    // 考生答题界面
    private static void examMenu(Scanner scanner) {
        List<String> currentAnswers = userAnswers.get(currentUser);
        boolean examActive = true;
        while (examActive && currentUser != null) {
            long elapsed = System.currentTimeMillis() - examStartTime;
            long remaining = EXAM_DURATION_MINUTES * 60 * 1000 - elapsed;
            if (remaining <= 0) {
                System.out.println("考试时间已结束");
                break;
            }
            int minutesLeft = (int)(remaining / 60000);
            int secondsLeft = (int)((remaining % 60000)/1000);
            
            System.out.printf("\n╔════════ %s 的答题卡 ════════╗\n", currentUser);
            System.out.printf("║    剩余时间:%02d:%02d            ║\n", minutesLeft, secondsLeft);
            System.out.println("╚════════════════════════════════╝");
            
            for (int i = 0; i < currentExamQuestions.size(); i++) {
                String status = currentAnswers.get(i).isEmpty() ? "❌未答" : "✅已答";
                String difficultyIcon = getDifficultyIcon(currentExamQuestions.get(i).getDifficulty());
                System.out.println((i+1) + ". " + difficultyIcon + " " + currentExamQuestions.get(i).getBrief() + " " + status);
            }
            
            System.out.println("\n请输入题号进行作答(1-" + currentExamQuestions.size() + "),或输入:");
            System.out.println("  'save' - 保存当前答案");
            System.out.println("  'list' - 查看所有题目");
            System.out.println("  'submit' - 交卷");
            System.out.print("命令: ");
            
            String input = scanner.nextLine();
            if (input.equalsIgnoreCase("save")) {
                System.out.println("✓ 当前答案已自动保存。");
            } else if (input.equalsIgnoreCase("list")) {
                showAllQuestions();
            } else if (input.equalsIgnoreCase("submit")) {
                System.out.println("确认交卷?(y/n): ");
                String confirm = scanner.nextLine();
                if (confirm.equalsIgnoreCase("y")) {
                    autoGradeAndReport();
                    examActive = false;
                    currentUser = null;
                    if (timerTask != null) timerTask.cancel(false);
                    break;
                }
            } else {
                try {
                    int qid = Integer.parseInt(input) - 1;
                    if (qid >= 0 && qid < currentExamQuestions.size()) {
                        Question q = currentExamQuestions.get(qid);
                        displayQuestion(q);
                        System.out.print("请输入你的答案: ");
                        String answer = scanner.nextLine();
                        if (answer.trim().isEmpty()) {
                            System.out.println("答案不能为空,此题未保存。");
                        } else {
                            currentAnswers.set(qid, answer.trim());
                            System.out.println("✓ 答案已实时保存。");
                        }
                    } else {
                        System.out.println("无效题号,请输入1-" + currentExamQuestions.size());
                    }
                } catch (NumberFormatException e) {
                    System.out.println("无效命令");
                }
            }
        }
        if (currentUser == null) System.out.println("返回主菜单。");
    }
    
    private static String getDifficultyIcon(Difficulty d) {
        switch(d) {
            case EASY: return "⭐";
            case MEDIUM: return "⭐⭐";
            case HARD: return "⭐⭐⭐";
            default: return "";
        }
    }
    
    private static void showAllQuestions() {
        System.out.println("\n========== 全部题目 ==========");
        for (int i = 0; i < currentExamQuestions.size(); i++) {
            Question q = currentExamQuestions.get(i);
            System.out.println((i+1) + ". " + getDifficultyIcon(q.getDifficulty()) + " " + q.getBrief());
        }
        System.out.println("==============================");
    }
    
    private static void displayQuestion(Question q) {
        System.out.println("\n┌─────────────────────────────────┐");
        if (q instanceof SingleChoice) {
            SingleChoice sq = (SingleChoice) q;
            System.out.println("│ 【单选题】" + sq.getBrief());
            System.out.println("├─────────────────────────────────┤");
            List<String> options = sq.getOptionsForDisplay();
            for (int j = 0; j < options.size(); j++) {
                System.out.println("│   " + (char)('A' + j) + ". " + options.get(j));
            }
        } else if (q instanceof MultiChoice) {
            MultiChoice mq = (MultiChoice) q;
            System.out.println("│ 【多选题】" + mq.getBrief());
            System.out.println("├─────────────────────────────────┤");
            List<String> options = mq.getOptionsForDisplay();
            for (int j = 0; j < options.size(); j++) {
                System.out.println("│   " + (char)('A' + j) + ". " + options.get(j));
            }
            System.out.println("├─────────────────────────────────┤");
            System.out.println("│ 提示:多选答案请用逗号分隔     │");
            System.out.println("│ 例如:A,B 或 A,B,C            │");
        } else if (q instanceof TrueFalse) {
            System.out.println("│ 【判断题】" + q.getBrief());
            System.out.println("├─────────────────────────────────┤");
            System.out.println("│   请输入:正确 或 错误         │");
        }
        System.out.println("└─────────────────────────────────┘");
    }

    // 自动判分并生成报告
    private static void autoGradeAndReport() {
        if (currentUser == null) return;
        List<String> answers = userAnswers.get(currentUser);
        int totalScore = 0;
        int maxPossible = 0;
        List<ResultDetail> details = new ArrayList<>();
        
        for (int i = 0; i < currentExamQuestions.size(); i++) {
            Question q = currentExamQuestions.get(i);
            maxPossible += q.getScore();
            String userAns = (i < answers.size()) ? answers.get(i) : "";
            boolean isCorrect = q.checkAnswer(userAns);
            if (isCorrect) totalScore += q.getScore();
            details.add(new ResultDetail(i+1, q.getBrief(), userAns, 
                q.getCorrectAnswerDisplay(), isCorrect, q.getScore(), q.getDifficulty()));
        }
        
        int correctRate = maxPossible == 0 ? 0 : totalScore * 100 / maxPossible;
        
        System.out.println("\n╔════════════════════════════════════════╗");
        System.out.println("║           考试成绩报告                  ║");
        System.out.println("╠════════════════════════════════════════╣");
        System.out.println("║ 考生: " + currentUser);
        System.out.println("║ 总分: " + totalScore + " / " + maxPossible);
        System.out.println("║ 正确率: " + correctRate + "%");
        
        // 评级
        String grade;
        if (correctRate >= 90) grade = "优秀 (A)";
        else if (correctRate >= 75) grade = "良好 (B)";
        else if (correctRate >= 60) grade = "及格 (C)";
        else grade = "不及格 (D)";
        System.out.println("║ 评级: " + grade);
        System.out.println("╠════════════════════════════════════════╣");
        System.out.println("║           错题解析                      ║");
        
        boolean hasWrong = false;
        for (ResultDetail rd : details) {
            if (!rd.correct) {
                hasWrong = true;
                System.out.println("║ " + rd.id + ". " + rd.questionText);
                System.out.println("║    你的答案: " + (rd.userAnswer.isEmpty()?"未作答":rd.userAnswer));
                System.out.println("║    正确答案: " + rd.correctAnswer);
                System.out.println("║    分值: " + rd.score + "分  难度: " + rd.difficulty.getChinese());
                System.out.println("║    ─────────────────────────────");
            }
        }
        
        if (!hasWrong) {
            System.out.println("║   🎉 恭喜!全做对了!太棒了! 🎉");
        }
        System.out.println("╚════════════════════════════════════════╝");
        
        // 保存考试记录
        ExamRecord record = new ExamRecord(currentUser, totalScore, maxPossible, 
            correctRate, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        examHistory.computeIfAbsent(currentUser, k -> new ArrayList<>()).add(record);
        saveExamHistory();
        
        // 清除考试状态
        currentUser = null;
        currentExamQuestions = null;
        if (timerTask != null) timerTask.cancel(false);
    }
    
    // 保存历史记录到文件
    private static void saveExamHistory() {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("exam_history.dat"))) {
            oos.writeObject(examHistory);
        } catch (IOException e) {
            // 忽略保存失败
        }
    }
    
    // 加载历史记录
    @SuppressWarnings("unchecked")
    private static void loadExamHistory() {
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("exam_history.dat"))) {
            examHistory = (Map<String, List<ExamRecord>>) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            examHistory = new HashMap<>();
        }
    }
}

// 难度枚举
enum Difficulty {
    EASY("简单"), MEDIUM("中等"), HARD("困难");
    
    private String chinese;
    Difficulty(String chinese) { this.chinese = chinese; }
    public String getChinese() { return chinese; }
}

// 考试记录类
class ExamRecord implements Serializable {
    String studentName;
    int totalScore;
    int maxScore;
    int correctRate;
    String examTime;
    
    ExamRecord(String name, int total, int max, int rate, String time) {
        this.studentName = name;
        this.totalScore = total;
        this.maxScore = max;
        this.correctRate = rate;
        this.examTime = time;
    }
}

// 题目抽象类
abstract class Question implements Serializable {
    protected String text;
    protected int score;
    protected Difficulty difficulty;
    
    public Question(String text, int score, Difficulty difficulty) { 
        this.text = text; 
        this.score = score;
        this.difficulty = difficulty;
    }
    public abstract boolean checkAnswer(String userInput);
    public abstract String getDisplayString();
    public abstract String getBrief();
    public abstract String getCorrectAnswerDisplay();
    public void setText(String text) { this.text = text; }
    public void setScore(int score) { this.score = score; }
    public int getScore() { return score; }
    public Difficulty getDifficulty() { return difficulty; }
}

// 单选题
class SingleChoice extends Question {
    private List<String> options;
    private String correctAnswer;
    
    public SingleChoice(String text, List<String> options, String correctAnswer, int score, Difficulty difficulty) {
        super(text, score, difficulty);
        this.options = options;
        this.correctAnswer = correctAnswer;
    }
    @Override
    public boolean checkAnswer(String userInput) { 
        String input = userInput.trim();
        if (input.length() == 1 && input.matches("[A-Za-z]")) {
            int idx = input.toUpperCase().charAt(0) - 'A';
            if (idx >= 0 && idx < options.size()) {
                return options.get(idx).equals(correctAnswer);
            }
        }
        return input.equalsIgnoreCase(correctAnswer); 
    }
    @Override
    public String getDisplayString() { 
        return "[" + difficulty.getChinese() + "][单选] " + text + " " + options + " 正确答案:" + correctAnswer; 
    }
    @Override
    public String getBrief() { return text; }
    @Override
    public String getCorrectAnswerDisplay() { return correctAnswer; }
    public void setOptions(List<String> opts) { this.options = opts; }
    public void setCorrectAnswer(String ans) { this.correctAnswer = ans; }
    public List<String> getOptionsForDisplay() { return options; }
}

// 多选题
class MultiChoice extends Question {
    private List<String> options;
    private List<String> correctAnswers;
    
    public MultiChoice(String text, List<String> options, List<String> correctAnswers, int score, Difficulty difficulty) {
        super(text, score, difficulty);
        this.options = options;
        this.correctAnswers = correctAnswers;
    }
    @Override
    public boolean checkAnswer(String userInput) {
        if (userInput.trim().isEmpty()) return false;
        String input = userInput.trim().toUpperCase();
        Set<String> userSet = new HashSet<>();
        
        if (input.matches("[A-Za-z](,[A-Za-z])*")) {
            String[] letters = input.split(",");
            for (String letter : letters) {
                int idx = letter.trim().charAt(0) - 'A';
                if (idx >= 0 && idx < options.size()) {
                    userSet.add(options.get(idx));
                }
            }
        } else {
            userSet.addAll(Arrays.asList(userInput.split(",")));
        }
        
        Set<String> correctSet = new HashSet<>(correctAnswers);
        return userSet.equals(correctSet);
    }
    @Override
    public String getDisplayString() { 
        return "[" + difficulty.getChinese() + "][多选] " + text + " " + options + " 正确答案:" + correctAnswers; 
    }
    @Override
    public String getBrief() { return text; }
    @Override
    public String getCorrectAnswerDisplay() { return correctAnswers.toString(); }
    public void setOptions(List<String> opts) { this.options = opts; }
    public void setCorrectAnswers(List<String> ans) { this.correctAnswers = ans; }
    public List<String> getOptionsForDisplay() { return options; }
}

// 判断题
class TrueFalse extends Question {
    private boolean correct;
    
    public TrueFalse(String text, boolean correct, int score, Difficulty difficulty) { 
        super(text, score, difficulty); 
        this.correct = correct; 
    }
    @Override
    public boolean checkAnswer(String userInput) {
        if (userInput.equalsIgnoreCase("正确") || userInput.equalsIgnoreCase("true") || userInput.equals("对"))
            return correct;
        else if (userInput.equalsIgnoreCase("错误") || userInput.equalsIgnoreCase("false") || userInput.equals("错"))
            return !correct;
        return false;
    }
    @Override
    public String getDisplayString() { 
        return "[" + difficulty.getChinese() + "][判断] " + text + " 正确答案:" + (correct?"正确":"错误"); 
    }
    @Override
    public String getBrief() { return text; }
    @Override
    public String getCorrectAnswerDisplay() { return correct?"正确":"错误"; }
    public void setCorrect(boolean c) { this.correct = c; }
}

// 成绩明细辅助类
class ResultDetail {
    int id;
    String questionText;
    String userAnswer;
    String correctAnswer;
    boolean correct;
    int score;
    Difficulty difficulty;
    
    ResultDetail(int id, String text, String userAns, String corrAns, boolean corr, int score, Difficulty difficulty) {
        this.id = id; 
        this.questionText = text; 
        this.userAnswer = userAns;
        this.correctAnswer = corrAns; 
        this.correct = corr; 
        this.score = score;
        this.difficulty = difficulty;
    }
}

// 公共主类
public class Main {
    public static void main(String[] args) {
        ExamSystem.main(args);
    }
}

7.2运行演示

点击查看代码
╔════════════════════════════════╗
║     简易在线考试系统 v2.0         ║
╚════════════════════════════════╝
1. 管理员登录(题目管理)
2. 考生登录(参加考试)
3. 查看成绩排名
4. 退出系统
请选择: 1

--- 题目管理 ---
1. 添加题目
2. 删除题目
3. 修改题目
4. 查看所有题目
5. 按难度查看题目
6. 返回主菜单
请选择: 1
选择题目类型: 1.单选题 2.多选题 3.判断题
1
请输入题干: 9+9=?
请输入分值: 2
请输入难度(1.简单 2.中等 3.困难): 1
请输入选项(用逗号分隔, 如: 1,2,3,4): 9,12,18,21
请输入正确答案(选项内容): 18
单选题添加成功!

--- 题目管理 ---
1. 添加题目
2. 删除题目
3. 修改题目
4. 查看所有题目
5. 按难度查看题目
6. 返回主菜单
请选择: 2

当前题库共 10 题:
1. [简单][单选] 1+1=? [1, 2, 3, 4] 正确答案:2
2. [简单][单选] 5-3=? [1, 2, 3, 4] 正确答案:2
3. [简单][判断] 3 + 5 = 8 正确答案:正确
4. [中等][多选] 下列哪些数字是偶数? [1, 2, 3, 4] 正确答案:[2, 4]
5. [中等][单选] 8÷2=? [2, 3, 4, 5] 正确答案:4
6. [中等][判断] 7 × 6 = 42 正确答案:正确
7. [困难][多选] 哪些是质数? [2, 4, 5, 6] 正确答案:[2, 5]
8. [困难][单选] 12 × 13 = ? [144, 156, 146, 166] 正确答案:156
9. [困难][判断] 11 × 11 = 121 正确答案:正确
10. [简单][单选] 9+9=? [9, 12, 18, 21] 正确答案:18
请输入要删除的题目编号: 1
删除成功

--- 题目管理 ---
1. 添加题目
2. 删除题目
3. 修改题目
4. 查看所有题目
5. 按难度查看题目
6. 返回主菜单
请选择: 3

当前题库共 9 题:
1. [简单][单选] 5-3=? [1, 2, 3, 4] 正确答案:2
2. [简单][判断] 3 + 5 = 8 正确答案:正确
3. [中等][多选] 下列哪些数字是偶数? [1, 2, 3, 4] 正确答案:[2, 4]
4. [中等][单选] 8÷2=? [2, 3, 4, 5] 正确答案:4
5. [中等][判断] 7 × 6 = 42 正确答案:正确
6. [困难][多选] 哪些是质数? [2, 4, 5, 6] 正确答案:[2, 5]
7. [困难][单选] 12 × 13 = ? [144, 156, 146, 166] 正确答案:156
8. [困难][判断] 11 × 11 = 121 正确答案:正确
9. [简单][单选] 9+9=? [9, 12, 18, 21] 正确答案:18
请输入要修改的题目编号: 9
当前题目: [简单][单选] 9+9=? [9, 12, 18, 21] 正确答案:18
输入新题干(直接回车保留原样): 
输入新分值(直接回车保留原值): 
新选项(逗号分隔, 回车跳过): 
新正确答案(选项内容, 回车跳过): 
修改完成

--- 题目管理 ---
1. 添加题目
2. 删除题目
3. 修改题目
4. 查看所有题目
5. 按难度查看题目
6. 返回主菜单
请选择: 4

当前题库共 9 题:
1. [简单][单选] 5-3=? [1, 2, 3, 4] 正确答案:2
2. [简单][判断] 3 + 5 = 8 正确答案:正确
3. [中等][多选] 下列哪些数字是偶数? [1, 2, 3, 4] 正确答案:[2, 4]
4. [中等][单选] 8÷2=? [2, 3, 4, 5] 正确答案:4
5. [中等][判断] 7 × 6 = 42 正确答案:正确
6. [困难][多选] 哪些是质数? [2, 4, 5, 6] 正确答案:[2, 5]
7. [困难][单选] 12 × 13 = ? [144, 156, 146, 166] 正确答案:156
8. [困难][判断] 11 × 11 = 121 正确答案:正确
9. [简单][单选] 9+9=? [9, 12, 18, 21] 正确答案:18

--- 题目管理 ---
1. 添加题目
2. 删除题目
3. 修改题目
4. 查看所有题目
5. 按难度查看题目
6. 返回主菜单
请选择: 5
选择难度(1.简单 2.中等 3.困难): 3

困难题目:
6. 哪些是质数?
7. 12 × 13 = ?
8. 11 × 11 = 121

--- 题目管理 ---
1. 添加题目
2. 删除题目
3. 修改题目
4. 查看所有题目
5. 按难度查看题目
6. 返回主菜单
请选择: 6

╔════════════════════════════════╗
║     简易在线考试系统 v2.0     ║
╚════════════════════════════════╝
1. 管理员登录(题目管理)
2. 考生登录(参加考试)
3. 查看成绩排名
4. 退出系统
请选择: 2
请输入考生姓名: yinuo

欢迎 yinuo,考试即将开始!
本次考试共 5 题,限时 5 分钟。
难度分布:简单2题 中等1题 困难2题
输入 'save' 可实时保存当前答案,输入 'submit' 提前交卷。

╔════════ yinuo 的答题卡 ════════╗
║    剩余时间:04:59            ║
╚════════════════════════════════╝
1. ⭐⭐⭐ 11 × 11 = 121 ❌未答
2. ⭐ 5-3=? ❌未答
3. ⭐ 3 + 5 = 8 ❌未答
4. ⭐⭐ 8÷2=? ❌未答
5. ⭐⭐⭐ 哪些是质数? ❌未答

请输入题号进行作答(1-5),或输入:
  'save' - 保存当前答案
  'list' - 查看所有题目
  'submit' - 交卷
命令: 1

┌─────────────────────────────────┐
│ 【判断题】11 × 11 = 121
├─────────────────────────────────┤
│   请输入:正确 或 错误         │
└─────────────────────────────────┘
请输入你的答案: 正确
✓ 答案已实时保存。

╔════════ yinuo 的答题卡 ════════╗
║    剩余时间:04:43            ║
╚════════════════════════════════╝
1. ⭐⭐⭐ 11 × 11 = 121 ✅已答
2. ⭐ 5-3=? ❌未答
3. ⭐ 3 + 5 = 8 ❌未答
4. ⭐⭐ 8÷2=? ❌未答
5. ⭐⭐⭐ 哪些是质数? ❌未答

请输入题号进行作答(1-5),或输入:
  'save' - 保存当前答案
  'list' - 查看所有题目
  'submit' - 交卷
命令: 2

┌─────────────────────────────────┐
│ 【单选题】5-3=?
├─────────────────────────────────┤
│   A. 1
│   B. 2
│   C. 3
│   D. 4
└─────────────────────────────────┘
请输入你的答案: B
✓ 答案已实时保存。

╔════════ yinuo 的答题卡 ════════╗
║    剩余时间:04:37            ║
╚════════════════════════════════╝
1. ⭐⭐⭐ 11 × 11 = 121 ✅已答
2. ⭐ 5-3=? ✅已答
3. ⭐ 3 + 5 = 8 ❌未答
4. ⭐⭐ 8÷2=? ❌未答
5. ⭐⭐⭐ 哪些是质数? ❌未答

请输入题号进行作答(1-5),或输入:
  'save' - 保存当前答案
  'list' - 查看所有题目
  'submit' - 交卷
命令: submit
确认交卷?(y/n): 
y

╔════════════════════════════════════════╗
║           考试成绩报告                  ║
╠════════════════════════════════════════╣
║ 考生: yinuo
║ 总分: 7 / 16
║ 正确率: 43%
║ 评级: 不及格 (D)
╠════════════════════════════════════════╣
║           错题解析                      ║
║ 3. 3 + 5 = 8
║    你的答案: 未作答
║    正确答案: 正确
║    分值: 2分  难度: 简单
║    ─────────────────────────────
║ 4. 8÷2=?
║    你的答案: 未作答
║    正确答案: 4
║    分值: 3分  难度: 中等
║    ─────────────────────────────
║ 5. 哪些是质数?
║    你的答案: 未作答
║    正确答案: [2, 5]
║    分值: 4分  难度: 困难
║    ─────────────────────────────
╚════════════════════════════════════════╝
返回主菜单。

╔════════════════════════════════╗
║     简易在线考试系统 v2.0     ║
╚════════════════════════════════╝
1. 管理员登录(题目管理)
2. 考生登录(参加考试)
3. 查看成绩排名
4. 退出系统
请选择: 2
请输入考生姓名: dashuai

欢迎 dashuai,考试即将开始!
本次考试共 5 题,限时 5 分钟。
难度分布:简单2题 中等2题 困难1题
输入 'save' 可实时保存当前答案,输入 'submit' 提前交卷。

╔════════ dashuai 的答题卡 ════════╗
║    剩余时间:05:00            ║
╚════════════════════════════════╝
1. ⭐⭐⭐ 哪些是质数? ❌未答
2. ⭐⭐ 8÷2=? ❌未答
3. ⭐ 5-3=? ❌未答
4. ⭐⭐ 7 × 6 = 42 ❌未答
5. ⭐ 3 + 5 = 8 ❌未答

请输入题号进行作答(1-5),或输入:
  'save' - 保存当前答案
  'list' - 查看所有题目
  'submit' - 交卷
命令: 1

┌─────────────────────────────────┐
│ 【多选题】哪些是质数?
├─────────────────────────────────┤
│   A. 2
│   B. 4
│   C. 5
│   D. 6
├─────────────────────────────────┤
│ 提示:多选答案请用逗号分隔     │
│ 例如:A,B 或 A,B,C            │
└─────────────────────────────────┘
请输入你的答案: A,C
✓ 答案已实时保存。

╔════════ dashuai 的答题卡 ════════╗
║    剩余时间:04:53            ║
╚════════════════════════════════╝
1. ⭐⭐⭐ 哪些是质数? ✅已答
2. ⭐⭐ 8÷2=? ❌未答
3. ⭐ 5-3=? ❌未答
4. ⭐⭐ 7 × 6 = 42 ❌未答
5. ⭐ 3 + 5 = 8 ❌未答

请输入题号进行作答(1-5),或输入:
  'save' - 保存当前答案
  'list' - 查看所有题目
  'submit' - 交卷
命令: 2

┌─────────────────────────────────┐
│ 【单选题】8÷2=?
├─────────────────────────────────┤
│   A. 2
│   B. 3
│   C. 4
│   D. 5
└─────────────────────────────────┘
请输入你的答案: C
✓ 答案已实时保存。

╔════════ dashuai 的答题卡 ════════╗
║    剩余时间:04:48            ║
╚════════════════════════════════╝
1. ⭐⭐⭐ 哪些是质数? ✅已答
2. ⭐⭐ 8÷2=? ✅已答
3. ⭐ 5-3=? ❌未答
4. ⭐⭐ 7 × 6 = 42 ❌未答
5. ⭐ 3 + 5 = 8 ❌未答

请输入题号进行作答(1-5),或输入:
  'save' - 保存当前答案
  'list' - 查看所有题目
  'submit' - 交卷
命令: 3

┌─────────────────────────────────┐
│ 【单选题】5-3=?
├─────────────────────────────────┤
│   A. 1
│   B. 2
│   C. 3
│   D. 4
└─────────────────────────────────┘
请输入你的答案: B
✓ 答案已实时保存。

╔════════ dashuai 的答题卡 ════════╗
║    剩余时间:04:40            ║
╚════════════════════════════════╝
1. ⭐⭐⭐ 哪些是质数? ✅已答
2. ⭐⭐ 8÷2=? ✅已答
3. ⭐ 5-3=? ✅已答
4. ⭐⭐ 7 × 6 = 42 ❌未答
5. ⭐ 3 + 5 = 8 ❌未答

请输入题号进行作答(1-5),或输入:
  'save' - 保存当前答案
  'list' - 查看所有题目
  'submit' - 交卷
命令: 4

┌─────────────────────────────────┐
│ 【判断题】7 × 6 = 42
├─────────────────────────────────┤
│   请输入:正确 或 错误         │
└─────────────────────────────────┘
请输入你的答案: 正确
✓ 答案已实时保存。

╔════════ dashuai 的答题卡 ════════╗
║    剩余时间:04:27            ║
╚════════════════════════════════╝
1. ⭐⭐⭐ 哪些是质数? ✅已答
2. ⭐⭐ 8÷2=? ✅已答
3. ⭐ 5-3=? ✅已答
4. ⭐⭐ 7 × 6 = 42 ✅已答
5. ⭐ 3 + 5 = 8 ❌未答

请输入题号进行作答(1-5),或输入:
  'save' - 保存当前答案
  'list' - 查看所有题目
  'submit' - 交卷
命令: 5

┌─────────────────────────────────┐
│ 【判断题】3 + 5 = 8
├─────────────────────────────────┤
│   请输入:正确 或 错误         │
└─────────────────────────────────┘
请输入你的答案: 正确
✓ 答案已实时保存。

╔════════ dashuai 的答题卡 ════════╗
║    剩余时间:04:17            ║
╚════════════════════════════════╝
1. ⭐⭐⭐ 哪些是质数? ✅已答
2. ⭐⭐ 8÷2=? ✅已答
3. ⭐ 5-3=? ✅已答
4. ⭐⭐ 7 × 6 = 42 ✅已答
5. ⭐ 3 + 5 = 8 ✅已答

请输入题号进行作答(1-5),或输入:
  'save' - 保存当前答案
  'list' - 查看所有题目
  'submit' - 交卷
命令: submit
确认交卷?(y/n): 
y

╔════════════════════════════════════════╗
║           考试成绩报告                  ║
╠════════════════════════════════════════╣
║ 考生: dashuai
║ 总分: 14 / 14
║ 正确率: 100%
║ 评级: 优秀 (A)
╠════════════════════════════════════════╣
║           错题解析                      ║
║   🎉 恭喜!全做对了!太棒了! 🎉
╚════════════════════════════════════════╝
返回主菜单。

╔════════════════════════════════╗
║     简易在线考试系统 v2.0     ║
╚════════════════════════════════╝
1. 管理员登录(题目管理)
2. 考生登录(参加考试)
3. 查看成绩排名
4. 退出系统
请选择: 3

========== 考试成绩排名 ==========
排名	考生	总分	正确率	考试时间
----------------------------------------
1	dashuai	14	100%	2026-04-17 14:54:34
2	yinuo	7	43%	2026-04-17 14:53:33
========================================

╔════════════════════════════════╗
║     简易在线考试系统 v2.0     ║
╚════════════════════════════════╝
1. 管理员登录(题目管理)
2. 考生登录(参加考试)
3. 查看成绩排名
4. 退出系统
请选择: 4
感谢使用!

八、总结
通过这次结对编程实践,我们不仅完成了一个功能完整的在线考试系统,更重要的是体验了敏捷协作的魅力。
代码统计:

  • 总行数:约600行
  • 开发时间:4小时
  • Bug数量:12个(全部修复)
posted on 2026-04-17 14:59  2452621  阅读(23)  评论(0)    收藏  举报