学生成绩管理系统项目实践课程
一、项目背景
想象一下,你是一位学校的超级管理员,每天要面对成百上千学生的成绩信息。这些信息就像一堆杂乱无章的拼图,你需要把它们整理得井井有条,以便随时查看某个学生的成绩,或者统计某个科目的平均分。如果没有一个好的工具,那可就像在茫茫大海里捞针一样困难啦!这时候,我们的学生成绩管理系统就闪亮登场啦!
这个系统就像是一个智能的成绩小管家,它可以帮助你轻松地添加、删除、更新和查询学生的成绩信息。你只需要坐在电脑前,通过简单的操作,就可以让这个小管家帮你完成所有繁琐的工作。而且,它还能进行各种统计分析,比如找出成绩优秀的学生,或者统计每个科目的平均分。有了这个系统,你就可以把更多的时间花在享受生活上,而不是被这些枯燥的成绩信息折磨啦!
在这个项目中,我们将使用Scala语言来开发这个学生成绩管理系统。Scala是一种非常强大的编程语言,它结合了面向对象编程和函数式编程的优点,让我们可以用更简洁、更高效的方式来编写代码。通过这个项目,你将学习到Scala语言的各种特性,包括运算符、流程控制语句、数组、集合、面向对象编程、函数、模式匹配等等。就像掌握了一把神奇的钥匙,打开了Scala编程的大门,以后遇到各种编程问题都能轻松应对啦!
二、开发环境
(一)使用的工具及安装步骤
1. Java Development Kit (JDK)
Java 是 Scala 运行的基础,我们需要安装 JDK 来提供 Java 运行环境。
下载:访问如下网站:
Oracle 官方网站(https://www.oracle.com/java/technologies/javase-downloads.html)或
OpenJDK 官方网站(https://openjdk.java.net/)
根据自己的操作系统选择合适的 JDK 版本进行下载。
安装:下载完成后,运行安装程序,按照提示进行安装。在安装过程中,需要注意选择合适的安装路径,并配置环境变量。
配置环境变量:
在 Windows 系统中,右键点击“此电脑”,选择“属性”,然后点击“高级系统设置”,在“系统属性”窗口中点击“环境变量”。在“系统变量”中找到“Path”变量,点击“编辑”,将 JDK 的安装路径下的“bin”目录添加到“Path”变量中。
在 Linux 或 macOS 系统中,可以编辑 ~/.bashrc 或 ~/.bash_profile 文件,添加以下内容:
export JAVA_HOME=/path/to/your/jdk
export PATH=$JAVA_HOME/bin:$PATH
验证安装:
打开命令行工具,输入 java -version 和 javac -version,如果能正确显示 Java 版本信息,则说明 JDK 安装成功。如下图是windows机器上安装的JDK:

2. Scala
Scala 是我们本项目使用的开发语言。
下载:访问 Scala 官方网站(https://www.scala-lang.org/download/),选择合适的版本进行下载。
安装:下载完成后,解压文件到指定的目录。
配置环境变量:与 JDK 类似,在 Windows 系统中,将 Scala 的安装路径下的“bin”目录添加到“Path”变量中。在 Linux 或 macOS 系统中,编辑 ~/.bashrc 或 ~/.bash_profile 文件,添加以下内容:
export SCALA_HOME=/path/to/your/scala
export PATH=$SCALA_HOME/bin:$PATH
验证安装:打开命令行工具,输入 scala -version,如果能正确显示 Scala 版本信息,则说明 Scala 安装成功。如下是windows环境安装的scala:

注意版本
Scala 2.12.x 需要JDK 8.
Scala 2.13.x+ 需要 JDK 11+.
3. maven
maven是 Scala 的构建工具,它可以帮助我们管理项目的依赖、编译代码、运行测试等。
以下是 Maven 下载与安装的完整指南,覆盖 Windows、Linux、macOS 三大系统,并包含环境变量配置、镜像加速等优化技巧,帮助您快速完成部署:
Maven 下载与安装
1) Windows 系统
步骤 1:下载最新版本
访问 Apache Maven 官网,选择 Binary zip archive 下载。例如:apache-maven-3.6.3-bin.zip。
步骤 2:解压文件
将压缩包解压到指定目录(如 C:apache-maven-3.6.3)。
步骤 3:配置环境变量
右键「此电脑」→「属性」→「高级系统设置」→「环境变量」。
新建系统变量:
变量名:MAVEN_HOME
变量值:如 C:apache-maven-3.6.3
编辑 Path 变量:
新增:%MAVEN_HOME%in。
验证安装:
打开命令提示符,输入 mvn -version,若显示版本信息(如 Apache Maven 3.6.3)则安装成功。
2) Linux 系统
步骤 1:下载二进制包
终端执行以下命令(以 wget 为例):
wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz
(清华大学镜像源加速下载,替代官网慢的问题)。
步骤 2:解压与移动
tar -zxvf apache-maven-3.6.3-bin.tar.gz -C /usr/local/
sudo mv /usr/local/apache-maven-3.6.3 /usr/local/maven
步骤 3:配置环境变量
编辑 ~/.bashrc 文件:
nano ~/.bashrc
添加以下内容:
export MAVEN_HOME=/usr/local/maven
export PATH=$MAVEN_HOME/bin:$PATH
保存后执行 source ~/.bashrc 生效。
验证安装:
终端输入 mvn -version,显示版本信息即成功。
3)macOS 系统
步骤 1:下载二进制包
访问官网下载 apache-maven-3.6.3-bin.tar.gz,或使用 Homebrew 快速安装:
brew install maven
步骤 2:解压与配置
手动解压后,编辑 ~/.zshrc(或 ~/.bash_profile):
nano ~/.zshrc
添加:
export MAVEN_HOME=/Users/yourname/apache-maven-3.6.3
export PATH=$MAVEN_HOME/bin:$PATH
保存后执行 source ~/.zshrc。
验证安装:
终端输入 mvn -version 检查版本。
关键配置
1. 配置国内镜像源(加速依赖下载)
步骤 1:找到 settings.xml
Windows:C:Users你的用户名.m2settings.xml(若不存在则创建)。
Linux/macOS:~/.m2/settings.xml。
步骤 2:添加阿里云镜像
在 <mirrors> 标签内插入:
<mirror>
<id>aliyun-maven</id>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/repository/public</url>
<mirrorOf>central</mirrorOf>
</mirror>
其他镜像源(如华为云、腾讯云)可参考。
验证配置:
执行 mvn help:system,若输出中包含阿里云 URL 则生效。
配置本地仓库路径
步骤 1:创建仓库目录
在任意位置新建文件夹(如 D:maven-repo)。
步骤 2:修改 settings.xml
在 <settings> 标签内添加:
<localRepository>D:maven-repo</localRepository>
避免依赖包默认存放在系统盘占用空间。
4. IntelliJ IDEA (可选)
IntelliJ IDEA 是一款功能强大的集成开发环境,支持 Scala 开发。
下载:访问 JetBrains 官方网站(https://www.jetbrains.com/idea/download/),选择合适的版本进行下载。
安装:下载完成后,运行安装程序,按照提示进行安装。
创建 Scala 项目:点击“File” -> “New” -> “Project”,选择“Scala”,然后按照向导创建一个新的 Scala 项目。(或者直接打开现有项目)
配置maven:点击“File” -> “Settings” ,搜索“maven”,然后配置maven home以及settings文件和仓库位置。如果配置了maven之后,我们可以使用idea中集成的maven进行工程构建。

(二)使用的技术
Scala 语言
Scala 是一种多范式的编程语言,它融合了面向对象编程和函数式编程的特性。以下是 Scala 语言的一些重要特性:
运算符:Scala 支持各种常见的运算符,如算术运算符(+, -, *, /)、比较运算符(==, !=, <, >)、逻辑运算符(&&, ||, !)等。例如:
// 算术运算符
val a = 10
val b = 20
val sum = a + b // 加法运算,这里使用了算术运算符 +
// 比较运算符
val isEqual = a == b // 判断 a 是否等于 b,使用了比较运算符 ==
// 逻辑运算符
val isTrue = a < b && a > 0 // 逻辑与运算,使用了比较运算符 < 和 > 以及逻辑运算符 &&
流程控制语句:Scala 提供了 if-else、for、while 等流程控制语句。例如:
// if-else 语句
val num = 10
if (num > 0) {
println("num 是正数")
} else if (num < 0) {
println("num 是负数")
} else {
println("num 是零")
} // 使用了 if-else 流程控制语句来根据 num 的值输出不同的信息
// for 循环
val numbers = List(1, 2, 3, 4, 5)
for (num <- numbers) {
println(num)
} // 使用 for 循环遍历列表 numbers 中的每个元素并打印
// while 循环
var i = 0
while (i < 5) {
println(i)
i = i + 1
} // 使用 while 循环,只要 i 小于 5 就会一直执行循环体
数组和集合:Scala 提供了丰富的数组和集合类,如 Array、List、Set、Map 等。例如:
// 数组
val array = Array(1, 2, 3, 4, 5)
println(array(0)) // 访问数组的第一个元素,使用数组的索引访问元素,索引从 0 开始
// 列表
val list = List(1, 2, 3, 4, 5)
val newList = list.map(x => x * 2) // 对列表中的每个元素进行乘以 2 的操作,使用了列表的 map 方法,这是一个高阶函数,传入一个匿名函数 x => x * 2
// 集合
val set = Set(1, 2, 3, 4, 5)
val isExists = set.contains(3) // 判断集合中是否包含元素 3,使用集合的 contains 方法
// 映射
val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
val value = map.getOrElse("a", 0) // 获取键 "a" 对应的值,如果不存在则返回默认值 0,使用映射的 getOrElse 方法
面向对象编程:Scala 是一种面向对象的编程语言,支持类、对象、继承、多态等特性。例如:
// 定义一个类
class Person(val name: String, val age: Int) {
def sayHello(): Unit = {
println(s"Hello, my name is $name and I'm $age years old.")
}
} // 定义了一个 Person 类,使用了面向对象编程中的类的概念,构造函数参数使用 val 修饰表示不可变属性
// 创建对象
val person = new Person("Tom", 20)
person.sayHello() // 创建 Person 类的对象并调用其方法
// 继承
class Student(name: String, age: Int, val studentId: String) extends Person(name, age) {
def study(): Unit = {
println(s"I'm student $studentId and I'm studying.")
}
} // 定义了一个 Student 类,继承自 Person 类,使用了面向对象编程中的继承特性
val student = new Student("Jack", 18, "12345")
student.sayHello()
student.study() // 创建 Student 类的对象并调用其方法和父类的方法
函数:Scala 是一种函数式编程语言,函数是一等公民。可以将函数作为参数传递给其他函数,也可以返回函数。例如:
// 定义一个函数
def add(a: Int, b: Int): Int = {
a + b
} // 定义了一个名为 add 的函数,接受两个 Int 类型的参数并返回它们的和
// 函数作为参数传递
def operate(a: Int, b: Int, func: (Int, Int) => Int): Int = {
func(a, b)
} // 定义了一个高阶函数 operate,接受两个 Int 类型的参数和一个函数作为参数,并调用传入的函数
val result = operate(10, 20, add)
println(result) // 调用 operate 函数,将 add 函数作为参数传递
// 返回函数
def multiplier(factor: Int): (Int) => Int = {
(x: Int) => x * factor
} // 定义了一个函数 multiplier,接受一个 Int 类型的参数并返回一个函数
val double = multiplier(2)
val triple = multiplier(3)
println(double(5))
println(triple(5)) // 调用 multiplier 函数得到新的函数并调用这些新函数
模式匹配:Scala 的模式匹配是一种强大的语法,用于匹配不同的模式并执行相应的操作。例如:
val num = 2
num match {
case 1 => println("The number is 1")
case 2 => println("The number is 2")
case _ => println("The number is other")
} // 使用模式匹配根据 num 的值输出不同的信息
// 匹配列表
val list = List(1, 2, 3)
list match {
case head :: tail => println(s"The head is $head and the tail is $tail")
case _ => println("The list is empty")
} // 使用模式匹配匹配列表的结构,提取列表的头和尾元素
三、系统功能开发
(一)模型层 (Model)
功能设计
模型层就像是一个仓库,里面存放着我们系统中最重要的数据。在学生成绩管理系统中,这个仓库里存放的就是学生的信息。我们可以把学生想象成一个个小盒子,每个盒子里都装着学生的ID、姓名和成绩信息。这些信息就像是盒子里的宝贝,我们需要好好地保管它们。
在模型层中,我们定义了一个Student类,它就像是这些小盒子的模板。通过这个模板,我们可以创建出一个个具体的学生对象。每个学生对象都有自己的ID、姓名和成绩信息,而且这些信息是不可变的,就像盒子里的宝贝一旦放进去就不能轻易改变一样。这样可以保证数据的安全性和一致性。
代码设计
package com.example.model
/**
* 学生类 - 表示一个学生实体及其成绩信息
*
* @param id 学生ID
* @param name 学生姓名
* @param grades 学生各科目成绩,使用Map集合存储 (科目名称 -> 成绩)
*
* 使用了Scala的以下特性:
* - Scala语法:case class:不可变的值类,自动生成equals, hashCode, toString等方法
* - Scala语法:集合:使用Map存储科目和成绩
* - Scala语法:字符串插值:在toString方法中使用s"..."
*/
case class Student(id: String, name: String, grades: Map[String, Double]) {
// Scala语法:计算平均成绩,使用集合操作和运算符
def averageGrade: Double = {
if (grades.isEmpty) 0.0
else grades.values.sum / grades.size // Scala语法:使用集合的sum方法计算总和,然后除以科目数量
}
// Scala语法:获取最高分,使用集合操作
def highestGrade: Double = {
if (grades.isEmpty) 0.0
else grades.values.max // Scala语法:使用集合的max方法获取最高分
}
// Scala语法:获取最低分,使用集合操作
def lowestGrade: Double = {
if (grades.isEmpty) 0.0
else grades.values.min // Scala语法:使用集合的min方法获取最低分
}
// Scala语法:获取成绩等级,使用模式匹配和流程控制
def getGradeLevel(score: Double): String = {
// Scala语法:模式匹配示例
score match {
case s if s >= 90 => "A"
case s if s >= 80 => "B"
case s if s >= 70 => "C"
case s if s >= 60 => "D"
case _ => "F" // Scala语法:默认情况,使用通配符_
}
}
// Scala语法:判断某门课程是否及格,使用Option和模式匹配
def isPassingGrade(subject: String): Boolean = {
// Scala语法:使用Option避免空值问题,并使用模式匹配处理结果
grades.get(subject) match {
case Some(score) => score >= 60 // Scala语法:Some代表有值
case None => false // Scala语法:None代表无值
}
}
// Scala语法:格式化输出学生信息,使用字符串插值
override def toString: String = {
val avgGrade = averageGrade
val level = getGradeLevel(avgGrade)
s"""学生ID: $id
|姓名: $name
|课程成绩: ${grades.mkString(", ")}
|平均分: $avgGrade
|等级: $level
|""".stripMargin
}
}
代码解释
case class:Scala中的case class是一种特殊的类,它会自动生成equals、hashCode、toString等方法。这就像是给我们的小盒子贴上了一个标签,上面写着盒子里装的是什么,方便我们识别和比较。
Map集合:Map是一种键值对的集合,就像一个小字典,我们可以通过科目名称来查找对应的成绩。
集合操作:sum、max、min等方法是Scala集合提供的强大功能,它们可以让我们轻松地对集合中的元素进行求和、求最大值、求最小值等操作。
模式匹配:模式匹配就像是一个聪明的小侦探,它可以根据不同的情况做出不同的处理。在getGradeLevel方法中,我们根据学生的分数来判断他的成绩等级。
字符串插值:在toString方法中,我们使用了字符串插值,它可以让我们更方便地将变量的值插入到字符串中。
(二)服务层 (Service)
功能设计
服务层就像是一个超级大管家,它负责处理系统的核心业务逻辑。就像一个餐厅的厨师,根据顾客的订单来烹饪美食一样,服务层根据用户的请求来处理学生的成绩信息。它可以添加、删除、更新和查询学生的信息,还可以进行各种统计分析。
服务层使用单例对象来实现,这就像是餐厅里只有一个厨师,所有的订单都由他来处理。这样可以保证系统的一致性和安全性。同时,服务层还使用了可变状态管理,就像厨师可以根据顾客的需求随时调整菜品的口味一样,服务层可以根据用户的请求随时修改学生的信息。
代码设计
package com.example.service
import com.example.model.Student
import scala.collection.mutable
/**
* 学生成绩管理服务类 - 提供学生成绩管理的核心功能
*
* 使用了Scala的以下特性:
* - Scala语法:单例对象 (object):用于实现单例模式
* - Scala语法:可变集合 (mutable):使用可变Map存储学生信息
* - Scala语法:高阶函数:使用filter、map等函数式编程特性
* - Scala语法:集合操作:使用集合的各种操作方法
* - Scala语法:Option类型:处理可能不存在的值
*/
object GradeManagementService {
// Scala语法:使用可变Map存储学生信息,键为学生ID,值为Student对象
private val students = mutable.Map[String, Student]()
/**
* 添加学生信息
*
* @param student 要添加的学生对象
* @return 是否添加成功
*/
def addStudent(student: Student): Boolean = {
if (students.contains(student.id)) {
false // 如果学生ID已存在,返回false
} else {
students += (student.id -> student) // Scala语法:使用运算符+=添加键值对
true
}
}
/**
* 删除学生信息
*
* @param id 要删除的学生ID
* @return 是否删除成功
*/
def removeStudent(id: String): Boolean = {
if (students.contains(id)) {
students -= id // Scala语法:使用运算符-=删除键值对
true
} else {
false
}
}
/**
* 更新学生信息
*
* @param student 更新后的学生对象
* @return 是否更新成功
*/
def updateStudent(student: Student): Boolean = {
if (students.contains(student.id)) {
students(student.id) = student // 更新学生信息
true
} else {
false
}
}
/**
* 更新学生某科目成绩
*
* @param id 学生ID
* @param subject 科目名称
* @param grade 新成绩
* @return 是否更新成功
*/
def updateGrade(id: String, subject: String, grade: Double): Boolean = {
// Scala语法:使用get方法返回Option[Student],然后用模式匹配处理
students.get(id) match {
case Some(student) =>
// Scala语法:创建新的成绩Map (Scala中Map是不可变的,需要创建新的Map)
val updatedGrades = student.grades + (subject -> grade) // Scala语法:使用+运算符添加或更新键值对
// Scala语法:更新学生信息 (创建新的Student对象,因为Student是不可变的case class)
students(id) = student.copy(grades = updatedGrades) // Scala语法:使用case class的copy方法创建新实例
true
case None => false
}
}
/**
* 获取学生信息
*
* @param id 学生ID
* @return Option[Student],可能包含学生信息,也可能为None
*/
def getStudent(id: String): Option[Student] = {
students.get(id) // Scala语法:返回Option[Student]
}
/**
* 获取所有学生信息
*
* @return 包含所有学生的列表
*/
def getAllStudents(): List[Student] = {
students.values.toList // Scala语法:将Map的values转换为List
}
/**
* 按姓名查找学生 (模糊匹配)
*
* @param name 学生姓名 (部分)
* @return 符合条件的学生列表
*/
def findStudentsByName(name: String): List[Student] = {
// Scala语法:使用高阶函数filter进行过滤,并转换为List
students.values.filter(_.name.contains(name)).toList
}
/**
* 按成绩范围查询学生
*
* @param subject 科目
* @param minGrade 最低分
* @param maxGrade 最高分
* @return 符合条件的学生列表
*/
def findStudentsByGradeRange(subject: String, minGrade: Double, maxGrade: Double): List[Student] = {
// Scala语法:使用高阶函数filter和模式匹配结合
students.values.filter {
student =>
student.grades.get(subject) match {
case Some(grade) => grade >= minGrade && grade <= maxGrade
case None => false
}
}.toList
}
/**
* 获取成绩优秀的学生 (平均分90分以上)
*
* @return 成绩优秀的学生列表
*/
def getExcellentStudents(): List[Student] = {
students.values.filter(_.averageGrade >= 90).toList
}
/**
* 获取不及格的学生 (有任一科目低于60分)
*
* @return 有不及格科目的学生列表
*/
def getFailingStudents(): List[Student] = {
students.values.filter(student => student.grades.values.exists(_ < 60)).toList
}
/**
* 按照平均分排序所有学生 (从高到低)
*
* @return 排序后的学生列表
*/
def getSortedStudentsByAverage(): List[Student] = {
students.values.toList.sortBy(-_.averageGrade) // Scala语法:使用负号实现从高到低排序
}
/**
* 统计信息:计算某门课程的平均分
*
* @param subject 课程名称
* @return 该课程的平均分
*/
def getSubjectAverage(subject: String): Double = {
val grades = students.values
.flatMap(_.grades.get(subject)) // Scala语法:使用flatMap合并所有Option结果
.toList
if (grades.isEmpty) 0.0 else grades.sum / grades.size
}
/**
* 清空所有学生数据
*/
def clearAll(): Unit = {
students.clear() // 清空Map
}
}
代码解释
object:Scala中的object是单例对象,它只有一个实例。这就像是餐厅里只有一个厨师,所有的订单都由他来处理。
mutable.Map:可变集合就像一个可以随意增减物品的仓库,我们可以使用+=和-=运算符来添加和删除元素。
高阶函数:filter、map等高阶函数可以让我们更方便地对集合进行操作。就像一个超级筛子,可以根据我们的条件筛选出符合要求的元素。
Option类型:Option类型可以帮助我们处理可能不存在的值。就像一个神秘的盒子,里面可能有东西,也可能没有东西。我们可以使用模式匹配来处理这种情况。
(三)界面层 (UI)
功能设计
界面层就像是一个超级大舞台,用户可以在这个舞台上和系统进行互动。就像观众在剧院里和演员互动一样,用户可以通过界面层输入自己的需求,然后系统会根据用户的需求做出相应的回应。
界面层使用密封特质和模式匹配来处理用户的输入,就像一个聪明的翻译官,能够准确地理解用户的意图。同时,它还使用了函数式错误处理,就像一个贴心的小护士,能够及时发现并处理用户输入时可能出现的错误。另外,界面层还使用了递归控制流,就像一个永不停歇的小火车,不断地等待用户的输入并处理。
代码设计
package com.example.ui
import com.example.model.Student
import com.example.service.GradeManagementService
import scala.io.StdIn
import scala.util.{
Try, Success, Failure}
/**
* 控制台界面类 - 提供用户交互接口
*
* 使用了Scala的以下特性:
* - Scala语法:异常处理:使用Try、Success、Failure进行优雅的异常处理
* - Scala语法:模式匹配:处理不同的用户输入和操作结果
* - Scala语法:循环控制:使用while循环和递归实现交互逻辑
* - Scala语法:字符串插值:格式化输出信息
*/
object ConsoleUI {
// Scala语法:使用特质抽象不同的用户操作
sealed trait UserAction
case object Exit extends UserAction
case object AddStudent extends UserAction
case object RemoveStudent extends UserAction
case object UpdateStudent extends UserAction
case object QueryStudent extends UserAction
case object ListAllStudents extends UserAction
case object SearchStudents extends UserAction
case object ShowStatistics extends UserAction
case object InvalidAction extends UserAction
/**
* 启动用户界面
*/
def start(): Unit = {
println("欢迎使用学生成绩管理系统")
// Scala语法:使用递归实现主循环,避免使用可变状态
def mainLoop(): Unit = {
displayMenu()
val action = getUserAction()
// Scala语法:使用模式匹配处理不同的用户操作
action match {
case Exit =>
println("感谢使用学生成绩管理系统,再见!")
return
case AddStudent => handleAddStudent()
case RemoveStudent => handleRemoveStudent()
case UpdateStudent => handleUpdateStudent()
case QueryStudent => handleQueryStudent()
case ListAllStudents => handleListAllStudents()
case SearchStudents => handleSearchStudents()
case ShowStatistics => handleShowStatistics()
case InvalidAction =>
println("无效的选项,请重新输入")
}
mainLoop() // Scala语法:递归调用,继续主循环
}
// 加载一些示例数据
loadSampleData()
// 启动主循环
mainLoop()
}
/**
* 显示主菜单
*/
private def displayMenu(): Unit = {
println("
====== 学生成绩管理系统 ======")
println("1. 添加学生信息")
println("2. 删除学生信息")
println("3. 更新学生信息")
println("4. 查询学生信息")
println("5. 显示所有学生信息")
println("6. 搜索学生")
println("7. 显示统计信息")
println("0. 退出系统")
println("===========================")
print("请输入您的选择: ")
}
/**
* 获取用户操作
*
* @return 用户选择的操作
*/
private def getUserAction(): UserAction = {
// Scala语法:使用Try包装可能出现异常的操作
Try(StdIn.readLine().trim.toInt) match {
case Success(1) => AddStudent
case Success(2) => RemoveStudent
case Success(3) => UpdateStudent
case Success(4) => QueryStudent
case Success(5) => ListAllStudents
case Success(6) => SearchStudents
case Success(7) => ShowStatistics
case Success(0) => Exit
case _ => InvalidAction // Scala语法:处理无效输入
}
}
/**
* 处理添加学生操作
*/
private def handleAddStudent(): Unit = {
println("
=== 添加学生信息 ===")
// 输入学生基本信息
print("请输入学生ID: ")
val id = StdIn.readLine().trim
// 检查学生ID是否已存在
if (GradeManagementService.getStudent(id).isDefined) {
println(s"学生ID '$id' 已存在,无法添加")
return
}
print("请输入学生姓名: ")
val name = StdIn.readLine().trim
// Scala语法:使用可变Map收集成绩信息
val grades = scala.collection.mutable.Map[String, Double]()
// 输入成绩信息
println("请输入学生成绩 (输入空白科目名结束)")
var continue = true
while (continue) {
print("科目名称 (留空结束): ")
val subject = StdIn.readLine().trim
if (subject.isEmpty) {
continue = false
} else {
print(s"$subject 成绩: ")
// Scala语法:使用Try处理可能的输入错误
Try(StdIn.readLine().trim.toDouble) match {
case Success(grade) =>
grades += (subject -> grade)
println(s"已添加 $subject: $grade")
case Failure(_) =>
println("成绩必须是数字,请重新输入")
}
}
}
// Scala语法:创建学生对象并添加到系统
val student = Student(id, name, grades.toMap) // Scala语法:转换为不可变Map
if (GradeManagementService.addStudent(student)) {
println(s"学生 $name (ID: $id) 添加成功")
} else {
println("添加学生失败")
}
}
/**
* 处理删除学生操作
*/
private def handleRemoveStudent(): Unit = {
println("
=== 删除学生信息 ===")
print("请输入要删除的学生ID: ")
val id = StdIn.readLine().trim
// 确认删除
print(s"确定要删除学生 (ID: $id) 吗? (y/n): ")
val confirm = StdIn.readLine().trim.toLowerCase
if (confirm == "y") {
if (GradeManagementService.removeStudent(id)) {
println(s"学生 (ID: $id) 已成功删除")
} else {
println(s"学生 (ID: $id) 不存在")
}
} else {
println("已取消删除操作")
}
}
/**
* 处理更新学生操作
*/
private def handleUpdateStudent(): Unit = {
println("
=== 更新学生信息 ===")
print("请输入要更新的学生ID: ")
val id = StdIn.readLine().trim
// Scala语法:模式匹配获取学生信息
GradeManagementService.getStudent(id) match {
case Some(student) =>
println(s"找到学生: ${student.name}")
println("1. 更新某科目成绩")
println("2. 添加新科目成绩")
println("0. 返回主菜单")
print("请选择操作: ")
// Scala语法:模式匹配处理更新选项
Try(StdIn.readLine().trim.toInt) match {
case Success(1) => updateStudentGrade(id, student)
case Success(2) => addNewSubject(id, student)
case Success(0) => return
case _ => println("无效选项")
}
case None =>
println(s"学生 (ID: $id) 不存在")
}
}
/**
* 更新学生某科目成绩
*
* @param id 学生ID
* @param student 学生对象
*/
private def updateStudentGrade(id: String, student: Student): Unit = {
// 显示现有科目
println("现有科目:")
student.grades.foreach {
case (subject, grade) =>
println(s"$subject: $grade")
}
print("请输入要更新的科目: ")
val subject = StdIn.readLine().trim
// 检查科目是否存在
if (!student.grades.contains(subject)) {
println(s"科目 '$subject' 不存在")
return
}
print(s"请输入 $subject 新成绩: ")
// Scala语法:使用模式匹配处理输入结果
Try(StdIn.readLine().trim.toDouble) match {
case Success(grade) =>
if (GradeManagementService.updateGrade(id, subject, grade)) {
println(s"科目 $subject 成绩已更新为 $grade")
} else {
println("更新成绩失败")
}
case Failure(_) =>
println("成绩必须是数字")
}
}
/**
* 添加新科目成绩
*
* @param id 学生ID
* @param student 学生对象
*/
private def addNewSubject(id: String, student: Student): Unit = {
print("请输入新科目名称: ")
val subject = StdIn.readLine().trim
// 检查科目是否已存在
if (student.grades.contains(subject)) {
println(s"科目 '$subject' 已存在")
return
}
print(s"请输入 $subject 成绩: ")
// Scala语法:使用Try和模式匹配处理可能的异常
Try(StdIn.readLine().trim.toDouble) match {
case Success(grade) =>
if (GradeManagementService.updateGrade(id, subject, grade)) {
println(s"科目 $subject 成绩 $grade 已添加")
} else {
println("添加科目失败")
}
case Failure(_) =>
println("成绩必须是数字")
}
}
/**
* 处理查询学生操作
*/
private def handleQueryStudent(): Unit = {
println("
=== 查询学生信息 ===")
print("请输入要查询的学生ID: ")
val id = StdIn.readLine().trim
// Scala语法:模式匹配查询学生信息
GradeManagementService.getStudent(id) match {
case Some(student) =>
println(student.toString)
// 显示额外的统计信息
println(s"最高分: ${student.highestGrade}")
println(s"最低分: ${student.lowestGrade}")
case None =>
println(s"学生 (ID: $id) 不存在")
}
}
/**
* 处理列出所有学生操作
*/
private def handleListAllStudents(): Unit = {
println("
=== 所有学生信息 ===")
val students = GradeManagementService.getAllStudents()
if (students.isEmpty) {
println("当前没有学生信息")
} else {
// Scala语法:按照平均分排序
val sortedStudents = GradeManagementService.getSortedStudentsByAverage()
println(s"共有 ${students.size} 名学生 (按平均分排序):")
println("ID 姓名 平均分 等级")
println("----------------------------------")
// Scala语法:使用for表达式遍历学生
for (student <- sortedStudents) {
val avgGrade = student.averageGrade
val level = student.getGradeLevel(avgGrade)
println(f"${student.id}%s ${student.name}%s $avgGrade%.1f $level%s")
}
}
}
/**
* 处理搜索学生操作
*/
private def handleSearchStudents(): Unit = {
println("
=== 搜索学生 ===")
println("1. 按姓名搜索")
println("2. 按成绩范围搜索")
println("3. 查询成绩优秀学生 (平均分>=90)")
println("4. 查询有不及格科目的学生")
println("0. 返回主菜单")
print("请选择搜索方式: ")
// Scala语法:使用模式匹配处理不同的搜索选项
Try(StdIn.readLine().trim.toInt) match {
case Success(1) => searchByName()
case Success(2) => searchByGradeRange()
case Success(3) => searchExcellentStudents()
case Success(4) => searchFailingStudents()
case Success(0) => return
case _ => println("无效选项")
}
}
/**
* 按姓名搜索学生
*/
private def searchByName(): Unit = {
print("请输入学生姓名 (可部分匹配): ")
val name = StdIn.readLine().trim
val students = GradeManagementService.findStudentsByName(name)
if (students.isEmpty) {
println(s"没有找到姓名包含 '$name' 的学生")
} else {
println(s"找到 ${students.size} 名学生:")
students.foreach(student => println(student.toString))
}
}
/**
* 按成绩范围搜索学生
*/
private def searchByGradeRange(): Unit = {
print("请输入科目名称: ")
val subject = StdIn.readLine().trim
print("请输入最低分: ")
val minGrade = Try(StdIn.readLine().trim.toDouble).getOrElse(0.0)
print("请输入最高分: ")
val maxGrade = Try(StdIn.readLine().trim.toDouble).getOrElse(100.0)
val students = GradeManagementService.findStudentsByGradeRange(subject, minGrade, maxGrade)
if (students.isEmpty) {
println(s"没有找到 $subject 科目成绩在 $minGrade-$maxGrade 之间的学生")
} else {
println(s"找到 ${students.size} 名学生:")
students.foreach {
student =>
val grade = student.grades.getOrElse(subject, 0.0)
println(s"${student.id} ${student.name} ${subject}: $grade")
}
}
}
/**
* 搜索成绩优秀的学生
*/
private def searchExcellentStudents(): Unit = {
val students = GradeManagementService.getExcellentStudents()
if (students.isEmpty) {
println("没有平均分达到90分以上的学生")
} else {
println(s"找到 ${students.size} 名优秀学生:")
students.foreach {
student =>
println(s"${student.id} ${student.name} 平均分: ${student.averageGrade}")
}
}
}
/**
* 搜索有不及格科目的学生
*/
private def searchFailingStudents(): Unit = {
val students = GradeManagementService.getFailingStudents()
if (students.isEmpty) {
println("没有不及格科目的学生")
} else {
println(s"找到 ${students.size} 名有不及格科目的学生:")
students.foreach {
student =>
val failingSubjects = student.grades.filter(_._2 < 60).keys.mkString(", ")
println(s"${student.id} ${student.name} 不及格科目: $failingSubjects")
}
}
}
/**
* 处理显示统计信息操作
*/
private def handleShowStatistics(): Unit = {
println("
=== 统计信息 ===")
val students = GradeManagementService.getAllStudents()
if (students.isEmpty) {
println("当前没有学生信息")
return
}
// Scala语法:获取所有科目
val allSubjects = students.flatMap(_.grades.keys).toSet
// Scala语法:显示每个科目的平均分
println("各科目平均分:")
for (subject <- allSubjects) {
val average = GradeManagementService.getSubjectAverage(subject)
println(f"$subject%s: $average%.2f")
}
// Scala语法:统计平均分段分布
val gradeRanges = List((90.0, 100.0), (80.0, 89.9), (70.0, 79.9), (60.0, 69.9), (0.0, 59.9))
val distribution = gradeRanges.map {
case (min, max) =>
val count = students.count(s => s.averageGrade >= min && s.averageGrade <= max)
(f"$min%.0f-$max%.1f", count)
}
println("
平均分分布:")
for ((range, count) <- distribution) {
val percentage = count * 100.0 / students.size
println(f"$range%s: $count%d 人 ($percentage%.1f%%)")
}
}
/**
* 加载示例数据
*/
private def loadSampleData(): Unit = {
// Scala语法:添加一些示例学生数据
val student1 = Student(
"S001",
"张三",
Map("数学" -> 85.0, "语文" -> 92.0, "英语" -> 78.0, "物理" -> 90.0)
)
val student2 = Student(
"S002",
"李四",
Map("数学" -> 92.0, "语文" -> 85.0, "英语" -> 95.0, "物理" -> 88.0)
)
val student3 = Student(
"S003",
"王五",
Map("数学" -> 78.0, "语文" -> 82.0, "英语" -> 65.0, "物理" -> 75.0)
)
val student4 = Student(
"S004",
"赵六",
Map("数学" -> 95.0, "语文" -> 92.0, "英语" -> 90.0, "物理" -> 94.0)
)
val student5 = Student(
"S005",
"钱七",
Map("数学" -> 59.0, "语文" -> 72.0, "英语" -> 63.0, "物理" -> 58.0)
)
// Scala语法:添加学生到系统
List(student1, student2, student3, student4, student5).foreach(GradeManagementService.addStudent)
}
}
四、部署运行本系统
1、构建和启动项目
自动构建和启动项目:
在Windows上:双击run.bat
在Linux/macOS上:执行sh run.sh或chmod +x run.sh && ./run.sh
使用maven构建和启动项目:
使用Maven命令:mvn clean package && java -jar target/student-grade-system-1.0-SNAPSHOT-jar-with-dependencies.jar

2、学生成绩管理操作
显示所有学生:
![图片[1] - 学生成绩管理系统项目实践课程 - 宋马](https://pic.songma.com/blogimg/20250428/429144d7a66541a79c7fbc60ebf83b03.png)
增加学生信息:
![图片[2] - 学生成绩管理系统项目实践课程 - 宋马](https://pic.songma.com/blogimg/20250428/aeadbd7aea0e44b395f8f6a3a1b14989.png)
![图片[3] - 学生成绩管理系统项目实践课程 - 宋马](https://pic.songma.com/blogimg/20250428/09980f6550034474a9c5b7cb64cdb828.png)
搜索学生:
![图片[4] - 学生成绩管理系统项目实践课程 - 宋马](https://pic.songma.com/blogimg/20250428/8cb1ee39625c43e99daa5da27aa06bfe.png)
显示统计信息:
![图片[5] - 学生成绩管理系统项目实践课程 - 宋马](https://pic.songma.com/blogimg/20250428/4754a72ece324b7d8ab7f179c163f700.png)
查询学生信息:
![图片[6] - 学生成绩管理系统项目实践课程 - 宋马](https://pic.songma.com/blogimg/20250428/2961188bd28445068ccf528153b45d96.png)
更新学生信息:
![图片[7] - 学生成绩管理系统项目实践课程 - 宋马](https://pic.songma.com/blogimg/20250428/bc70bc1917ff4bbc81bf9590302cd4d6.png)

五、项目总体设计
(一)设计原则
(1)分层架构设计是本系统设计的核心原则之一。我们将系统划分为模型层、服务层和界面层。模型层(如Student.scala中的Student类)主要负责数据的表示和封装,它定义了学生实体及其相关属性和方法,如学生的基本信息、成绩以及平均分、最高分、最低分等计算方法。服务层(如GradeManagementService.scala中的GradeManagementService对象)承担了系统的业务逻辑处理,包括学生信息的添加、删除、更新、查询等操作。界面层(如ConsoleUI.scala中的ConsoleUI对象)则负责与用户进行交互,接收用户的输入并展示处理结果。通过这种分层架构,不同层次的关注点得到了有效的分离,使得每个层次的代码更加清晰、易于维护和扩展。
(2)单一职责原则要求每个类和方法只负责一项功能。在本系统中,每个类和方法都有明确的职责。例如,Student类只负责表示学生实体及其相关属性和方法,不涉及业务逻辑处理;GradeManagementService对象中的每个方法只负责一项具体的业务操作,如addStudent方法只负责添加学生信息,removeStudent方法只负责删除学生信息等。这样的设计使得代码的可读性和可维护性大大提高,当需要修改某个功能时,只需要修改相应的类和方法,而不会影响到其他部分的代码。
(3)开闭原则强调系统应该易于扩展,而无需修改现有代码。在本系统中,我们通过抽象和接口的方式来实现这一原则。例如,GradeManagementService对象中的方法都是基于抽象的业务逻辑进行设计的,当需要添加新的业务功能时,只需要在服务层添加新的方法,而不需要修改现有的方法和其他层次的代码。同时,Student类作为数据模型,也具有良好的扩展性,当需要添加新的学生属性时,只需要在Student类中添加相应的字段和方法即可。
(4)函数式编程风格在本系统中得到了充分的体现。我们大量使用了高阶函数和不可变数据结构。例如,在GradeManagementService对象中,使用了filter、map等高阶函数来处理集合数据,使得代码更加简洁、高效。同时,Student类中的grades属性使用了不可变的Map集合,避免了数据的意外修改,提高了代码的安全性和可维护性。
(5)面向对象与函数式编程结合是Scala语言的一大优势,我们充分利用了这一优势。在本系统中,Student类采用了面向对象的设计思想,将学生的属性和行为封装在一起;而在服务层和界面层,我们则大量使用了函数式编程的特性,如高阶函数、模式匹配等,使得代码更加简洁、灵活。
(6)领域驱动设计思想指导我们根据业务领域创建合理的模型。在本系统中,Student类就是根据学生成绩管理的业务领域创建的模型,它准确地反映了学生的业务特征和行为。通过领域驱动设计,我们能够更好地理解业务需求,设计出更加符合实际业务的系统。
(二)设计总流程
本系统的数据处理流程主要围绕用户输入展开,通过一系列的模块协作,最终将处理结果展示给用户。
首先,用户在界面上进行输入操作,这些输入可能是添加学生信息、删除学生信息、查询学生信息等各种命令。用户输入的数据首先到达ConsoleUI模块,该模块负责解析用户输入的命令。在ConsoleUI中,通过getUserAction方法将用户输入的数字转换为对应的操作类型,如AddStudent、RemoveStudent等。使用Try和模式匹配来处理可能的输入异常,确保系统的健壮性。
解析后的命令被传递到GradeManagementService模块,该模块是系统的业务逻辑处理核心。GradeManagementService根据不同的命令类型,调用相应的方法来处理业务逻辑。例如,如果用户输入的是添加学生信息的命令,GradeManagementService会调用addStudent方法,检查学生ID是否已存在,如果不存在则将学生信息添加到系统中。
在处理业务逻辑的过程中,GradeManagementService会与Student模块进行交互。Student模块负责数据的表示,它定义了学生的基本信息和成绩信息。GradeManagementService会根据业务需求创建、修改或查询Student对象。例如,在添加学生信息时,GradeManagementService会创建一个新的Student对象,并将其存储到系统中。
业务逻辑处理完成后,GradeManagementService会将处理结果返回给ConsoleUI模块。ConsoleUI模块负责将处理结果展示给用户。例如,如果添加学生信息成功,ConsoleUI会输出“学生 [姓名] (ID: [ID]) 添加成功”的提示信息;如果查询学生信息不存在,ConsoleUI会输出“学生 (ID: [ID]) 不存在”的提示信息。
最后,处理结果通过ConsoleUI展示在用户界面上,用户可以根据展示的结果进行下一步的操作。整个流程形成了一个闭环,确保了用户输入的命令能够得到正确的处理和反馈。通过这种分层和模块化的设计,系统的各个部分职责明确,易于维护和扩展。当需要添加新的业务功能时,只需要在相应的模块中添加新的代码,而不会影响到其他部分的功能。


















暂无评论内容