说明:这是 JavaSE 阶段的收尾综合大作业,要求把整个阶段学过的知识点串成一套完整的学生信息管理系统,覆盖:数据类型与运算符、流程控制(分支/循环)、类与对象(封装/构造方法)、继承与多态、抽象类与接口、自定义异常、集合框架(ArrayList/Map/Set/泛型/迭代器)、比较器排序(Comparable/Comparator) 等核心内容;Lambda 表达式 / 函数式编程 / Stream 流 / 方法引用 作为"进阶挑战(选做)"给出实现建议,鼓励但不强制。
题目按 模块 划分:模块一 ~ 模块二(⭐ 必做) → 模块三 ~ 模块四(⭐⭐ 必做) → 模块五(⭐⭐⭐⭐ 挑战·选做),最后附 简答题(必做)。请新建 Java 项目,按模块逐步编写并运行,将运行结果截图与代码一并提交。
| 项目 | 要求 |
|---|---|
| 包名 | 按下列"推荐包结构"创建(entity / exception / manager / util / main) |
| 类名 | 自由命名,但需见名知意(如 Student、ScoreManager) |
| 测试入口 | 主程序统一命名为 StudentSystemApp |
| 文件头注释 | 每个 Java 文件头部需包含 @author、@title、@description、@create 文档注释 |
提交\
└─ 张三\
├─ entity\ ← 模块一:实体类 + 接口
│ ├─ Person.java (抽象类)
│ ├─ Student.java
│ ├─ Teacher.java
│ ├─ Course.java
│ ├─ Score.java
│ ├─ Studyable.java (接口)
│ └─ Teachable.java (接口)
├─ exception\ ← 模块二:自定义异常
│ ├─ ScoreOutOfBoundsException.java
│ ├─ StudentNotFoundException.java
│ ├─ CourseNotFoundException.java
│ └─ DuplicateStudentException.java
├─ manager\ ← 模块三:核心功能(集合 + 流程控制)
│ ├─ StudentManager.java
│ ├─ CourseManager.java
│ └─ ScoreManager.java
├─ util\ ← 模块四:排序与排名
│ └─ RankUtil.java
├─ main\ ← 模块三:主菜单入口
│ └─ StudentSystemApp.java
└─ 简答题.txt ← 简答题答案
⚠ 注意:包结构可微调(如合并包),但逻辑分层必须清晰:实体类 / 异常类 / 管理类 / 主程序要分开。
某学校需要一个控制台版学生信息管理系统,用于管理学生、教师、课程、成绩四类核心信息。要求通过 Scanner 键盘输入 + 菜单 交互,实现学生增删改查、课程维护、成绩录入与校验、成绩排名等功能。
Person(抽象类:name / age)
├── Student(学号 id / 班级 clazz / 成绩列表 List<Score>)—— 学 → Studyable
└── Teacher(工号 id / 教授课程 Course) —— 教 → Teachable
Course(课程编号 / 课程名 / 学分)
Score (学号 / 课程编号 / 成绩 double,0~100)
| 模块 | 名称 | 必做程度 | 核心知识点 |
|---|---|---|---|
| 模块一 | 实体类设计(Person/Student/Teacher/Course/Score + 接口) | ⭐ 必做 | 数据类型、类与对象、封装、构造方法、继承、抽象类、接口、多态 |
| 模块二 | 自定义异常体系 | ⭐ 必做 | 自定义异常、throw/throws、try-catch、程序鲁棒性 |
| 模块三 | 系统核心功能(学生/课程/成绩管理 + 主菜单) | ⭐⭐ 必做 | 集合框架、泛型、迭代器、Scanner、流程控制(switch/while/if/for) |
| 模块四 | 排序与排名(Comparable / Comparator / TreeSet) | ⭐⭐ 必做 | 比较器排序、二次比较、Collections、compareTo 与 equals/hashCode 一致性 |
| 模块五 | 函数式编程与 Stream 流高级查询 | ⭐⭐⭐⭐ 选做·挑战 | Lambda、Stream、方法引用(给出建议,不强制) |
| 简答题 | 综合理论问答 | 必做 | 抽象类 vs 接口、equals/hashCode、异常机制等 |
| 知识点 | 落地位置 | 说明 |
|---|---|---|
| 数据类型与运算符 | 模块一/三 | String(学号/姓名/课程名)、int(年龄/学分)、double(成绩/平均分/及格率)、算术/比较/逻辑运算符 |
| 流程控制 | 模块三 | 主菜单 switch、外层 while 循环、输入校验 if-else、遍历 for / foreach |
| 类与对象、封装 | 模块一 | private 成员 + getter/setter、全参构造方法 |
| 继承 | 模块一 | Student extends Person、Teacher extends Person、super(name, age) |
| 抽象类 | 模块一 | Person 抽象类 + 抽象方法 getRole() |
| 接口 | 模块一 | Studyable / Teachable 接口,实现类重写抽象方法 |
| 多态 | 模块三 | Person[] / ArrayList<Person> 装 Student+Teacher,instanceof 向下转型 |
| 自定义异常 | 模块二/三 | 继承 Exception、throw 抛出、throws 声明、多 catch + finally |
| 集合框架 | 模块三 | ArrayList 学生/课程/成绩、HashMap<String, List<Score>> 按学号分组 |
| 泛型 | 模块三 | ArrayList<Student> 等泛型集合 |
| 迭代器 | 模块三/四 | Iterator 遍历集合 |
| 比较器排序 | 模块四 | Comparable 自然排序、Comparator 匿名内部类 / Lambda、Collections.sort |
| equals/hashCode | 模块一 | 以"业务主键"(学号/工号/学号+课程)重写 |
| String 类 | 模块一/三 | equals、compareTo、format 等 |
| Lambda / Stream / 方法引用 | 模块五(选做) | filter / map / count / distinct / collect(toMap) / 方法引用 |
知识点:数据类型、类与对象、封装、构造方法、继承、抽象类、接口、多态、equals/hashCode/toString
包名:
entity
要求定义一个抽象类 Person,作为"人"的公共抽象:
// Person.java
public abstract class Person {
private String name; // 姓名
private int age; // 年龄
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// TODO: getter / setter
// TODO: 抽象方法 —— 返回角色描述("学生" / "教师")
public abstract String getRole();
@Override
public String toString() {
return "姓名:" + name + ",年龄:" + age;
}
}
要求:
name / age 用 private 封装,提供 getter/setter(封装知识点)。getRole() 由子类实现。toString()(可结合 getClass().getSimpleName() 输出类名)。要求定义两个接口,分别规范"学生能学习、教师能授课":
// Studyable.java —— 学习能力接口
public interface Studyable {
void study(); // 学习
}
// Teachable.java —— 教学能力接口
public interface Teachable {
void teach(); // 授课
}
💡 建议(不强制):可以给接口再补一个 JDK8 默认方法,例如
default void prepare() { System.out.println("准备中..."); },体会接口默认方法的使用。
Student extends Person implements Comparable<Student>, Studyable,核心要求:
// Student.java
import java.util.ArrayList;
import java.util.List;
public class Student extends Person implements Comparable<Student>, Studyable {
private String id; // 学号,如 S001
private String clazz; // 班级,如 "Java2301"
private List<Score> scores; // 该学生的成绩列表
public Student(String name, int age, String id, String clazz) {
super(name, age); // 调用父类构造方法(继承知识点)
this.id = id;
this.clazz = clazz;
this.scores = new ArrayList<>();
}
// TODO: getter / setter
// TODO: addScore(Score s) —— 向成绩列表添加一条成绩
// TODO: getTotalScore() —— 遍历 scores 累加总分(注意:scores 为空返回 0)
// TODO: getAvgScore() —— 总分 / 成绩条数(保留两位小数;无成绩返回 0)
// TODO: getScoreCount() —— 返回成绩条数
// TODO: 实现抽象方法 getRole() —— 返回 "学生"
// TODO: 实现接口方法 study() —— 输出 "xxx 同学正在努力学习..."
// TODO: 重写 compareTo —— 按平均分【降序】,平均分相同再按学号
// 提示:int result = Double.compare(o.getAvgScore(), this.getAvgScore());
// return result == 0 ? this.id.compareTo(o.getId()) : result;
// TODO: 重写 equals / hashCode —— 以【学号】判定同一人
// 提示:equals 用 Objects.equals(id, student.id);hashCode 用 Objects.hash(id)
// TODO: 重写 toString —— 输出 学号/姓名/年龄/班级/平均分
}
要求:
super(name, age)(继承中构造方法的调用)。compareTo 用 Double.compare 实现降序 + 二次比较(学号)。equals/hashCode 以学号为判定依据,与 compareTo 判定口径一致。Teacher extends Person implements Teachable:
// Teacher.java
public class Teacher extends Person implements Teachable {
private String id; // 工号,如 T001
private Course course; // 教授的课程(对象关联)
public Teacher(String name, int age, String id, Course course) {
super(name, age);
this.id = id;
this.course = course;
}
// TODO: getter / setter
// TODO: 实现 getRole() —— 返回 "教师"
// TODO: 实现 teach() —— 输出 "xxx 老师正在讲授《课程名》..."
// TODO: 重写 equals / hashCode —— 以【工号】判定
// TODO: 重写 toString
}
// Course.java
public class Course {
private String courseId; // 课程编号,如 C001
private String courseName; // 课程名称,如 "JavaSE 基础"
private int credit; // 学分
public Course(String courseId, String courseName, int credit) {
this.courseId = courseId;
this.courseName = courseName;
this.credit = credit;
}
// TODO: getter / setter
// TODO: toString —— 输出 "C001-JavaSE 基础-3学分"
}
// Score.java
public class Score {
private String studentId; // 学号
private String courseId; // 课程编号
private double score; // 成绩(0~100)
public Score(String studentId, String courseId, double score) throws ScoreOutOfBoundsException {
this.studentId = studentId;
this.courseId = courseId;
setScore(score); // 在 setter 中校验并抛出异常
}
// TODO: getter / setter —— setScore 中校验:score < 0 || score > 100 时
// throw new ScoreOutOfBoundsException("成绩必须在 0~100 之间,当前值:" + score);
// TODO: equals / hashCode —— 以 studentId + courseId 判定(同一学生同一课程视为一条成绩)
// TODO: toString
}
📌 注意:
Score的构造方法签名带throws ScoreOutOfBoundsException——这里引出了模块二的异常类,请先完成异常类再回来编译。同时请思考:
Score类用到了哪些数据类型和运算符?(double成绩、||逻辑或、比较运算符等)
在 main 包下写一个临时测试类 EntityTest(只用于验证,不属于最终系统),完成以下验证:
// EntityTest.java(自测用)
public class EntityTest {
public static void main(String[] args) throws Exception {
// 1. 多态:Person[] 数组同时装 Student 和 Teacher
Person[] people = new Person[2];
people[0] = new Student("张三", 18, "S001", "Java2301");
people[1] = new Teacher("王老师", 35, "T001", new Course("C001", "JavaSE 基础", 3));
// 2. 遍历数组,调用 getRole()(动态绑定:编译看左,运行看右)
// 预期输出:张三 是 学生 / 王老师 是 教师
// 3. instanceof 向下转型
// 如果是 Student → 调用 study();如果是 Teacher → 调用 teach()
}
}
预期输出:
张三(18 岁)的角色:学生
王老师(35 岁)的角色:教师
张三 同学正在努力学习...
王老师 老师正在讲授《JavaSE 基础》...
模块一思考题(选做,不要求提交):
Person 为什么设计成抽象类?如果不抽象,new Person("李四", 20) 合不合理?(提示:世界上没有"人"这个具体对象)Student 同时继承 Person 和实现 Studyable,这体现了"单继承多实现"的什么特点?equals/hashCode 为什么要以学号为判定依据?知识点:自定义异常、继承
Exception、throw抛出、throws声明、try-catch、程序鲁棒性包名:
exception
均继承 Exception(编译期异常),提供无参构造 + 带消息构造:
// ScoreOutOfBoundsException.java —— 成绩越界(0~100 之外)
public class ScoreOutOfBoundsException extends Exception {
public ScoreOutOfBoundsException() { }
public ScoreOutOfBoundsException(String message) { super(message); }
}
// StudentNotFoundException.java —— 按学号查找学生时不存在
public class StudentNotFoundException extends Exception {
public StudentNotFoundException() { }
public StudentNotFoundException(String message) { super(message); }
}
// CourseNotFoundException.java —— 按课程编号查找课程时不存在
public class CourseNotFoundException extends Exception {
public CourseNotFoundException() { }
public CourseNotFoundException(String message) { super(message); }
}
// DuplicateStudentException.java —— 添加学生时学号已存在(重复)
public class DuplicateStudentException extends Exception {
public DuplicateStudentException() { }
public DuplicateStudentException(String message) { super(message); }
}
在 main 包下写临时测试类 ExceptionTest,验证异常机制:
// ExceptionTest.java(自测用)
public class ExceptionTest {
public static void main(String[] args) {
// 1. 正常成绩:new Score("S001", "C001", 88.5) → 输出成绩信息
// 2. 非法成绩:new Score("S001", "C001", 150) → 抛出 ScoreOutOfBoundsException
// 用 try-catch 捕获并输出错误消息:"成绩必须在 0~100 之间,当前值:150.0"
// 3. 同时抛多个异常(如 -5 和 120),验证多 catch 块各自独立捕获
// 4. 在 catch 后加 finally 块,输出"—— 成绩录入完成"
}
}
预期输出:
========== 测试 1:正常成绩 ==========
S001 同学 C001 课程成绩:88.5 分
========== 测试 2:成绩越界 ==========
成绩必须在 0~100 之间,当前值:150.0
—— 成绩录入完成
========== 测试 3:多异常捕获 ==========
成绩必须在 0~100 之间,当前值:-5.0
成绩必须在 0~100 之间,当前值:120.0
模块二思考题(选做):
Exception 而不是直接继承 Object?throw 和 throws 的区别是什么?catch 块的顺序有什么要求?(提示:子类异常在前,父类异常在后)知识点:集合框架(
ArrayList/HashMap)、泛型、迭代器、Scanner 键盘输入、流程控制(switch/while/if-else/for)、多态、自定义异常综合应用包名:
manager(管理类)、main(主菜单)
内部用 ArrayList<Student> 保存所有学生,封装如下功能(方法签名仅供参考,可自行设计):
// StudentManager.java
import java.util.ArrayList;
import java.util.Iterator;
public class StudentManager {
private ArrayList<Student> students; // 泛型集合
public StudentManager() {
this.students = new ArrayList<>();
}
// TODO: addStudent(Student s) —— 添加学生
// 要求:先判断学号是否已存在(遍历 students 用 equals / id 比对)
// 已存在 → 抛 DuplicateStudentException("学号已存在:S001")
// 不存在 → students.add(s),输出"添加成功"
// TODO: removeById(String id) —— 按学号删除学生
// 要求:找到并删除;找不到 → 抛 StudentNotFoundException("学生不存在:S001")
// TODO: findById(String id) —— 按学号查找,返回 Student;找不到返回 null
// 要求:必须使用迭代器 Iterator 遍历(复习迭代器知识点)
// TODO: printAll() —— 遍历输出所有学生(foreach 或迭代器)
// TODO: getStudents() —— 返回学生集合(供排序模块使用)
}
💡 建议(不强制):可以加一个
updateStudent(String id, String newName, int newAge)修改方法(先findById,再setName/setAge),进一步练习"查改"流程。
内部用 ArrayList<Course> 保存所有课程,封装类似功能:
// CourseManager.java
public class CourseManager {
private ArrayList<Course> courses;
public CourseManager() {
this.courses = new ArrayList<>();
}
// TODO: addCourse(Course c) —— 添加课程(课程编号重复则不允许)
// TODO: findById(String courseId) —— 按课程编号查找,返回 Course;找不到抛 CourseNotFoundException
// TODO: removeById(String courseId) —— 按课程编号删除(找不到抛 CourseNotFoundException)
// TODO: printAll() —— 遍历输出所有课程
// TODO: getCourses()
}
内部用 ArrayList<Score> 保存"成绩台账"(所有成绩),同时提供按学号分组统计功能:
// ScoreManager.java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class ScoreManager {
private ArrayList<Score> scores; // 成绩台账
private HashMap<String, List<Score>> scoreMap; // 学号 → 该学生的成绩列表(Map 知识点)
public ScoreManager() {
this.scores = new ArrayList<>();
this.scoreMap = new HashMap<>();
}
// TODO: addScore(Student stu, Course cou, double sc) —— 录入一条成绩
// 要求:
// 1) new Score(stu.getId(), cou.getCourseId(), sc) —— 成绩越界会抛 ScoreOutOfBoundsException
// 2) 加入全局成绩台账 scores.add(score)
// 3) 学生侧记录成绩:stu.addScore(score)
// 4) 更新 scoreMap:按学号分组(若 map 中还没有该学号,先 put 一个新的 ArrayList,再 add)
// 5) 输出"录入成功:张三 的 JavaSE 基础 成绩 = 88.5"
// TODO: printAll() —— 遍历输出成绩台账(迭代器或 foreach)
// TODO: getStudentTotal(String studentId) —— 从 scoreMap 取该学生成绩列表求总分
// TODO: getStudentAvg(String studentId) —— 求平均分
// TODO: countPass() —— 统计及格(>= 60)人数 / 及格率(用运算符计算百分比,保留两位小数)
}
📌 必做点:
HashMap<String, List<Score>>的"按学号分组"必须实现——这是对 Map 集合 和 ArrayList 嵌套 的综合训练。
在 main 包编写程序入口,用 Scanner + switch + while 实现控制台菜单:
// StudentSystemApp.java
import java.util.Scanner;
public class StudentSystemApp {
public static void main(String[] args) {
// 1. 创建各管理类对象
// StudentManager sm = new StudentManager();
// CourseManager cm = new CourseManager();
// ScoreManager scm = new ScoreManager();
// 2. 初始化少量示例数据(方便演示):
// 学生:张三/18/S001/Java2301、李四/19/S002/Java2301、王五/20/S003/Java2302
// 课程:C001-JavaSE 基础-3、C002-MySQL 数据库-2、C003-数据结构-3
// 教师:王老师/35/T001/C001
// 3. 主循环 + 主菜单(while + switch)
// 主菜单:
// ===== 学生信息管理系统 =====
// 1. 学生管理
// 2. 课程管理
// 3. 成绩管理
// 4. 成绩排名
// 5. 高级查询(挑战·选做)
// 0. 退出
// 请输入你的选择:
// 4. 二级菜单(以学生管理为例):
// ===== 学生管理 =====
// 1. 添加学生
// 2. 删除学生
// 3. 查找学生
// 4. 查看所有学生
// 0. 返回上级
// 5. 每个功能都用 Scanner 接收键盘输入,并 try-catch 处理自定义异常
// (例如:添加重复学号 → 捕获 DuplicateStudentException 并提示)
}
}
===== 学生信息管理系统 =====
1. 学生管理
2. 课程管理
3. 成绩管理
4. 成绩排名
5. 高级查询(挑战·选做)
0. 退出
请输入你的选择:1
===== 学生管理 =====
1. 添加学生
2. 删除学生
3. 查找学生
4. 查看所有学生
0. 返回上级
请输入你的选择:1
请输入姓名:赵六
请输入年龄:21
请输入学号:S004
请输入班级:Java2302
添加成功:赵六
在"添加学生""删除学生""录入成绩"三个功能中,必须用 try-catch 处理自定义异常:
| 操作 | 可能抛出的异常 | 提示 |
|---|---|---|
| 添加学生(学号重复) | DuplicateStudentException |
输出"学号已存在:S004" |
| 删除/查找学生(不存在) | StudentNotFoundException |
输出"学生不存在:S999" |
| 查找课程(不存在) | CourseNotFoundException |
输出"课程不存在:C999" |
| 录入成绩(0~100 之外) | ScoreOutOfBoundsException |
输出"成绩必须在 0~100 之间,当前值:150.0" |
模块三思考题(选做):
ArrayList 存储学生和用之前学的 Student[] 数组存储,最大的区别是什么?ConcurrentModificationException)HashMap<String, List<Score>> 中,key 为什么用学号 String 而不是 Student 对象?(提示:简化 + 唯一)知识点:
Comparable自然排序、Comparator比较器(匿名内部类 / Lambda)、Collections工具类、TreeSet自动排序去重、迭代器遍历、compareTo 与 equals/hashCode 一致性包名:
util
在 RankUtil 中完成 4 个排序/排名功能,并在 main 包中写 RankTest 验证:
// RankUtil.java
import java.util.*;
public class RankUtil {
/**
* 功能 1:成绩排行榜 —— TreeSet 自然排序
* 利用 Student 的 compareTo(平均分降序,相同按学号),
* 把学生添加进 TreeSet<Student>,迭代器遍历输出排行榜
*/
public static void showRank(StudentManager sm) {
// TODO: 1) 从 sm.getStudents() 拿到 ArrayList<Student>
// 2) 创建 TreeSet<Student>,把所有学生 add 进去(自动按自然排序排好 + 去重)
// 3) 用 Iterator 遍历输出,格式:排名-学号-姓名-平均分
}
/**
* 功能 2:按姓名排序 —— Comparator 匿名内部类
* 使用 Collections.sort(list, new Comparator<Student>() {...}),
* 匿名内部类中 o1.getName().compareTo(o2.getName())
*/
public static void sortByName(StudentManager sm) {
// TODO: 拷贝一份学生列表,Collections.sort + 匿名内部类 Comparator 按姓名升序
// 遍历输出
}
/**
* 功能 3:按年龄排序 —— Comparator + Lambda
* 使用 Collections.sort(list, (s1, s2) -> Integer.compare(s1.getAge(), s2.getAge()));
*/
public static void sortByAge(StudentManager sm) {
// TODO: 拷贝学生列表,用 Lambda 按年龄升序排序,遍历输出
}
/**
* 功能 4:按平均分降序(和自然排序对比)—— 用 Collections.sort + 匿名内部类
* 要求:不依赖 Student 的 compareTo,在 Comparator 中自己算平均分(Double.compare 降序)
*/
public static void sortByAvgDesc(StudentManager sm) {
// TODO: Double.compare(o2.getAvgScore(), o1.getAvgScore()) 实现降序
}
}
沿用模块三的示例数据,给部分学生录入成绩后,调用 RankUtil 的 4 个方法,预期输出如下:
========== 功能 1:成绩排行榜(TreeSet 自然排序) ==========
第1名:S001 张三 平均分 90.0
第2名:S002 李四 平均分 85.0
第3名:S003 王五 平均分 78.0
========== 功能 2:按姓名排序(Comparator 匿名内部类) ==========
S001 张三
S002 李四
S003 王五
========== 功能 3:按年龄排序(Comparator + Lambda) ==========
S001 张三 18 岁
S002 李四 19 岁
S003 王五 20 岁
========== 功能 4:按平均分降序(Collections.sort) ==========
S001 张三 90.0
S002 李四 85.0
S003 王五 78.0
📌 以上输出仅为示意,实际内容取决于你录入的数据,但排序规则必须正确。
Student 重写 compareTo 后,放入 TreeSet 会发生什么?(提示:自动排序 + 按 compareTo 返回值去重)equals/hashCode 以学号判定,compareTo 也要保证学号相同返回 0?(提示:compareTo 与 equals/hashCode 判定口径一致性,官方推荐 compareTo == 0 → equals 返回 true)Student 类?(提示:用 Comparator 在集合这边定义排序规则——Collections.sort(list, comparator) 或 TreeSet(comparator))模块四思考题(选做):
Collections.sort(list) 和 TreeSet 自动排序有什么区别?(提示:前者改已有 List,后者存入时排序+去重)(s1, s2) -> Integer.compare(...) 等价于哪个方法引用写法?(提示:Comparator.comparingInt(Student::getAge))TreeSet 吗?知识点:Lambda 表达式、函数式接口(Predicate / Consumer / Function)、Stream 流(filter / map / count / distinct / sorted / collect)、方法引用
说明:本模块为选做挑战,给出实现建议,不强制要求。学有余力的同学建议完成,它是对今日(0804)所学的 Stream 流与函数式编程 的实战检验。
在 util 包新增 StreamQuery 类,用 Stream 流完成 6 个高级查询(每个查询都是一条 Stream 链):
// StreamQuery.java
import java.util.*;
import java.util.stream.Collectors;
public class StreamQuery {
/**
* 查询 1:及格学生名单 —— filter + forEach
* 过滤出平均分 >= 60 的学生,输出"学号-姓名-平均分"
* 建议:list.stream().filter(s -> s.getAvgScore() >= 60).forEach(s -> System.out.println(...));
*/
public static void queryPass(List<Student> list) { }
/**
* 查询 2:所有学生姓名 —— map + collect(toList)
* 把学生流映射成姓名流,收集成 List<String> 输出
* 建议:list.stream().map(Student::getName).collect(Collectors.toList())
*/
public static void queryNames(List<Student> list) { }
/**
* 查询 3:及格人数统计 —— filter + count
* 建议:long cnt = list.stream().filter(s -> s.getAvgScore() >= 60).count();
*/
public static void countPass(List<Student> list) { }
/**
* 查询 4:班级去重 —— map + distinct
* 取出所有班级名,去重后输出
* 建议:list.stream().map(Student::getClazz).distinct().forEach(System.out::println);
*/
public static void distinctClazz(List<Student> list) { }
/**
* 查询 5:学号 → 学生 映射 —— collect(Collectors.toMap)
* 收集成 Map<String, Student>(键=学号,值=学生对象)
* 建议:Map<String, Student> map = list.stream()
* .collect(Collectors.toMap(Student::getId, s -> s));
*/
public static void toStudentMap(List<Student> list) { }
/**
* 查询 6:按平均分降序排名 —— sorted + Comparator.comparingDouble 方法引用
* 建议:list.stream()
* .sorted(Comparator.comparingDouble(Student::getAvgScore).reversed())
* .forEach(...);
*/
public static void rankByStream(List<Student> list) { }
}
========== 查询 1:及格学生 ==========
S001-张三-90.0
S002-李四-85.0
========== 查询 2:所有学生姓名 ==========
[张三, 李四, 王五]
========== 查询 3:及格人数 ==========
及格人数:2
========== 查询 4:班级去重 ==========
Java2301
Java2302
========== 查询 5:学号→学生 Map ==========
{S001=Student{...}, S002=Student{...}, S003=Student{...}}
========== 查询 6:Stream 排名 ==========
S001 张三 90.0
S002 李四 85.0
S003 王五 78.0
Student::getName 等价于 s -> s.getName();System.out::println 等价于 s -> System.out.println(s)。skip((pageNo - 1) * pageSize).limit(pageSize) 实现成绩排行榜分页。new Function<Student, String>() {...} 匿名内部类版本,再改成 Lambda 版本,体会简化。@FunctionalInterface interface ScoreFilter { boolean test(Student s); }),把"过滤规则"作为参数传递——体会函数式编程"把函数作为参数"的思想。请用文字说明(可配合示例代码)回答以下问题:
从以下维度对比 抽象类 和 接口:
equals 为什么必须重写 hashCode?compareTo == 0 与 equals 返回 true 有什么关系?本作业中学生类为什么统一以"学号"判定?Comparable,什么时候用 Comparator?Exception 或 RuntimeException?两者区别?throw 和 throws 的区别?从三个维度回答:
| 评分维度 | 达标标准 | 分值参考 |
|---|---|---|
| 模块一 实体类 | 抽象类/接口/继承/多态/equals/hashCode 全部正确,能编译运行 | 25 分 |
| 模块二 异常 | 4 个自定义异常 + try-catch 正确使用 | 15 分 |
| 模块三 核心功能 | 菜单可运行,增删改查 + 成绩录入校验 + Map 分组全部实现 | 30 分 |
| 模块四 排序排名 | TreeSet / Comparator / Collections 三种排序正确 | 20 分 |
| 简答题 | 表达清楚、要点完整 | 10 分 |
| 模块五 Stream(选做) | 6 个查询正确,可额外加分 | 加分 10 分 |
✅ 提交清单:全部 Java 源码 + 各模块运行结果截图 + 简答题文档 + 提交目录结构与命名合规。