6.3 断言与防御式编程
1.回忆:设计ADT
第一个防御:让bug不可能出现
最好的防御就是不要引入bug
静态检查:通过在编译时捕获bug来消除许多bug。
动态检查:Java使数组溢出错误不可能捕获他们动态。如果尝试在数组或列表的边界之外使用索引,那么Java会自动产生错误。——未检查的异常/运行时错误
不可变:不可变类型是一种类型,它的值一旦创建就永远不会改变。
不可变值:由final,它可以被分配一次,但从来没有重新分配。
不可变的引用:通过final,这使得引用不可重新分配,但是引用指向的对象可以是可变的或不可变的
第二道防线:本地化bug
如果无法避免bugs,尝试着将bug限制在最小的范围内
限定在一个方法/小模块内部,不扩散
尽快失败,就越容易发现、越早修复
应该尽可能早的指出client的bug (输入存在错误)
断言:利用断言,Fail fast,避免扩散
检查前置条件是防御式编程的一种典型形式
2.断言
(1)什么是断言,为什么断言?
什么是断言
断言:在开发阶段的代码中嵌入,检验某些“假设”是否成立。若成立,表明程序运行正常,否则表明存在错误。
-当断言为真时,意味着一切都按预期运行。
-当它为false时,意味着它在代码中检测到一个意外错误。
每个断言包含一个布尔表达式,您相信该表达式将在程序执行时为真。如果不为真,JVM将抛出一个AssertionError。出现AssertionError,意味着内部某些假设被违反了。增强程序员对代码质量的信心:对代码所做的假设都保持正确
断言即是对代码中程序员所做假设的文档化,也不会影响运行时性能(在实际使用时,assertion可以被disabled)
断言通常有两个参数
一个布尔表达式,描述假设应该是正确的
-一个显示的信息,如果为假。
Java语言有两个形式的关键字assert:
– assert condition;
– assert condition : message;
-这两个语句对条件求值,如果布尔表达式的值为false,则抛出AssertionError。
在第二个语句中,表达式被传递给的构造函数AssertionError对象,并转换为消息字符串。当断言失败时,描述将在错误消息中打印出来,因此可以使用它向程序员提供有关失败原因的额外细节。所构造的message在发生错误时显示给用户,便于快速发现错误所在
为什么使用断言?
▪文档和测试程序员的假设,例如,不变量;▪验证程序员的理解
▪快速发现漏洞
▪增加对程序没有bug的信心
▪断言将黑盒测试变成白盒测试
经验表明,在编程时编写断言是检测和纠正错误的最快速、最有效的方法之一。
另外一个好处是,断言可以记录程序的内部工作,增强可维护性。
(2)什么该断言,什么不该断言?
何时使用断言?
断言可用于验证:
——内部不变量:断言一个值是在一定的约束条件,例如,断言x > 0。
——表示不变量:断言,一个对象的状态是在约束。一个类的每个实例在执行方法之前或之后必须是什么?类不变量通常通过私有布尔方法验证,如checkRep()。
——控控制流不变量:断言不会达到一定位置。例如,switch-case语句的默认子句。
——方法的前置条件:调用方法时,必须真正的什么?通常用方法的参数或对象的状态表示。
——方法的后置条件:什么方法成功完成后一定是真的吗?
断言什么?
▪前置条件:方法参数要求
▪后置条件:方法返回值要求
-这种断言有时被称为自检。
▪控制流程:覆盖所有
-如果条件语句或 switch不能涵盖所有可能的情况,使用断言阻止非法情况是一个良好的做法。
void foo() {
for (..