本节课将以 "实战驱动" 为核心,通过 3 个由浅入深的案例 / 项目,帮你把 Kotlin 基础语法、面向对象、集合、高阶函数、IO 流等知识点串联起来,真正做到 "学完能用"。每个案例都会先明确需求,再分步实现,最后总结核心知识点,确保你能跟上节奏并理解背后的逻辑。
一、实战案例一:简易计算器(基础语法综合应用)
计算器是入门级实战的经典案例,能帮你巩固函数定义、条件判断、输入输出、异常处理等基础语法,同时培养 "边界场景思考" 能力(比如输入不合法、除数为零)。
1. 需求分析
明确计算器的核心功能和边界:
- 支持 加减乘除 4 种运算(运算符仅支持
+
-
*
/
); - 处理输入合法性:
- 确保用户输入的 "数字" 是有效数字(比如拒绝 "abc""12a" 这类无效值);
- 除法运算时,确保除数不为 0;
- 支持 "多次计算",用户可选择继续计算或退出。
2. 分步实现
步骤 1:定义计算核心函数
先封装一个 "计算逻辑" 函数,接收两个数字和运算符,返回计算结果或错误信息(用密封类区分 "成功" 和 "失败",比直接返回 null 更清晰):
Kotlin
// 密封类:区分计算结果的"成功"与"失败"
sealed class CalculationResult {
data class Success(val result: Double) : CalculationResult() // 成功:携带结果
data class Error(val message: String) : CalculationResult() // 失败:携带错误信息
}
// 计算函数:接收两个数字、运算符,返回计算结果
fun calculate(num1: Double, num2: Double, operator: Char): CalculationResult {
return when (operator) {
'+' -> CalculationResult.Success(num1 + num2)
'-' -> CalculationResult.Success(num1 - num2)
'*' -> CalculationResult.Success(num1 * num2)
'/' -> {
if (num2 == 0.0) {
CalculationResult.Error("错误:除数不能为0!")
} else {
CalculationResult.Success(num1 / num2)
}
}
else -> CalculationResult.Error("错误:不支持的运算符(仅支持 + - * /)")
}
}
步骤 2:处理用户输入(封装输入验证逻辑)
用户输入可能不合法(比如输 "abc" 当数字),所以封装两个工具函数:readValidNumber
(读取有效数字)和 readValidOperator
(读取有效运算符):
Kotlin
// 读取有效数字:直到用户输入合法数字才返回
fun readValidNumber(prompt: String): Double {
while (true) {
print(prompt) // 提示用户输入(比如"请输入第一个数字:")
val input = readLine() // 读取控制台输入
// 尝试将输入转为Double,失败则提示重新输入
val number = input?.toDoubleOrNull()
if (number != null) {
return number
} else {
println("输入无效!请输入一个合法的数字(比如 123 或 45.6)")
}
}
}
// 读取有效运算符:直到用户输入 +-*/ 才返回
fun readValidOperator(): Char {
while (true) {
print("请输入运算符(+ - * /):")
val input = readLine()
// 校验:输入不为空、长度为1、且是合法运算符
if (input != null && input.length == 1 && input[0] in listOf('+', '-', '*', '/')) {
return input[0]
} else {
println("输入无效!请输入 + - * / 中的一个运算符")
}
}
}
步骤 3:实现主逻辑(串联输入、计算、输出)
写一个主函数,实现 "循环计算" 的逻辑:用户计算一次后,可选择继续或退出:
Kotlin
fun main() {
println("=== 简易计算器 ===")
while (true) {
// 1. 读取用户输入(通过工具函数确保输入合法)
val num1 = readValidNumber("请输入第一个数字:")
val num2 = readValidNumber("请输入第二个数字:")
val operator = readValidOperator()
// 2. 调用计算函数,处理结果
val result = calculate(num1, num2, operator)
when (result) {
is CalculationResult.Success -> println("计算结果:$num1 $operator $num2 = ${result.result}")
is CalculationResult.Error -> println(result.message) // 打印错误信息
}
// 3. 询问用户是否继续
print("是否继续计算?(y/n):")
val continueInput = readLine()?.lowercase()
if (continueInput != "y") {
println("计算器已退出,再见!")
break // 退出循环
}
println("-------------------") // 分割线,优化体验
}
}
二、实战案例二:学生信息管理系统(面向对象与集合综合应用)
这个案例会用到 Kotlin 的面向对象(OOP) 和集合(Collection) ,模拟 "学生信息的增删改查",贴近实际开发中的 "数据管理" 场景。
1. 需求分析
核心功能是对 "学生信息" 的 4 个操作:
- 添加学生:输入学生的 ID、姓名、年龄、成绩,确保 ID 唯一(不重复添加);
- 查询学生:支持按 "学生 ID" 查询,返回该学生的所有信息;
- 修改学生:按 ID 找到学生后,修改其姓名、年龄、成绩(ID 不可改);
- 删除学生:按 ID 删除学生,删除前确认是否存在该学生;
- 所有学生信息保存在内存中(用集合存储),支持控制台交互。
2. 分步实现
步骤 1:定义 Student 数据类(封装学生属性)
Kotlin 的data class
会自动生成equals()
、hashCode()
、toString()
等方法,非常适合存储 "数据模型"(比如学生、用户):
Kotlin
// 学生数据类:id(唯一标识)、name、age、score
data class Student(
val id: String, // ID不可改(用val)
var name: String, // 姓名可改(用var)
var age: Int, // 年龄可改
var score: Double // 成绩可改
) {
// 重写toString(),自定义打印格式(更易读)
override fun toString(): String {
return "学生信息:ID=$id,姓名=$name,年龄=$age,成绩=$score"
}
}
步骤 2:实现 StudentManager 管理类(封装增删改查逻辑)
用 "管理类" 封装对学生集合的操作,避免业务逻辑散落在主函数中,符合 "单一职责原则":
Kotlin
import java.util.* // 导入集合相关类
class StudentManager {
// 用MutableList存储学生(支持增删改),初始为空
private val studentList: MutableList<Student> = mutableListOf()
// 1. 添加学生:确保ID唯一
fun addStudent(student: Student): String {
// 判断ID是否已存在(用any()检查集合中是否有相同ID的学生)
val isIdExists = studentList.any { it.id == student.id }
return if (isIdExists) {
"添加失败:学生ID=${student.id}已存在!"
} else {
studentList.add(student)
"添加成功!当前学生总数:${studentList.size}"
}
}
// 2. 查询学生:按ID查询(返回Student?,null表示未找到)
fun queryStudentById(id: String): Student? {
// 用find()找到第一个匹配ID的学生
return studentList.find { it.id == id }
}
// 3. 修改学生:按ID修改姓名、年龄、成绩
fun updateStudent(id: String, newName: String, newAge: Int, newScore: Double): String {
val studentToUpdate = queryStudentById(id)
return if (studentToUpdate == null) {
"修改失败:未找到ID=$id的学生!"
} else {
// 修改属性(因为name/age/score是var)
studentToUpdate.name = newName
studentToUpdate.age = newAge
studentToUpdate.score = newScore
"修改成功!${studentToUpdate}"
}
}
// 4. 删除学生:按ID删除
fun deleteStudent(id: String): String {
val studentToDelete = queryStudentById(id)
return if (studentToDelete == null) {
"删除失败:未找到ID=$id的学生!"
} else {
studentList.remove(studentToDelete)
"删除成功!当前学生总数:${studentList.size}"
}
}
// 辅助功能:查看所有学生
fun showAllStudents(): String {
return if (studentList.isEmpty()) {
"当前无学生信息!"
} else {
"=== 所有学生信息 ===\n" + studentList.joinToString(separator = "\n")
}
}
}
步骤 3:实现控制台交互逻辑(主函数)
通过 "菜单" 让用户选择操作,串联管理类的方法:
Kotlin
fun main() {
val studentManager = StudentManager()
println("=== 学生信息管理系统 ===")
while (true) {
// 打印菜单
println("\n请选择操作:")
println("1. 添加学生")
println("2. 查询学生(按ID)")
println("3. 修改学生信息")
println("4. 删除学生(按ID)")
println("5. 查看所有学生")
println("6. 退出系统")
print("请输入操作编号(1-6):")
// 读取用户选择的操作编号
val choice = readLine()?.toIntOrNull() ?: 0 // 输入无效时默认0
// 根据选择执行对应操作
when (choice) {
1 -> addStudentMenu(studentManager) // 调用添加学生的子逻辑
2 -> queryStudentMenu(studentManager) // 调用查询学生的子逻辑
3 -> updateStudentMenu(studentManager) // 调用修改学生的子逻辑
4 -> deleteStudentMenu(studentManager) // 调用删除学生的子逻辑
5 -> println(studentManager.showAllStudents())
6 -> {
println("系统已退出,再见!")
break
}
else -> println("操作编号无效!请输入1-6之间的数字")
}
}
}
// 子逻辑:添加学生(拆分主函数代码,更清晰)
fun addStudentMenu(manager: StudentManager) {
print("请输入学生ID:")
val id = readLine() ?: ""
print("请输入学生姓名:")
val name = readLine() ?: ""
print("请输入学生年龄:")
val age = readLine()?.toIntOrNull() ?: 0
print("请输入学生成绩:")
val score = readLine()?.toDoubleOrNull() ?: 0.0
// 校验必要信息(ID和姓名不能为空)
if (id.isBlank() || name.isBlank()) {
println("添加失败:ID和姓名不能为空!")
return
}
if (age <= 0 || age > 150) {
println("添加失败:年龄必须在1-150之间!")
return
}
if (score < 0 || score > 100) {
println("添加失败:成绩必须在0-100之间!")
return
}
// 调用管理类的添加方法
val result = manager.addStudent(Student(id, name, age, score))
println(result)
}
// 子逻辑:查询学生(按ID)
fun queryStudentMenu(manager: StudentManager) {
print("请输入要查询的学生ID:")
val id = readLine() ?: ""
val student = manager.queryStudentById(id)
if (student != null) {
println(student)
} else {
println("未找到ID=$id的学生!")
}
}
// 子逻辑:修改学生信息
fun updateStudentMenu(manager: StudentManager) {
print("请输入要修改的学生ID:")
val id = readLine() ?: ""
// 先查询学生是否存在
val student = manager.queryStudentById(id)
if (student == null) {
println("未找到ID=$id的学生!")
return
}
// 输入新信息(可保留原信息,比如按回车不修改)
print("请输入新姓名(原姓名:${student.name}):")
val newName = readLine()?.takeIf { it.isNotBlank() } ?: student.name
print("请输入新年龄(原年龄:${student.age}):")
val newAge = readLine()?.toIntOrNull() ?: student.age
print("请输入新成绩(原成绩:${student.score}):")
val newScore = readLine()?.toDoubleOrNull() ?: student.score
// 校验新信息
if (newAge <= 0 || newAge > 150) {
println("修改失败:年龄必须在1-150之间!")
return
}
if (newScore < 0 || newScore > 100) {
println("修改失败:成绩必须在0-100之间!")
return
}
// 调用管理类的修改方法
val result = manager.updateStudent(id, newName, newAge, newScore)
println(result)
}
// 子逻辑:删除学生(按ID)
fun deleteStudentMenu(manager: StudentManager) {
print("请输入要删除的学生ID:")
val id = readLine() ?: ""
val student = manager.queryStudentById(id)
if (student == null) {
println("未找到ID=$id的学生!")
return
}
// 二次确认(避免误删)
print("确定要删除学生 ${student.name}(ID=$id)吗?(y/n):")
val confirm = readLine()?.lowercase()
if (confirm == "y") {
val result = manager.deleteStudent(id)
println(result)
} else {
println("已取消删除操作!")
}
}
三、实战项目:文本处理工具(高阶函数与 IO 流应用)
这个项目难度稍高,会用到 Kotlin 的高阶函数 和IO 流(文件读写),模拟实际开发中的 "文本处理" 场景(比如统计文章字数、查找关键词)。
1. 需求分析
核心功能是对 "文本文件" 的处理:
- 读取文本文件:支持从指定路径读取.txt 文件(处理 "文件不存在""权限不足" 等异常);
- 文本统计:统计文件的 "总行数" 和 "总字数"(字数按空格 / 换行分割,过滤空字符);
- 关键词查找:查找文件中包含指定关键词的所有行,返回行号和内容;
- 生成处理报告 :将统计结果和关键词查找结果,写入新的报告文件(如
report.txt
); - 支持用户输入文件路径、关键词,交互友好。
2. 分步实现(附完整代码)
步骤 1:导入 IO 相关依赖
Kotlin 的 IO 操作依赖kotlin.io
包(默认包含,无需额外导入),但文件操作需要java.io.File
类,所以先导入:
kotlin
import java.io.File
import java.io.FileNotFoundException
import kotlin.io.path.exists
步骤 2:实现文件读取与写入(处理异常)
文件操作可能出现异常(如文件不存在、权限不够),用try-catch
捕获,避免程序崩溃:
Kotlin
/**
* 读取文件内容
* @param filePath 文件路径(如 "D:/test.txt")
* @return 成功返回文件内容(String),失败返回错误信息(String)
*/
fun readFileContent(filePath: String): String {
return try {
// 用Kotlin扩展函数readText()读取文件(自动处理流关闭)
File(filePath).readText(Charsets.UTF_8)
} catch (e: FileNotFoundException) {
"读取失败:文件不存在(路径:$filePath)"
} catch (e: SecurityException) {
"读取失败:权限不足,无法访问文件"
} catch (e: Exception) {
"读取失败:未知错误(${e.message})"
}
}
/**
* 写入报告到文件
* @param reportContent 报告内容
* @param outputPath 输出路径(默认 "report.txt",在项目根目录)
* @return 成功返回true,失败返回false
*/
fun writeReport(reportContent: String, outputPath: String = "report.txt"): Boolean {
return try {
// 用Kotlin扩展函数writeText()写入文件
File(outputPath).writeText(reportContent, Charsets.UTF_8)
println("报告已生成:$outputPath")
true
} catch (e: SecurityException) {
println("写入失败:权限不足,无法创建文件")
false
} catch (e: Exception) {
println("写入失败:未知错误(${e.message})")
false
}
}
步骤 3:实现文本统计函数(用高阶函数简化逻辑)
统计 "行数" 和 "字数",用高阶函数let
/run
简化判空和流程:
Kotlin
/**
* 统计文本的行数和字数
* @param content 文本内容(从文件读取的字符串)
* @return Pair(总行数, 总字数)
*/
fun countTextInfo(content: String): Pair<Int, Int> {
// 用run函数串联操作:先分割行,再统计
return content.run {
// 统计行数:按换行符分割,过滤空行(如连续换行)
val lines = split("\n", "\r\n") // 兼容Windows(\r\n)和Linux(\n)换行
val lineCount = lines.filter { it.isNotBlank() }.size
// 统计字数:按空格/制表符分割,过滤空字符
val wordCount = lines.flatMap { line ->
line.split("\s+".toRegex()) // 正则:匹配1个或多个空白字符
}.filter { it.isNotBlank() }.size
Pair(lineCount, wordCount) // 返回行数和字数的配对
}
}
步骤 4:实现关键词查找函数(返回行号和内容)
查找包含关键词的行,返回 "行号 + 行内容" 的列表,支持忽略大小写:
Kotlin
/**
* 查找包含关键词的行
* @param content 文本内容
* @param keyword 关键词
* @param caseSensitive 是否区分大小写(默认false,不区分)
* @return 匹配的行列表(格式:"行号:内容")
*/
fun findKeywordLines(content: String, keyword: String, caseSensitive: Boolean = false): List<String> {
if (keyword.isBlank()) {
return listOf("关键词不能为空!")
}
// 分割行,遍历每一行(记录行号)
return content.split("\n", "\r\n")
.withIndex() // 给每一行添加索引(行号,从1开始)
.filter { (index, line) ->
// 判断行是否包含关键词(处理大小写)
val lineToCheck = if (caseSensitive) line else line.lowercase()
val keywordToCheck = if (caseSensitive) keyword else keyword.lowercase()
lineToCheck.contains(keywordToCheck)
}
.map { (index, line) ->
// 格式化结果:行号(索引+1)+ 内容
"第${index + 1}行:$line"
}
}
步骤 5:实现主逻辑(串联所有功能)
让用户输入文件路径、关键词,生成完整报告:
Kotlin
fun main() {
println("=== 文本处理工具 ===")
// 1. 读取用户输入的文件路径
print("请输入要处理的文本文件路径(如 D:/test.txt):")
val filePath = readLine() ?: ""
if (filePath.isBlank()) {
println("路径不能为空!程序退出。")
return
}
// 2. 读取文件内容
println("\n正在读取文件...")
val fileContent = readFileContent(filePath)
// 判断是否读取成功(成功的内容不会包含"失败"关键词,简单判断)
if (fileContent.startsWith("读取失败")) {
println(fileContent)
return
}
println("文件读取成功(内容长度:${fileContent.length} 字符)")
// 3. 统计文本信息
println("\n正在统计文本信息...")
val (lineCount, wordCount) = countTextInfo(fileContent)
val statsReport = "=== 文本统计结果 ===\n" +
"总行数(非空):$lineCount\n" +
"总字数(非空):$wordCount\n"
// 4. 关键词查找
print("\n请输入要查找的关键词(留空则不查找):")
val keyword = readLine() ?: ""
val keywordReport = if (keyword.isNotBlank()) {
println("正在查找关键词:$keyword...")
val matchedLines = findKeywordLines(fileContent, keyword)
"\n=== 关键词查找结果(关键词:$keyword)===\n" +
if (matchedLines.isEmpty()) {
"未找到包含关键词的行\n"
} else {
"匹配行数:${matchedLines.size}\n" + matchedLines.joinToString(separator = "\n") + "\n"
}
} else {
"\n(未指定关键词,跳过查找)\n"
}
// 5. 生成完整报告
val fullReport = statsReport + keywordReport +
"=== 报告生成时间 ===" + "\n" +
"处理文件路径:$filePath\n"
// 6. 打印报告并写入文件
println("\n=== 完整处理报告 ===")
println(fullReport)
writeReport(fullReport)
println("\n处理完成!")
}