软件构造复习(五)

正确性与健壮性

健壮性

健壮性:系统在不正常输入或不正常外部环境下仍能够表现正常的程度

面向健壮性的编程:

处理未期望的行为和错误终止

即使终止执行,也要准确且无歧义地向用户展示全面的错误信息

错误信息有助于进行debug

健壮性准则:总是假定用户恶意、假定自己的程序可能失败;认为用户可能输入任何东西,返回给用户的错误提示信息要准确、详细、无歧义

正确性

正确性:程序按照Spec执行的能力,是最重要的指标

正确性:永不给用户错误的结果

健壮性:尽可能保持软件运行而不是总退出

正确性倾向于直接保持,健壮性则倾向于容错

正确性与健壮性的对比

 健壮性:避免给用户太大压力,帮助用户承担一些麻烦

 健壮性:让客户变得更容易,出错也可以容忍,程序内部已有容错机制

正确性:让开发者更容易,用户输入错误(不满足precondition),直接结束

内部倾向于正确,外部倾向于健壮

对外的接口,倾向于健壮;对内的实现,倾向于正确

在内外部之间做好隔离,防止错误扩散

可靠性=正确性+健壮性

提高正确性与健壮性

  1. 使用断言、防御式编程、正确性验证等方式
  2. 观察错误的症状(存储器信息、堆栈追踪、日志、测试)
  3. 识别潜在的错误
  4. 修复错误

衡量正确性与健壮性

外部观察角度

平均失效间隔时间(MTBF):相邻两次故障之间的平均工作时间

 内部观察角度(间接)

残余缺陷率:每千行代码中遗留的bug数量

 错误与异常

错误和异常都继承自Throwable,其有两个子类:Error和Exception

 错误:程序员通常无能为力,一旦发生,想办法让程序优雅地结束

异常:自己的程序导致的问题,可以捕获、可以处理

错误类型

用户输入错误

设备错误

物理限制(内存空间不足等)

大多数时候,程序员不需要实例化Error

异常处理

异常

程序执行中的非正常事件,程序无法再按预想的流程执行

将错误信息传递给上层调用者,并报告“案发现场”的信息。是return之外的第二种退出途径

上层调用者如果没有异常处理程序将其捕获,则继续将异常上抛

若找不到异常处理程序,整个系统完全退出(上抛到最上层调用者后仍没有异常处理程序)

异常的分类

分为运行时异常(RuntimeException类)和其他异常

运行时异常是由程序员在代码里处理不当造成,其他异常是由外部原因造成(打开不存在的文件等)

运行时异常是程序源代码中引入的故障所造成的,如果在代码中提前验证(空指针、数组越界)就可以避免

非运行时异常是程序员无法完全控制的外在问题导致的,即使在代码中提前验证(文件是否存在),也无法完全避免失效发生

Checked异常与Unchecked异常

是从异常处理机制角度进行的分类

Checked异常可被编译器检查,类似于静态类型检查

对于Checked异常,要么方法中有对应的处理程序(将其捕获),要么在方法签名中声明

Error和RuntimeException不能被编译器检查,是Unchecked异常,类似于动态类型检查

Unchecked异常可以使用try...catch等机制处理,但不需要,也不应该这样处理

异常处理

throws:声明“本方法可能会发生某个(些)异常”

throw:抛出异常

try...catch...finally:捕获异常并处理

使用哪种异常?

如果客户端可以通过其他的方法恢复异常,那么采用checked exception;

如果客户端对出现的这种异常无能为力,那么采用unchecked exception;

异常出现的时候,要做一些试图恢复它的动作而不要仅仅的打印它的信息

尽量使用unchecked exception来处理编程错误:因为unchecked exception不用使客户端代码显式的处理它们,它们自己会在出现的地方挂起程序并打印出异常信息

如果client端对某种异常无能为力,可以把它转变为一个unchecked exception,程序被挂起并返回客户端异常信息

不要创建没有意义的异常,client应该从checked exception中获取更有价值的信息(案发现场具体是什么样子),利用异常返回的信息来明确操作失败的原因

如果client仅仅想看到异常信息,可以简单抛出一个unchecked exception:

总结:

Checked exception应该让客户端从中得到丰富的信息。

要想让代码更加易读,倾向于用unchecked exception来处理程序中的错误

错误可预料,但无法预防,但可以有手段从中恢复,此时使用checked

如果做不到这一点,则使用unchecked exception

 用throws声明抛出Unchecked exception

异常也是方法和client端之间spec的一部分,在post-condition中刻画

程序员必须在方法的spec中明确写清本方法会抛出的所有checked exception,以便于调用该方法的client加以处理

相反地,unchecked exception不是post-condition的一部分,所以不应该出现在spec和函数签名中!

如果一个方法可能抛出多种checked exception,必须把它们都列在函数签名中

方法throws的异常包括:调用的其他方法抛出的checked exception;本方法创建的checked exception

不要抛出Error和unchecked exception!

 抛出异常与LSP原则

如果子类型中override了父类型中的函数,那么子类型中方法抛出的异常不能比父类型抛出的异常类型更宽泛

子类型方法可以抛出更具体的异常,也可以不抛出任何异常

如果父类型的方法未抛出异常,那么子类型的方法也不能抛出异常

 如何抛出异常

用“throw 异常对象”来抛出异常

利用Exception的构造函数,将发生错误的现场信息充分的传递给client

 创建异常类

如果JDK提供的exception类无法充分描述你的程序发生的错误,可以创建自己的异常类

 可以给异常类添加额外信息并在捕获时获取信息,以便于调试:

 捕获异常

异常发生后,如果找不到处理器,就终止执行程序,在控制台打印出stack trace(最上一行是异常发生的“第一现场”,接下来每行为上一行被调用的位置)

使用try...catch捕获异常

 

也可以不在本方法内处理,而是传递给调用方,由client处理(“推卸责任”)

checked exception必须被处理或继续上抛

尽量在自己这里处理,实在不行就往上传——要承担责任

可使用异常对象的getMessage方法获取异常信息,用getClass().getName()获取异常的具体类型

可以用多个catch语句来捕获多种异常

 多次抛出异常与异常链

本来catch语句下面是用来做exception handling的,但也可以在catch里抛出异常

这么做的目的是:更改exception的类型,更方便client端获取错误信息并处理

 

但这么做的时候最好保留“根原因”:

 当异常被捕获时,原本的异常可以被取回:

 finally语句

无论异常是否发生都要执行的代码写在finally中,在最后执行

如果try中的程序抛出了catch中没有捕获的异常,则运行至try中抛出异常的代码后,执行finally中的代码,然后将异常上抛

可以不使用catch而只使用finally:

 无论try中的代码是否抛出异常,in.close()都会被执行

在有finally的情况下,try中的return会被屏蔽:

 finally中的return语句会阻止异常的继续上抛:

 分析堆栈追踪

函数的调用:

结果:

 在上面代码的methodC中加入一个除0操作,则结果如下:

 可见错误信息的第一行是异常产生的位置,此后的每一行都是其上一行的方法被调用的位置

 断言

 当前置条件不满足时,抛出AssertionError异常——尽可能早地指出用户的bug

检查前置条件是防御式编程的一种典型形式

断言:在开发阶段的代码中嵌入,检验某些“假设”是否成立。若成立,表明程序运行正常,否则表明存在错误

断言即是对代码中程序员所做假设的文档化,也不会影响运行时性能(在实际使用时,assertion都会被disabled)

断言的形式:

 断言什么

应断言:

  • 内部不变量
  • 表示不变量
  • 控制流不变量(断言程序的某个位置不会到达,如switch的defalt)
  • 方法的前置条件
  • 方法的后置条件

控制流不变量:

 但不要使用assert,而是要直接抛出AssertionError

其他的断言情形:不应被修改的量、空指针等

断言主要用于开发阶段,避免引入和帮助发现bug。实际运行阶段,不再使用断言,避免影响性能

所以程序的正确性不能依赖于断言!

 若采用前一种,则由于实际运行阶段不使用断言,所以会跳过list.remove(x)

不要随意断言程序外部的事情!外部错误要使用Exception机制去处理

由于断言非常影响性能,所以java缺省关闭断言,要记得打开

断言与异常

断言与正确性相关,异常与健壮性相关

使用异常来处理你“预料到可以发生”的不正常情况

使用断言处理“绝不应该发生”的情况

对于传入参数的检查:

如果参数来自于外部(不受自己控制),使用异常处理

如果来自于自己所写的其他代码,可以使用断言来帮助发现错误

posted @ 2023-05-24 23:58  YY_R  阅读(51)  评论(0)    收藏  举报