摘要: 本文通过一个完整的Java学生管理系统实战项目,系统性地展示了面向对象编程三大特性(封装、继承、多态)的实际应用。从需求分析、系统设计到逐步实现,详细讲解了如何使用抽象类、枚举、自定义异常等核心概念构建健壮的应用程序。项目包含人员管理、课程管理、选课与成绩三大模块,提供了完整的命令行交互界面和异常处理机制,帮助读者将理论知识转化为实践能力。
关键词: Java面向对象编程, 学生管理系统, 封装继承多态, 自定义异常, 枚举类
在过去的十一篇文章里,我们深入学习了面向对象编程的三大特性:封装、继承、多态。我们还认识了抽象类、接口、内部类、枚举、Record,以及异常处理。如果把写程序比作盖房子,那你现在手里已经有一套相当完整的“工具箱”了——有设计图纸(类)、有施工规范(封装)、有预制件(继承)、有各种专用工具(接口、枚举、异常)。
知识点学了不少,但如果它们只是散落在各自的位置上,就像一套崭新的工具各自躺在工具箱里,你还没亲手用它们搭出过一栋完整的建筑。
今天,我们要做一件事:把第三阶段学到的所有知识,放到一个完整的项目中融会贯通。
我们要写一个学生管理系统——它不是什么大型企业应用,但足以让你感受到面向对象编程是如何把现实世界中的“学生”、“老师”、“课程”、“成绩”这些概念,优雅地映射到代码中的。
1. 我们要做什么?—— 需求分析
这是一个命令行版的学生管理系统,它要能管理学校里的基本数据。我们把它设计成三个核心模块:
模块一:人员管理
- 学校里有两种人:学生和老师。
- 学生有:姓名、年龄、学号、年级(大一/大二/大三/大四)。
- 老师有:姓名、年龄、工号、职称(助教/讲师/副教授/教授)。
- 系统能添加学生/老师,能列出所有学生/老师。
模块二:课程管理
- 课程有:课程编号、课程名称、学分、授课老师。
- 系统能添加课程,能列出所有课程。
模块三:选课与成绩
- 学生可以选修课程(一个学生可以选多门课)。
- 老师可以为选修了自己课程的学生录入成绩(0~100 分)。
- 系统能查询某个学生的所有课程和成绩,能计算平均分。
此外,我们还要考虑:
- 用枚举来表示年级、职称、性别。
- 用自定义异常来处理业务错误(比如学生不存在、选课重复、成绩越界)。
- 用
List来存储数据(虽然集合框架是下一阶段的内容,但我们今天先简单地用ArrayList,它很好上手,和数组类似但更灵活)。
这个项目的复杂度恰到好处——它足够让你体会到面向对象设计的精妙,又不会因为功能太多而迷失在细节里。
2. 系统设计 —— 先画出“骨架”
在写代码之前,我们先设计一下类之间的关系。面向对象编程的核心就是先设计结构,再填充实现。
类图(文字版)
┌──────────────────┐
│ Person │ ← 抽象类
│ - name: String │
│ - age: int │
│ - gender: Gender│
│ + introduce() │
└────────┬─────────┘
│
┌────┴────┐
│ │
┌────▼─────┐ ┌──▼──────────┐
│Student │ │ Teacher │
│-studentId│ │-employeeId │
│-grade │ │-title │
│-courses │ │+ teach() │
│+ enroll()│ │ │
└──────────┘ └─────────────┘
┌─────────────────┐ ┌─────────────────┐
│ Course │ │ GradeRecord │
│ - courseId │◄──────│ - student │
│ - name │ │ - course │
│ - credit │ │ - score │
│ - teacher │ └─────────────────┘
│ + addStudent() │
│ + getStudents()│
└─────────────────┘
核心设计思路
Person抽象类:作为学生和老师的共同父类,封装了姓名、年龄、性别,定义了抽象方法introduce(),让每个子类以自己的方式介绍自己。Student和Teacher子类:继承Person,添加各自特有的属性和方法。Course类:表示一门课程,包含课程信息和授课老师,维护一个选了该课的学生列表。GradeRecord类:表示一条成绩记录,关联一个学生和一门课程,记录分数。Gender、GradeLevel、Title枚举:用枚举来约束取值范围,保证类型安全。- 自定义异常:
StudentNotFoundException、DuplicateEnrollmentException、InvalidScoreException。 SchoolManager主控类:包含main方法,提供命令行交互菜单。
3. 逐步实现
我们从最底层开始,一层一层往上搭建。
第一步:枚举类
先用枚举来定义那些“固定选项”。
// Gender.java
package com.example.school;
public enum Gender {
MALE("男"),
FEMALE("女");
private final String chineseName;
Gender(String chineseName) {
this.chineseName = chineseName;
}
public String getChineseName() {
return chineseName;
}
}
// GradeLevel.java
package com.example.school;
public enum GradeLevel {
FRESHMAN("大一"),
SOPHOMORE("大二"),
JUNIOR("大三"),
SENIOR("大四");
private final String chineseName;
GradeLevel(String chineseName) {
this.chineseName = chineseName;
}
public String getChineseName() {
return chineseName;
}
}
// Title.java
package com.example.school;
public enum Title {
ASSISTANT("助教"),
LECTURER("讲师"),
ASSOCIATE_PROFESSOR("副教授"),
PROFESSOR("教授");
private final String chineseName;
Title(String chineseName) {
this.chineseName = chineseName;
}
public String getChineseName() {
return chineseName;
}
}
这些枚举都带了一个 chineseName 字段,方便在输出时显示中文,而不是英文常量名。
第二步:抽象类 Person
// Person.java
package com.example.school;
public abstract class Person {
protected String name;
protected int age;
protected Gender gender;
public Person(String name, int age, Gender gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() { return name; }
public int getAge() { return age; }
public Gender getGender() { return gender; }
// 抽象方法:让子类各自实现介绍方式
public abstract String introduce();
@Override
public String toString() {
return String.format("%s (%s, %d岁)", name, gender.getChineseName(), age);
}
}
这里用 protected 修饰字段,让子类可以直接访问(但更好的做法是用 getter,我们这里为了代码简洁用了 protected)。
第三步:自定义异常
我们先定义几个业务异常,让程序能精准地表达错误。
// StudentNotFoundException.java
package com.example.school;
public class StudentNotFoundException extends Exception {
public StudentNotFoundException(String studentId) {
super("学生不存在:" + studentId);
}
}
// DuplicateEnrollmentException.java
package com.example.school;
public class DuplicateEnrollmentException extends Exception {
public DuplicateEnrollmentException(String studentId, String courseId) {
super("学生 " + studentId + " 已选修课程 " + courseId);
}
}
// InvalidScoreException.java
package com.example.school;
public class InvalidScoreException extends Exception {
public InvalidScoreException(double score) {
super("成绩必须在 0~100 之间:" + score);
}
}
这些异常都继承自 Exception(受检异常),强制调用方处理。
第四步:Student 类
// Student.java
package com.example.school;
import java.util.ArrayList;
import java.util.List;
public class Student extends Person {
private String studentId;
private GradeLevel grade;
// 该学生选修的课程列表
private List<Course> enrolledCourses;
public Student(String name, int age, Gender gender, String studentId, GradeLevel grade) {
super(name, age, gender);
this.studentId = studentId;
this.grade = grade;
this.enrolledCourses = new ArrayList<>();
}
public String getStudentId() { return studentId; }
public GradeLevel getGrade() { return grade; }
public void setGrade(GradeLevel grade) { this.grade = grade; }
public List<Course> getEnrolledCourses() {
return new ArrayList<>(enrolledCourses); // 返回副本,保护内部数据
}
// 选课
public void enroll(Course course) throws DuplicateEnrollmentException {
if (enrolledCourses.contains(course)) {
throw new DuplicateEnrollmentException(studentId, course.getCourseId());
}
enrolledCourses.add(course);
course.addStudent(this); // 双向关联:课程也记录这个学生
}
// 获取某门课的成绩
public Double getScoreForCourse(Course course) {
for (GradeRecord record : course.getGradeRecords()) {
if (record.getStudent().equals(this)) {
return record.getScore();
}
}
return null; // 还没有成绩
}
// 计算平均分(只计算有成绩的课程)
public double calculateAverage() {
List<GradeRecord> allRecords = new ArrayList<>();
for (Course course : enrolledCourses) {
allRecords.addAll(course.getGradeRecords());
}
// 筛选出属于该学生的成绩记录
double sum = 0;
int count = 0;
for (GradeRecord record : allRecords) {
if (record.getStudent().equals(this)) {
sum += record.getScore();
count++;
}
}
return count == 0 ? 0 : sum / count;
}
@Override
public String introduce() {
return String.format("我是学生 %s,学号 %s,%s %s",
name, studentId, grade.getChineseName(), gender.getChineseName());
}
@Override
public String toString() {
return String.format("%s [%s, %s]", super.toString(), studentId, grade.getChineseName());
}
}
注意这里用了双向关联:Student 持有 enrolledCourses,Course 也持有选修了它的学生列表。这种设计让查询变得灵活:既可以从学生查到课程,也可以从课程查学生。
第五步:Teacher 类
// Teacher.java
package com.example.school;
public class Teacher extends Person {
private String employeeId;
private Title title;
public Teacher(String name, int age, Gender gender, String employeeId, Title title) {
super(name, age, gender);
this.employeeId = employeeId;
this.title = title;
}
public String getEmployeeId() { return employeeId; }
public Title getTitle() { return title; }
public void setTitle(Title title) { this.title = title; }
@Override
public String introduce() {
return String.format("我是老师 %s,工号 %s,%s",
name, employeeId, title.getChineseName());
}
@Override
public String toString() {
return String.format("%s [%s, %s]", super.toString(), employeeId, title.getChineseName());
}
}
第六步:Course 类
// Course.java
package com.example.school;
import java.util.ArrayList;
import java.util.List;
public class Course {
private String courseId;
private String name;
private double credit;
private Teacher teacher;
// 选修该课程的学生列表
private List<Student> students;
// 成绩记录列表
private List<GradeRecord> gradeRecords;
public Course(String courseId, String name, double credit, Teacher teacher) {
this.courseId = courseId;
this.name = name;
this.credit = credit;
this.teacher = teacher;
this.students = new ArrayList<>();
this.gradeRecords = new ArrayList<>();
}
public String getCourseId() { return courseId; }
public String getName() { return name; }
public double getCredit() { return credit; }
public Teacher getTeacher() { return teacher; }
public List<Student> getStudents() {
return new ArrayList<>(students);
}
public List<GradeRecord> getGradeRecords() {
return new ArrayList<>(gradeRecords);
}
// 添加学生(由 Student.enroll() 调用)
public void addStudent(Student student) {
if (!students.contains(student)) {
students.add(student);
}
}
// 录入成绩
public void assignGrade(Student student, double score) throws InvalidScoreException {
if (score < 0 || score > 100) {
throw new InvalidScoreException(score);
}
if (!students.contains(student)) {
throw new IllegalArgumentException("该学生没有选修这门课程:" + student.getStudentId());
}
// 如果已有成绩记录,更新;否则新增
for (GradeRecord record : gradeRecords) {
if (record.getStudent().equals(student)) {
record.setScore(score);
return;
}
}
gradeRecords.add(new GradeRecord(student, this, score));
}
// 获取某个学生的成绩
public Double getStudentScore(Student student) {
for (GradeRecord record : gradeRecords) {
if (record.getStudent().equals(student)) {
return record.getScore();
}
}
return null;
}
@Override
public String toString() {
return String.format("%s [%s] %s学分, 授课教师: %s",
courseId, name, credit, teacher.getName());
}
}
第七步:GradeRecord 类(关联类)
// GradeRecord.java
package com.example.school;
public class GradeRecord {
private Student student;
private Course course;
private double score;
public GradeRecord(Student student, Course course, double score) {
this.student = student;
this.course = course;
this.score = score;
}
public Student getStudent() { return student; }
public Course getCourse() { return course; }
public double getScore() { return score; }
public void setScore(double score) { this.score = score; }
@Override
public String toString() {
return String.format("%s - %s: %.1f分", student.getName(), course.getName(), score);
}
}
GradeRecord 是一个关联类,它把学生和课程关联起来,同时携带了“成绩”这个额外的属性。这种设计比在 Student 或 Course 中单独维护成绩更清晰。
第八步:SchoolManager 主控类(菜单 + 交互)
// SchoolManager.java
package com.example.school;
import java.util.*;
public class SchoolManager {
private List<Student> students = new ArrayList<>();
private List<Teacher> teachers = new ArrayList<>();
private List<Course> courses = new ArrayList<>();
private Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
SchoolManager manager = new SchoolManager();
manager.run();
}
public void run() {
// 预先添加一些示例数据
initSampleData();
while (true) {
showMenu();
String choice = scanner.nextLine().trim();
switch (choice) {
case "1" -> addStudent();
case "2" -> addTeacher();
case "3" -> addCourse();
case "4" -> listStudents();
case "5" -> listTeachers();
case "6" -> listCourses();
case "7" -> enrollStudent();
case "8" -> assignGrade();
case "9" -> showStudentReport();
case "0" -> {
System.out.println("感谢使用学生管理系统,再见!");
return;
}
default -> System.out.println("无效选项,请重新输入。");
}
}
}
private void showMenu() {
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.println("7. 学生选课");
System.out.println("8. 录入成绩");
System.out.println("9. 查看学生成绩报告");
System.out.println("0. 退出");
System.out.print("请选择操作:");
}
// ---------- 添加数据 ----------
private void addStudent() {
System.out.print("姓名:");
String name = scanner.nextLine();
System.out.print("年龄:");
int age = Integer.parseInt(scanner.nextLine());
System.out.print("性别 (MALE/FEMALE):");
Gender gender = Gender.valueOf(scanner.nextLine().toUpperCase());
System.out.print("学号:");
String id = scanner.nextLine();
System.out.print("年级 (FRESHMAN/SOPHOMORE/JUNIOR/SENIOR):");
GradeLevel grade = GradeLevel.valueOf(scanner.nextLine().toUpperCase());
Student s = new Student(name, age, gender, id, grade);
students.add(s);
System.out.println("✅ 学生添加成功:" + s.introduce());
}
private void addTeacher() {
System.out.print("姓名:");
String name = scanner.nextLine();
System.out.print("年龄:");
int age = Integer.parseInt(scanner.nextLine());
System.out.print("性别 (MALE/FEMALE):");
Gender gender = Gender.valueOf(scanner.nextLine().toUpperCase());
System.out.print("工号:");
String id = scanner.nextLine();
System.out.print("职称 (ASSISTANT/LECTURER/ASSOCIATE_PROFESSOR/PROFESSOR):");
Title title = Title.valueOf(scanner.nextLine().toUpperCase());
Teacher t = new Teacher(name, age, gender, id, title);
teachers.add(t);
System.out.println("✅ 老师添加成功:" + t.introduce());
}
private void addCourse() {
System.out.print("课程编号:");
String id = scanner.nextLine();
System.out.print("课程名称:");
String name = scanner.nextLine();
System.out.print("学分:");
double credit = Double.parseDouble(scanner.nextLine());
// 选择授课老师
if (teachers.isEmpty()) {
System.out.println("❌ 请先添加老师!");
return;
}
System.out.println("可选老师:");
for (int i = 0; i < teachers.size(); i++) {
System.out.println((i + 1) + ". " + teachers.get(i));
}
System.out.print("选择授课老师编号:");
int idx = Integer.parseInt(scanner.nextLine()) - 1;
Teacher teacher = teachers.get(idx);
Course c = new Course(id, name, credit, teacher);
courses.add(c);
System.out.println("✅ 课程添加成功:" + c);
}
// ---------- 查询 ----------
private void listStudents() {
System.out.println("\n--- 所有学生 ---");
if (students.isEmpty()) {
System.out.println("暂无学生数据。");
return;
}
for (Student s : students) {
System.out.println(" " + s + " | " + s.introduce());
}
}
private void listTeachers() {
System.out.println("\n--- 所有老师 ---");
if (teachers.isEmpty()) {
System.out.println("暂无老师数据。");
return;
}
for (Teacher t : teachers) {
System.out.println(" " + t + " | " + t.introduce());
}
}
private void listCourses() {
System.out.println("\n--- 所有课程 ---");
if (courses.isEmpty()) {
System.out.println("暂无课程数据。");
return;
}
for (Course c : courses) {
System.out.println(" " + c + " | 选课人数:" + c.getStudents().size());
}
}
// ---------- 业务操作 ----------
private Student findStudentById(String id) {
for (Student s : students) {
if (s.getStudentId().equals(id)) {
return s;
}
}
return null;
}
private Course findCourseById(String id) {
for (Course c : courses) {
if (c.getCourseId().equals(id)) {
return c;
}
}
return null;
}
private void enrollStudent() {
try {
System.out.print("请输入学生学号:");
String studentId = scanner.nextLine();
Student student = findStudentById(studentId);
if (student == null) {
throw new StudentNotFoundException(studentId);
}
System.out.print("请输入课程编号:");
String courseId = scanner.nextLine();
Course course = findCourseById(courseId);
if (course == null) {
System.out.println("❌ 课程不存在:" + courseId);
return;
}
student.enroll(course);
System.out.println("✅ " + student.getName() + " 成功选修 " + course.getName());
} catch (DuplicateEnrollmentException e) {
System.out.println("❌ " + e.getMessage());
} catch (StudentNotFoundException e) {
System.out.println("❌ " + e.getMessage());
}
}
private void assignGrade() {
try {
System.out.print("请输入学生学号:");
String studentId = scanner.nextLine();
Student student = findStudentById(studentId);
if (student == null) {
throw new StudentNotFoundException(studentId);
}
System.out.print("请输入课程编号:");
String courseId = scanner.nextLine();
Course course = findCourseById(courseId);
if (course == null) {
System.out.println("❌ 课程不存在:" + courseId);
return;
}
if (!student.getEnrolledCourses().contains(course)) {
System.out.println("❌ 该学生未选修这门课程。");
return;
}
System.out.print("请输入成绩 (0~100):");
double score = Double.parseDouble(scanner.nextLine());
course.assignGrade(student, score);
System.out.println("✅ 成绩录入成功!");
} catch (StudentNotFoundException e) {
System.out.println("❌ " + e.getMessage());
} catch (InvalidScoreException e) {
System.out.println("❌ " + e.getMessage());
} catch (IllegalArgumentException e) {
System.out.println("❌ " + e.getMessage());
}
}
private void showStudentReport() {
System.out.print("请输入学生学号:");
String studentId = scanner.nextLine();
Student student = findStudentById(studentId);
if (student == null) {
System.out.println("❌ 学生不存在:" + studentId);
return;
}
System.out.println("\n========== 成绩报告 ==========");
System.out.println("学生:" + student.getName() + " (" + student.getStudentId() + ")");
System.out.println("年级:" + student.getGrade().getChineseName());
System.out.println("选修课程:");
List<Course> enrolled = student.getEnrolledCourses();
if (enrolled.isEmpty()) {
System.out.println(" (尚未选修任何课程)");
return;
}
for (Course course : enrolled) {
Double score = student.getScoreForCourse(course);
String scoreDisplay = (score == null) ? "未录入" : String.format("%.1f", score);
System.out.printf(" %s [%s]:%s 学分,成绩:%s%n",
course.getCourseId(), course.getName(), course.getCredit(), scoreDisplay);
}
double avg = student.calculateAverage();
System.out.printf("\n平均分:%.1f%n", avg);
System.out.println("================================");
}
// ---------- 示例数据 ----------
private void initSampleData() {
// 添加老师
Teacher t1 = new Teacher("张教授", 45, Gender.MALE, "T001", Title.PROFESSOR);
Teacher t2 = new Teacher("李副教授", 38, Gender.FEMALE, "T002", Title.ASSOCIATE_PROFESSOR);
teachers.add(t1);
teachers.add(t2);
// 添加课程
Course c1 = new Course("C001", "Java程序设计", 4.0, t1);
Course c2 = new Course("C002", "数据库原理", 3.0, t2);
Course c3 = new Course("C003", "数据结构", 3.5, t1);
courses.add(c1);
courses.add(c2);
courses.add(c3);
// 添加学生
Student s1 = new Student("王小明", 20, Gender.MALE, "S001", GradeLevel.SOPHOMORE);
Student s2 = new Student("李小丽", 19, Gender.FEMALE, "S002", GradeLevel.FRESHMAN);
Student s3 = new Student("赵大伟", 21, Gender.MALE, "S003", GradeLevel.JUNIOR);
students.add(s1);
students.add(s2);
students.add(s3);
// 给部分学生选课
try {
s1.enroll(c1);
s1.enroll(c2);
s2.enroll(c1);
s3.enroll(c2);
s3.enroll(c3);
} catch (DuplicateEnrollmentException e) {
// 示例数据不会重复,忽略
}
// 录入一些成绩
try {
c1.assignGrade(s1, 85.0);
c2.assignGrade(s1, 90.0);
c1.assignGrade(s2, 78.0);
c2.assignGrade(s3, 92.0);
} catch (InvalidScoreException e) {
// 示例数据分数合法,忽略
}
System.out.println("✅ 示例数据加载完成。");
}
}
4. 运行与测试
把以上所有类放到 com.example.school 包下,运行 SchoolManager.main()。你会看到菜单,可以自由操作:
- 添加学生/老师/课程:体验用枚举来输入选项,感受类型安全带来的好处。
- 学生选课:选课功能会检查是否重复选课,抛出自定义异常。
- 录入成绩:录入时会检查成绩范围(0~100),不合规会抛出
InvalidScoreException。 - 查看成绩报告:展示某个学生所有课程的成绩和平均分。
你可以尝试一些“错误操作”,比如录入一个超过 100 分的成绩、让一个未选课的学生录入成绩、重复选课等——观察程序是否都能优雅地处理,而不是崩溃。
5. 知识点回顾:我们用了什么?
这个不到 500 行的项目,几乎涵盖了第三阶段的所有核心概念:
| 知识点 | 体现在哪里 |
|---|---|
| 封装 | 所有类的字段都是 private,通过 public 方法访问 |
| 继承 | Student 和 Teacher 继承 Person |
| 多态 | Person 引用可以指向 Student 或 Teacher,introduce() 动态绑定 |
| 抽象类 | Person 是抽象类,定义了模板,不允许直接实例化 |
| 接口 | (本项目中未显式使用,但你可以自己扩展,比如定义 Gradable 接口) |
| 枚举 | Gender、GradeLevel、Title 让取值范围可控 |
| 自定义异常 | StudentNotFoundException、DuplicateEnrollmentException、InvalidScoreException |
| 异常处理 | try-catch、受检异常的声明与处理 |
| 集合 | ArrayList 存储学生、老师、课程、成绩记录 |
| 内部类 | (你可以把 GradeRecord 设计成 Course 的内部类,目前作为独立类更清晰) |
| 包与导入 | 所有类都放在 com.example.school 包下,使用 import 导入标准库 |
| Record | (你可以把 GradeRecord 改写成 Record,作为一个练习) |
6. 如何扩展?(留给你的挑战)
这个系统还远未完善,你可以继续扩展它:
- 删除功能:添加删除学生、删除课程的功能(需要考虑关联数据)。
- 数据持久化:把数据保存到文件,下次启动时加载(我们会 I/O 时再讲)。
- 查找功能:按姓名、学号、课程名进行搜索。
- 统计分析:计算每门课程的平均分、最高分、最低分,按成绩排序等。
- 使用 Record 重构:把
GradeRecord改写成 Record,感受代码量的变化。 - 增加接口:定义
Gradable接口,让Student实现它,统一处理“有成绩”的对象。 - 增加用户输入校验:目前输入性别时如果输错会直接抛异常,可以加一个更友好的循环询问。
7. 写在最后
恭喜你完成了第三阶段的实战项目!
从第一篇的 Hello, World! 走到这里,你已经掌握了 Java 的核心——面向对象编程。你现在能够:
- 用类来建模现实世界的事物。
- 用封装来保护数据、隔离变化。
- 用继承来复用代码、建立层次关系。
- 用多态来写出灵活、可扩展的程序。
- 用抽象类和接口来定义契约和模板。
- 用枚举来表示固定选项。
- 用异常处理来让程序更健壮。
你写的代码,已经从一个“顺序执行的脚本”,变成了一个有结构、有层次、有逻辑、有防御的“小系统”。这是质的飞跃。
从下一篇文章开始,我们将进入第四阶段:进阶篇。我们会学习 Java 的标准类库中那些最常用的工具:集合框架、泛型、I/O、日期时间、Lambda 和 Stream……这些会让你的 Java 能力再上一个台阶。
休息一下,喝杯水,然后继续前进吧。
动手试试
- 把整个学生管理系统跑起来,创建几个学生、老师、课程,完成一次完整的“选课 → 录入成绩 → 查看报告”的流程。
- 在
SchoolManager中增加一个“按课程查看成绩排名”的功能:输入课程编号,列出所有选修该课程的学生及其成绩,按分数从高到低排序。 - 把
GradeRecord改写成 Record,调整Course类中相关代码,体会 Record 带来的简洁。 - 增加一个
Advisor接口(导师),包含giveAdvice(Student student)方法。让Teacher实现这个接口,表示老师可以给学生提供建议。然后在主菜单中增加“老师给学生建议”的功能。 - (挑战)思考这个系统的设计还有哪些可以改进的地方?比如
Student中的calculateAverage()遍历了所有课程的成绩记录,效率是不是可以优化?Course和Student之间的双向关联,有没有更好的设计方式?
我们下个阶段见。😃
📌 获取本系列示例代码请访问 GitCode。
&spm=1001.2101.3001.5002&articleId=162907292&d=1&t=3&u=bc9794c3bca841bbadec5a9933da3718)
1493

被折叠的 条评论
为什么被折叠?



