跳至正文

⚠️ 异常处理

📖 什么是异常?

**异常(Exception)**是程序执行过程中发生的错误。如果能够很好地处理异常,程序就能稳定运行而不会停止!

💡 基本try-catch

基本用法

fun main() {
try {
val result = 10 / 0 // 发生错误!
println(result)
} catch (e: Exception) {
println("发生错误:${e.message}")
}
// 发生错误:/ by zero

println("程序继续运行!")
}

处理特定异常

fun main() {
try {
val text = "abc"
val number = text.toInt() // NumberFormatException
} catch (e: NumberFormatException) {
println("无法转换为数字:${e.message}")
} catch (e: Exception) {
println("未知错误:${e.message}")
}
}

finally

fun main() {
try {
println("开始任务")
// 执行任务
} catch (e: Exception) {
println("发生错误")
} finally {
println("清理工作(总是执行)")
}
}

🎯 实战示例

安全的数字转换

fun safeToInt(text: String): Int? {
return try {
text.toInt()
} catch (e: NumberFormatException) {
null
}
}

fun main() {
println(safeToInt("123")) // 123
println(safeToInt("abc")) // null

val age = safeToInt("25") ?: 0
println("年龄:$age")
}

文件读取模拟

class FileReader {
fun readFile(filename: String): String {
if (filename.isEmpty()) {
throw IllegalArgumentException("文件名为空")
}
if (!filename.endsWith(".txt")) {
throw IllegalArgumentException("仅支持文本文件")
}
return "文件内容:$filename"
}
}

fun main() {
val reader = FileReader()

try {
val content = reader.readFile("data.txt")
println(content)
} catch (e: IllegalArgumentException) {
println("错误:${e.message}")
}

try {
reader.readFile("")
} catch (e: IllegalArgumentException) {
println("错误:${e.message}")
// 错误:文件名为空
}
}

用户输入验证

data class User(val name: String, val age: Int)

fun createUser(name: String, age: Int): User {
require(name.isNotBlank()) { "姓名为必填项" }
require(age > 0) { "年龄必须为正数" }
require(age < 150) { "年龄过大" }

return User(name, age)
}

fun main() {
try {
val user1 = createUser("张三", 25)
println(user1)

val user2 = createUser("", 25) // 错误!
} catch (e: IllegalArgumentException) {
println("验证失败:${e.message}")
// 验证失败:姓名为必填项
}
}

API响应处理

sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
}

fun divide(a: Int, b: Int): Result<Int> {
return try {
Result.Success(a / b)
} catch (e: ArithmeticException) {
Result.Error(e)
}
}

fun main() {
val result1 = divide(10, 2)
when (result1) {
is Result.Success -> println("结果:${result1.data}")
is Result.Error -> println("错误:${result1.exception.message}")
}

val result2 = divide(10, 0)
when (result2) {
is Result.Success -> println("结果:${result2.data}")
is Result.Error -> println("错误:${result2.exception.message}")
}
}

🔍 作为表达式使用

try作为值

fun main() {
val number = try {
"123".toInt()
} catch (e: NumberFormatException) {
0 // 默认值
}

println(number) // 123

val invalid = try {
"abc".toInt()
} catch (e: NumberFormatException) {
-1
}

println(invalid) // -1
}

链式调用

fun safeDivide(a: Int, b: Int): Int? {
return try {
a / b
} catch (e: ArithmeticException) {
null
}
}

fun main() {
val result = safeDivide(10, 2)
?.let { it * 2 }
?.let { it + 5 }
?: 0

println(result) // 15 (10/2 * 2 + 5)
}

🛡️ 编写安全的代码

require vs check

fun processOrder(quantity: Int, price: Int) {
// 输入验证
require(quantity > 0) { "数量必须为正数" }
require(price > 0) { "价格必须为正数" }

val total = quantity * price

// 状态验证
check(total < 1000000) { "总金额过大" }

println("订单完成:${total}元")
}

fun main() {
try {
processOrder(10, 5000) // 成功
processOrder(-1, 5000) // require失败
} catch (e: IllegalArgumentException) {
println("输入错误:${e.message}")
} catch (e: IllegalStateException) {
println("状态错误:${e.message}")
}
}

checkNotNull

fun processName(name: String?) {
val validName = checkNotNull(name) { "姓名为null" }
println("处理:$validName")
}

fun main() {
try {
processName("张三") // 成功
processName(null) // 失败
} catch (e: IllegalStateException) {
println("错误:${e.message}")
}
}

runCatching

从Kotlin 1.3开始提供的便捷方法!

fun main() {
// 使用runCatching
val result = runCatching {
"123".toInt()
}

result
.onSuccess { println("成功:$it") }
.onFailure { println("失败:${it.message}") }

// getOrNull
val number = runCatching { "abc".toInt() }.getOrNull()
println(number) // null

// getOrDefault
val safe = runCatching { "abc".toInt() }.getOrDefault(0)
println(safe) // 0
}

🎯 实用模式

重试逻辑

fun <T> retry(times: Int, block: () -> T): T? {
repeat(times) { attempt ->
try {
return block()
} catch (e: Exception) {
println("尝试${attempt + 1}失败:${e.message}")
if (attempt == times - 1) throw e
}
}
return null
}

fun main() {
var count = 0

try {
val result = retry(3) {
count++
if (count < 3) {
throw Exception("还没准备好")
}
"成功!"
}
println(result)
} catch (e: Exception) {
println("最终失败")
}
}

资源清理

class Resource : AutoCloseable {
init {
println("创建资源")
}

fun use() {
println("使用资源")
}

override fun close() {
println("清理资源")
}
}

fun main() {
try {
Resource().use { resource ->
resource.use()
// 自动调用close()
}
} catch (e: Exception) {
println("发生错误")
}
}

🤔 常见问题

Q1. 什么时候使用throw?

A: 需要明确抛出异常时!

fun validateAge(age: Int) {
if (age < 0) {
throw IllegalArgumentException("年龄不能为负数")
}
if (age > 150) {
throw IllegalArgumentException("年龄过大")
}
}

Q2. Exception vs RuntimeException?

A: Kotlin不区分!

// Java中区分checked/unchecked
// Kotlin中全部为unchecked(无需声明throws)

fun divide(a: Int, b: Int): Int {
return a / b // 无需声明throws!
}

Q3. 自定义异常?

A: 继承Exception!

class InvalidEmailException(message: String) : Exception(message)

fun validateEmail(email: String) {
if (!email.contains("@")) {
throw InvalidEmailException("无效的邮箱:$email")
}
}

fun main() {
try {
validateEmail("invalid")
} catch (e: InvalidEmailException) {
println(e.message)
}
}

🎬 总结

通过异常处理来创建稳定的程序吧!

核心总结:
✅ 使用try-catch处理异常
✅ 使用require/check进行验证
✅ 使用runCatching简化处理
✅ 作为表达式返回值
✅ 使用finally进行清理工作

下一步:在文件输入输出中学习如何处理文件!