学生成绩管理系统
把类、封装、集合、循环、输入全用上,做一个能跑的完整程序。
第一步:设计数据
一个学生 = 一个 Student 对象。一群学生 = 一个 List<Student>。管理操作放进 StudentManager。
把"数据"和"对数据的管理"分成两个类,是真实项目的基本分层。Student 只负责描述一个学生,Manager 负责增删查改。
Student.java
public class Student {
private final String id;
private String name;
private double score;
public Student(String id, String name, double score) {
this.id = id;
this.name = name;
setScore(score);
}
public String getId() { return id; }
public String getName() { return name; }
public double getScore() { return score; }
public void setScore(double score) {
if (score < 0 || score > 100) {
throw new IllegalArgumentException("分数必须在 0~100 之间");
}
this.score = score;
}
public String getGrade() {
if (score >= 90) return "优秀";
if (score >= 80) return "良好";
if (score >= 60) return "及格";
return "不及格";
}
@Override
public String toString() {
return String.format("%-6s %-8s %6.1f %s", id, name, score, getGrade());
}
}
id 声明成 final,表示学号一旦确定终身不变——编译器替你守住这条业务规则。
StudentManager.java
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class StudentManager {
private final List<Student> students = new ArrayList<>();
public void add(Student s) {
if (findById(s.getId()) != null) {
System.out.println("学号已存在:" + s.getId());
return;
}
students.add(s);
System.out.println("已添加 " + s.getName());
}
public Student findById(String id) {
for (Student s : students) {
if (s.getId().equals(id)) { // 字符串比较用 equals
return s;
}
}
return null;
}
public boolean remove(String id) {
Student s = findById(id);
if (s == null) return false;
students.remove(s);
return true;
}
public void listAll() {
if (students.isEmpty()) {
System.out.println("暂无学生");
return;
}
System.out.println("学号 姓名 分数 等级");
for (Student s : students) {
System.out.println(s);
}
}
public void statistics() {
if (students.isEmpty()) return;
double total = 0;
for (Student s : students) {
total += s.getScore();
}
System.out.printf("人数 %d,平均分 %.2f%n", students.size(), total / students.size());
List<Student> sorted = new ArrayList<>(students);
sorted.sort(Comparator.comparingDouble(Student::getScore).reversed());
System.out.println("最高分:" + sorted.get(0));
}
}
注意 total / students.size():total 是 double,所以这里是小数除法。如果两个都是 int,平均分会被砍成整数——第二课的坑,在真实项目里就是这么出现的。
Main.java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
StudentManager manager = new StudentManager();
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("\n1 添加 2 全部 3 统计 4 删除 0 退出");
System.out.print("请选择:");
String choice = sc.nextLine().trim();
switch (choice) {
case "1":
System.out.print("学号:");
String id = sc.nextLine().trim();
System.out.print("姓名:");
String name = sc.nextLine().trim();
System.out.print("分数:");
try {
double score = Double.parseDouble(sc.nextLine().trim());
manager.add(new Student(id, name, score));
} catch (NumberFormatException e) {
System.out.println("分数得是数字");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
break;
case "2":
manager.listAll();
break;
case "3":
manager.statistics();
break;
case "4":
System.out.print("要删除的学号:");
System.out.println(manager.remove(sc.nextLine().trim()) ? "已删除" : "没找到");
break;
case "0":
System.out.println("再见");
sc.close();
return;
default:
System.out.println("没这个选项");
}
}
}
}
全程用 nextLine() 读取再自己转换,彻底绕开第三课那个换行符残留的坑。
怎么跑起来
三个文件放在同一目录:
javac *.java
java Main
自己动手改进
- 按分数排序后输出全部(提示:
sorted.sort(...)已经写好了,改造listAll) - 支持按姓名模糊查找(提示:
s.getName().contains(keyword)) - 把数据存到文件,重启后还在(提示:
java.io.PrintWriter写、Scanner读) - 问 AI 私教:"
Comparator.comparingDouble(Student::getScore)里的::是什么?" —— 那是方法引用,Java 8 的重要特性
10 秒自测
findById 里如果把 s.getId().equals(id) 写成 s.getId() == id,会怎样?
大多数时候找不到。== 比的是对象引用,而用户从 Scanner 输入的字符串是运行时新建的对象,和代码里存的不是同一个。字符串比较永远用 equals。