第10篇 异常处理——程序的容错机制 仓颉原生中文编程
第10篇 异常处理——程序的容错机制**作者**中文编程倡导者—— 李金雨联系方式wbtm2718qq.com**目标读者**编程入门零基础核心理念使用华为仓颉原生中文编程体验真正的国产编程语言一、什么是异常想象一下这些场景你打开一个文件但文件不存在你输入abc但程序期待一个数字你访问数组的第100个元素但数组只有10个元素你做除法时除数是0这些情况都会导致程序出错专业术语叫做抛出异常Throw Exception。如果不处理程序就会崩溃退出。异常处理就是让程序在遇到错误时能够优雅地处理而不是直接死掉。二、生活中的异常处理例子1餐厅点餐正常情况点了一份炒饭厨师做好了端上来异常情况厨师发现米饭用完了处理方式服务员告诉你抱歉米饭用完了要不要试试面条例子2ATM取钱正常情况输入密码正确取出现金异常情况密码错误、余额不足、机器故障处理方式显示友好的提示信息而不是让机器死机三、仓颉语言中的异常处理仓颉使用try-catch机制来处理异常。基础语法import std.console.* import std.convert.* func 安全地除法(被除数: Int64, 除数: Int64): Int64 { try { return 被除数 / 除数 } catch (异常: Exception) { println(发生错误${异常.message}) return 0 // 出错时返回默认值 } } main() { var 结果1 安全地除法(10, 2) // 正常5 var 结果2 安全地除法(10, 0) // 异常除数为0 println(结果1${结果1}) println(结果2${结果2}) }运行结果发生错误division by zero 结果15 结果20四、常见的异常类型import std.console.* func 演示各种异常(): Unit { // 1. 数组越界异常 try { var 数字列表 [1, 2, 3] var 值 数字列表[10] // 越界了 } catch (异常: IndexOutOfBoundsException) { println(数组越界${异常.message}) } // 2. 数字转换异常 try { var 文本 abc var 数字 Int64.parse(文本) // 无法转换 } catch (异常: NumberFormatException) { println(数字格式错误${异常.message}) } // 3. 空指针异常 try { var 空名字: String? null var 长度 空名字!.length // 空值解引用 } catch (异常: NullPointerException) { println(空指针异常${异常.message}) } }五、抛出异常有时候我们需要自己抛出异常来提示调用者。// 定义一个检查年龄的函数 func 设置年龄(年龄: Int64): Unit { if (年龄 0) { throw Exception(年龄不能为负数) } if (年龄 150) { throw Exception(年龄不能超过150岁) } println(年龄设置成功${年龄}岁) } main() { try { 设置年龄(25) // 正常 设置年龄(-5) // 抛出异常 } catch (异常: Exception) { println(错误${异常.message}) } }运行结果年龄设置成功25岁 错误年龄不能为负数六、语法设计讨论异常处理中的类型声明同学们在学习异常处理时我们再次遇到了仓颉的类型后置语法问题。看看异常处理的代码try { // 可能出错的代码 } catch (异常: Exception) { // 类型后置 // 处理异常 } func 安全地除法(被除数: Int64, 除数: Int64): Int64 { // 参数和返回值类型都后置 try { return 被除数 / 除数 } catch (异常: Exception) { // 又是类型后置 return 0 } }按照中国人的语言习惯我们习惯说异常类型的变量、“整数类型的参数”我们习惯说字符串类型的返回值但仓颉的写法catch (异常: Exception)→ 读作捕获异常异常类型的定语后置func 函数名(): 返回类型→ 读作函数名返回某某类型的定语后置如果仓颉能改进成C#风格// 假设的改进语法 try { // 可能出错的代码 } catch (Exception 异常) { // 异常类型的异常变量——定语前置 // 处理异常 } Int64 安全地除法(Int64 被除数, Int64 除数) { // 整数类型的安全地除法函数 try { return 被除数 / 除数 } catch (Exception 异常) { return 0 } }这样读起来多么自然“定义一个整数类型的函数安全地除法它有两个整数类型的参数…”华为在设计仓颉语言时采用了类似Rust的类型后置语法这就像是在说异常异常类型的而不是异常类型的异常。现代汉语都是定语前置的我们希望中文编程语言能真正符合中国人的语言习惯七、finally块finally块中的代码无论是否发生异常都会执行常用于清理资源。func 读取文件(文件名: String): String { var 文件内容 var 文件: File? null try { 文件 File.open(文件名, r) 文件内容 文件.readAll() return 文件内容 } catch (异常: Exception) { println(读取文件失败${异常.message}) return } finally { // 无论成功与否都要关闭文件 if (文件 ! null) { 文件.close() println(文件已关闭) } } }八、自定义异常我们可以创建自己的异常类型来表示特定的错误。// 自定义异常类 class 年龄异常 : Exception { init(消息: String) { super(消息) } } class 成绩异常 : Exception { init(消息: String) { super(消息) } } // 使用自定义异常 func 设置学生成绩(成绩: Float64): Unit { if (成绩 0) { throw 成绩异常(成绩不能为负数) } if (成绩 100) { throw 成绩异常(成绩不能超过100分) } println(成绩设置成功${成绩}分) } main() { try { 设置学生成绩(85.5) 设置学生成绩(105) // 抛出成绩异常 } catch (异常: 成绩异常) { println(成绩错误${异常.message}) } catch (异常: Exception) { println(其他错误${异常.message}) } }九、练习时间练习1安全的计算器创建一个计算器类包含加、减、乘、除四个方法。除法要处理除数为0的情况所有方法都要处理可能的溢出异常。练习2用户注册验证创建一个用户注册函数验证以下内容用户名不能为空长度3-20个字符密码不能为空长度6-30个字符年龄必须在1-150之间邮箱格式要正确如果验证失败抛出相应的异常。练习3银行转账系统创建一个转账函数需要处理以下异常情况转出账户余额不足转账金额必须大于0转账金额不能超过单笔限额比如5万元收款账户不存在十、本课小结今天我们学习了异常程序运行时的错误情况try-catch捕获并处理异常throw主动抛出异常finally无论是否异常都会执行的代码块自定义异常创建特定的异常类型类型语法思考在异常处理中我们频繁地声明异常变量类型、函数参数类型、返回值类型每一次都要面对类型后置的语法。希望未来的中文编程语言能让类型声明更符合中国人的说话习惯真正做到说人话十一、课后作业完善学生管理系统的异常处理添加成绩输入验证0-100分添加年龄输入验证5-30岁添加学号格式验证不能为空长度固定编写一个文件复制程序要求检查源文件是否存在检查目标目录是否有写入权限使用try-catch-finally确保资源正确释放思考在你的日常生活中还有哪些场景可以用异常处理的思路来解决下篇预告第11篇《文件操作——数据的持久化存储》学习如何将数据保存到文件中让数据不会随程序关闭而丢失