Outline
1.健壮性和正确性;
2.关于健壮性和正确性的测度;
3.Java中的Error和Exception;
4.处理异常;
5.断言;
6.防御式编程;
7.Debug的一些工具;
2022-06-08 20:56:50
1.健壮性和正确性
可靠性=健壮性+正确性
健壮性:系统在不正常输入或不正常外部环境下仍能够表现正常的程度。(尽可能保持软件运行而不是总是退出)
面向健壮性的编程:处理未期望的行为和错误终止;即使终止执行,也要准确的向用户展示全面的错误信息;错误信息有助于进行debug。
面向健壮性的编程的原则:封闭实现细节,限定 用户的恶意行为;考虑极端情况,没有“不可能”。
Postel’s Law:对别人宽容点,对自己狠一点。程序员应总是假定用户恶意、假定自己的代码可能失败;把用户想象成“白痴”,可能输入任何东西。
正确性:程序按照spec加以执行的能力,是最重要的质量指标。(永不给用户错误的结果)
正确性和健壮性的对比:
正确性倾向于直接报错(error),健壮性则倾向于容错(fault-tolerance)。
正确性让开发者变得更容易:用户输入错误,直接结束(不满足precondition的调用)。
健壮性让用户变得更容易:出错也可以容忍,程序内部已有容错机制。
对外的接口,倾向于健壮;对内的实现,倾向于正确。(与后文中提到的Exception和Assertions的使用场景类似)
以几个例子来说明一下二者的区别:
提升程序健壮性和正确性的一般步骤:
用一张我认为很好的图概括一下:
2.关于健壮性和正确性的测度
外部观察角度MTBF:平均故障间隔时间(平均无故障运行时间),是指相邻两次故障之间的平均工作时间。即总的运行时间/总的故障次数。
MTBF的计算依赖于对系统failure的定义:对于可修复系统,是指导致系统不可使用的failure,出现问题,但系统仍可运行,则不视为failure。
同理还有故障前平均时间MTTF,它是用来描述不可修复系统的故障前平均时间。
下图描述的是可修复系统的MTBF(注意可修复系统没有MTTF):两个红色状态的间隔时间即为MTBF。
内部观察角度残余缺陷率:每千行代码中遗留的bug的数量。
像一般企业的代码残余缺陷率为1 - 10 defects/kloc,而像NASA这样的用于科研航空机构的代码残余缺陷率低至0.01 - 0.1 defects/kloc。
另外还可以用Halstead Volume来描述,具体内容之前在面向可维护性的软件构造中提到过,这里不再赘述。
3.Java中的Error和Exception
Java中所有的Error和Exception都是继承自Throwable类,记住下面这张图即可:图中红色虚线圈起来的一部分包括所有的Error和RuntimeException都属于Unchecked Exception,其余异常都属于checked Exception(编译时异常),下一节中会具体讲到对二者的区别对待。
Error通常是指内部错误,程序员通常无能为力,一旦其发生,应该想办法让程序优雅的结束。所以我们不要抛出Error对象(即使抛出了也是毫无意义的,程序员不能让内部错误恢复);异常则是你自己程序导致的问题,可以捕获、可以处理。
Error的分类:用户输入错误、设备错误、物理限制。一些典型的Error有OutOfMemoryError、StackOverflowError、InternalError、NoClassDefFoundError。
*.关于我在Lab2程序编写时碰到的一个RuntimeException的思考与回味:
当时在编写Lab2时,对一个Map进行了迭代器遍历,第一遍的时候程序没有任何报错,一切运行也很正常;但是在我第二次运行时,程序莫名其妙的报了一个NullPointerException。当时还没有学到这一章节的内容,我便去搜索引擎上查找相关原因,这个运行时异常是由程序对一个空的(null)对象解引用造成的,但是奇怪的是这个Map在我的程序中不可能有一个key指向一个空的对象啊(这里的Map存放的是当前单词在语料库生成图中的出边集(目标点和边权值),与这个单词没有连接的单词也不会出现在Map中,所以更不会有null出现)。在学习完这一章节的知识之后,我恍然大悟,首先这个异常属于Unchecked Exception,所以静态检查和编译都通过了,运行的时候才报错;至于后面为什么不会解引用null也会出现这个异常,我的推测是IDE并不知道我们的具体代码实现,它只清楚在当前这个Map的迭代遍历中可能会出现NullPointerException,所以负责任地提醒我们编写的代码存在着漏洞,这样的处理百利而无一害。但是我当时自然而然地将NullPointerException当做普通的编译时异常try catch了一下,运行时也没有报错了,说明源代码确实也不会触发NullPointerException,只是IDE细致入微的考虑。放现在来看,这并不是一个好的处理,Unchecked Exception最好的解决办法是重写代码,而不是try catch和throw。
下面是我当时的代码:现在看来,最好在对value解引用之前判断一下其是否为空,然后再去操作,这样就不会触发异常了。
感觉这个试错也是学习中的一大乐趣吧,学以致用才能更好地把握知识。