文章目录
- 6.1 健壮性与正确性
- 6.2 错误与异常处理(**)
-
- 3. checked异常、unchecked异常
- 8. throws
-
- 8.4 LSP原则对异常的要求
- 9. throw
-
- 9.4 throw一个异常的步骤
- 10. catch
- 11. finally
- 6.3 断言与防御机制
-
- 3. 断言VS异常
- 6.4 debugging 代码调试
-
-
- 7.2. stack trace
-
6.1 健壮性与正确性
健壮性:保证程序可以正常进行下去(容错)
-
面向内部的接口倾向于:正确性
面向外部的接口倾向于:健壮性
- error(程序员犯的错误)——> defect/fault/bug(缺陷,bug的根源)——> failure(失效,运行时的外部表现)
- 衡量
-
外部观察角度
平均失效间隔时间(MTBF):失效时间+修复时间
-
内部观察角度
残余缺陷率:每千行代码中遗留的bug数量
-
-
编程时提高健壮性与正确性的方法:
assertion,exception,error,defensive programming(防御式编程)
6.2 错误与异常处理(**)
注:异常用来提高程序健壮性,断言用来提高程序正确性
-
error与异常
error:外部因素导致的
异常:
- 运行时异常:程序写错导致的
-
其他异常:client调用错误导致的
(一般异常处理的不是程序出错,而是client调用错误)
- 出现异常时会层层向上(调用者)找是否可以处理出现的异常,直到找到。
3. checked异常、unchecked异常
1. Unchecked异常:error + Runtime异常
不需要在编译时用try…catch等机制处理,但若执行时出现会导致程序失败
2. Checked异常
必须捕获并指定错误处理器handler,否则编译无法通过
3. 常见Unchecked异常
ArrayIndexOutOfBoundsException——使用时超出数组边界
NullPointerException——使用空指针时
NumberFormatException——将字符串转换为数值类型失败时
ClassCastException——强制转换对象引用失败时
-
关键字
try
catch
finally(不管正确路径还是错误路径,finally下的语句都要执行)
throws(声明可能会发生的异常)
throw(抛出异常)
- 针对checked异常编译器检测的两种方式
- try catch捕获
- 方法名后加上对应的throws,对异常进行抛出
- Unchecked异常也可以throws声明或try/catch捕获,但不应该这么做
checked异常 | Unchecked异常 |
---|---|
客户端可以通过其他的额方法恢复异常 | 客户端无能为力 |
可预料不可预防 | 可预料可预防 |
除了右边以外的派生 | 派生于Error或者RuntimeException的异常 |
1 尽量使用unchecked异常处理编程错误(eg:NullPointerException,IllegalArgumentException)
原因:不用客户端代码显示的处理
2. 如果client对某种异常无能为力,可以把它转变成一个unchecked异常,程序被挂起并返回客户端异常信息
3. 针对每一个checked 异常,创建checked异常的子类,采用继承的方式使客户端可以明确失败原因并去克服。
4. 对于RuntimeException,不需要定义子类不需要继承,就用java提供的几种异常即可
8. throws
注:java中任何一个程序都不会显示的通过throws语句抛出 RuntimeException(正确)
1. 需要在方法的**spec中明确写清本方法抛出的*所有*checked exception**,以便于调用该方法的client加以处理。
下图即在规约中明确写出
2. 抛出Unchecked异常程序不会报错,但这种写法是不应该的。
3. 使用throws的两种情况:
i. 调用的函数抛出checked exception(处理不了就往上抛)
ii. 自己当前的方法抛出checked exception
8.4 LSP原则对异常的要求
1. 如果父类型抛出异常,则相对应的子类型要么**不抛出异常**,要么抛出的异常比父类型抛出的**一样/更具体**。
错误举例:父抛 FileNotFoundException,子抛 IOException(x)
2. 如果父类型中的方法没有抛出异常,那么子类型中的方法**必须捕获所有的checked exception**
9. throw
- 两种使用方式:
- throw new EOFException();
-
EOFException e = new EOFException();
throw e;
- **throw和throws一般在方法中是一一对应的(但是不写throws也不会报错)**或者throw也可以与try/catch一一对应
-
自己构造异常类输出错误的现场信息方法:
利用Exception的构造函数
9.4 throw一个异常的步骤
- 找到一个可表达错误的异常类/构造一个新的异常类
- 构造Exception类的实例,写入错误信息
-
抛出它
【注意:关于自己构造异常类的代码】
1.
2. 注意throws和throw的对应 3. 用辅助方法记录“案发现场信息”
10. catch
- 获取异常具体信息的两种方法
- e.getMessage()
- e.getClass().setName()
- catch多个类型异常的优先级 离try语句越近异常类越具体
-
rethrowing 重新抛出异常
即在catch中抛出异常
重抛异常会把异常抛给上一级环境中的异常处理程序。同一个 try 块的后续 catch 子句将被忽略。此外,异常对象的所有信息都得以保持,所以高一级环境中捕获此异常的处理程序可以从这个异常对象中得到所有信息。
public static void f() throws Exception {
System.out.println("a_mark");
throw new Exception("b_mark");
}
public static void g() throws Exception {
try {
f();
} catch (Exception e) {
System.out.println("c_mark");
e.printStackTrace(System.out);
throw e;
}
}
public static void main(String[] args) {
try {
g();
} catch (Exception e) {
System.out.println("e_mark");
e.printStackTrace(System.out);
}
}
或
目的:更改exception的类型,方便client端获取错误信息。(注:加上se.initCause(e)是为了保留根原因,能调用se.getCause()得到异常e)
11. finally
无论是否发生异常,finally后的部分都会被执行
-
执行顺序问题
1. 当程序出现异常并捕获
不会经过2(1的时候就中断了)
2. 当程序出现异常但没有被捕获
顺序是1、5——没有6的原因是程序没有被捕获,一般会找调用方看是否能处理异常
3. 异常被捕获了,但是catch中又抛出了一个异常
顺序为1、3、5
4. 带资源的try(TWR)
可以带多个资源
- Stack Trace
- 注意顺序
- 打印调用栈
- throwable提供的printStackTrace(接收类型PrintWriter(接收类型StringWriter)))
- 或把调用栈的内容放在数组里面
6.3 断言与防御机制
程序开发的时候才需要断言!
-
两种形式
assert conditon
assert condition : message;
例如:assert x > 0 : " x is " + x ;
- 断言的检测内容
- 内部不变量
- 表示不变量
- 控制流不变量
- 方法的前置条件(通常选择用异常写)
- 方法的后置条件 B处应该为:
3. 断言VS异常
1.断言能做的,异常都能做(但是断言可以自动关闭)
2.
private一般用断言查 |
---|
public一般用异常查 |
返回值用断言检测 |
- 防御式编程
- 保证对不正确输入也要有结果
- 路障设置(代理模式——用来校验的功能)
6.4 debugging 代码调试
提高程序的核心手段不是测试/debug,而是前文的模式方法
-
复现bug
保证环境一致
- 诊断bug
-
print出来
注:软件release出去的时候,所有用于debug的print语句都要去除或禁用
实际操作中可以用一个方法专门用来打印错误信息,release时禁用方法里那条print语句即可
-
看日志(日志可以设置级别)
例:用Hashset打印
-
- 围狼算法(分治)
- 切片
- 寻找差异
- 分析版本提交的不同
- 基于差异的调试(比较不同测试用例覆盖代码的差异)
- 其他差异(软硬件环境…)
- 符号化执行
- debug工具
- 暴力搜索
7.2. stack trace
例:判断Stack Trace的输出