异常简介
作为一名程序员,代码异常是经常需要考虑的一件事情
异常的种类
我会从几个角度来说明异常,先从异常发生是否符合预期来看;
第一种,错误发生在预期情况内,比如 程序打开一个文件,该文件不存在,又或者 当我们从网络中 监听一个事件,但是该事件类型并不再我们定义的类型中
第二种,错误不再代码设计范围内,比如一个代码内部接口,期望的参数和实际参数不相同(从代码设计来看,不可能发生这种 情况) 比如断言的使用
还有一种分类方法,是从错误处理角度来看
第一种,当错误发生时,程序不应该崩溃,只是抛出错误(记录日志), 第二种,当错误发生时,触发了异常,程序会立即崩溃,比如访问了 非法地址(*0=0);又或者除0( 10/0) 第三种,当错误发生时,程序不应该继续继续(程序状态异常) 应该立即退出
fn main() {
let a: *mut i32 = 0 as *mut i32 ;
unsafe {
*a = 10;
}
}
一般上面两种分类方法,又被统一为:
- 可恢复异常: 一般时预期内的异常,只需要正确处理错误后,程序可以继续执行
- 不可恢复异常: 一般对应为发生了 程序非法指令
- 致命异常: 该错误发生后,程序无法正常执行(比如链表节点状态不正确、内存不足)
第二种情况,由于会导致程序崩溃,无法通过软件去捕获该异常,不在语言讨论范围内,我们只讨论第1和第3中情况 这两类情况 软件可以捕获到错误,只是对待错误的处理行为不同
对于可恢复异常,当异常发生时,一般选择是记录错误,并且返回错误到调用者,具体处理由调用者决定,以此一层一层向 上透传;
对于致命异常,则会记录日志,然后调用一条 非法指令,让程序直接崩溃
已有的异常处理方法
由于致命异常,一般会让程序直接崩溃,并不需要在返回,因此致命异常不在本小节讨论;
本节只讨论对于应该返回错误的情况
C语言作为低级程序语言,在错误处理这里,也是最为原始的
- 对于返回值是指针的(申请资源类接口),当返回值为Null 表示接口错误
- 对于返回值是整型的,通过返回 -1(非0) 表示接口异常
- 应用编程,会在异常发生时,通过设置一个全局变量 errno 表示错误类型
缺点:
- C 语言可以不对错误进行处理(不接收返回值 或者不判断返回值),静态检查可以处理部分问题
- C 语言无法详细描述错误,尤其是内部接口,1是受限于返回值类型约束,2是受限于文档描述,如果对返回值 增加过多的定义,就必须要对每个返回值进行说明,无论是后续修改以及使用者都很不友好
java python 这类高级语言,通过异常的机制处理错误
- 错误异常本身是一个类型,可以自解释
- 异常通过 throw 抛出
- 异常必须要通过try包裹,该行为本质上是为了保证异常一定能够被处理
可以看到异常的这种机制,基本上已经解决了C里面的问题,但是java异常类的继承特性,引入了新的问题
- 异常可以通过 异常基础类 统一捕获,然后忽略掉: 这并不是异常设计的本意
- 异常可以层层传递,如果上一层没有处理异常,异常会继续向上抛出,意味着我可以写出下面的代码 异常不会被及时处理
moduleA:
fn funcA() {
throw Exception("Invalid param");
}
modle B:
fn funcB()
{
funcA();
}
module C :
fn funC() {
try {
funcB();
} catch {
Exception: invalid param????
}
}