软件构造课程笔记(五)——健壮性的软件构建

一、健壮性和正确性

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

即使因为意外终止执行了,也要向用户展示准确的错误信息。面向健壮性的编程要求封闭实现细节,以达到限定用户的恶意行为的目的,并且要考虑到各种各样大的极端情况,假设用户可以做任何事情。

目的是让用户变得更容易:出错也可以容忍,因为程序内部已有容错机制。

对自己的代码要保守,对用户的行为要开放。

正确性:程序按照spec加以执行的能力,是最重要的质量指标!

面向正确性的编程要求永不给用户错误的结果。

目的是让开发者变得更容易:用户输入错误,直接结束。正确性倾向于直接报错(error),健壮性则倾向于容错(fault-tolerance)

二、错误和异常处理 

1.Java中的错误和异常 

Error:与代码无关,程序员通常无能为力,一旦发生,想办法让程序优雅的结束。也可以通过对外部环境的配置解决问题,如用户输入错误、设备错误、物理限制等。

Exception:一定是程序导致的问题,可以捕获、可以处理。

由于程序员对Error通常无法预料无法解决,因此重点关注可被解决的Exception

2. 异常

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

程序除了return正常退出之外还可以通过throws抛出异常来非正常退出。

运行时异常:由程序员在代码里处理不当造成,在源代码中引入了故障,而如果在代码中提前进行验证,这些故障就可以避免。动态类型检查的时候会发现这种异常,而一旦出现,代码就必然有错误,可以通过调试解决。
其他异常:由外部原因造成,程序员无法完全控制的外在问题所导致的,即使在代码中提前加以验证,也无法完全避免失效发生。

3.Checked and unchecked exceptions

Checked exceptionUnchecked exception
Basic必须被显式地捕获或者传递 (try-catch-finally-throw),否则编译器无法通过,在静态类型检查时就会报错异常可以不必捕获或抛出,编译器不去检查,不会给出任何错误提示
Class of Exception继承自Exception类继承自RuntimeException类
Handling从异常发生的现场获取详细的信息,利用异常返回的信息来明确操作失败的原因,并加以合理的恢复处理简单打印异常信息,无法再继续处理
Appearance代码看起来复杂,正常逻辑代码和异常处理代码混在一起清晰,简单

 4.抛出异常

        定义: 一个方法不处理这个异常,而是调用层次向上传递,谁调用这个方法,这个异常就由谁来处理。

        throw: 将产生的异常抛出(强调的是动作),抛出的既可以是异常的引用,也可以是异常对象。(位置: 方法体内)

        throws: 如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。用它修饰的方法向调用者表明该方法可能会抛出异常(可以是一种类型,也可以是多种类型,用逗号隔开)(位置: 写在方法名 或方法名列表之后 ,在方法体之前。)

注意 : 调用可能会抛出异常的方法,必须添加try-catch代码块尝试去捕获异常 或者 添加throws 声明来将异常抛出给更上一层的调用者进行处理,这里需要注意一个细节:新的异常包含原始异常的所有信息,根据这个我们可以去追溯最初异常发生的位置。

总结throw 和throws 关键字的区别

1、写法上 : throw 在方法体内使用,throws 函数名后或者参数列表后方法体前
2、意义:throw 强调动作,而throws 表示一种倾向、可能但不一定实际发生
3、throws 后面跟的是异常类,可以一个,可以多个,多个用逗号隔开。throw 后跟的是异常对象,或者异常对象的引用。
4、throw 用户抛出异常,当在当前方法中抛出异常后,当前方法执行结束(throw 后,如果有finally语句的话,会执行到finally语句后再结束。)。可以理解成return一样。

        自定义异常

        之前的异常都是系统自带的,系统自己处理,但是很多时候项目会出现特有问题,而这些问题并未被java所描述并封装成对象,所以对于这些特有的问题可以按照java的对问题封装的思想,将特有的问题进行自定义异常封装。在Java中要想创建自定义异常,需要继承Throwable或者他的子类Exception。

class  自定义异常类 extends 异常类型(Exception){

 // 因为父类已经把异常信息的操作都完成了,所在子类只要在构造时,
 // 将异常信息传递给父类通过super语句即可。
 // 重写有参和无参构造方法
}

5.LSP中的异常


       1. 如果子类型中override了父类型中的方法,那么子类型中方法抛出的异常不能比父类型抛出的异常类型更宽泛,异常不能逆变
        2.子类型方法可以抛出更具体的异常,也可以不抛出任何异常,异常可以协变
        3.如果父类型的方法未抛出异常,那么子类型的方法也不能抛出异常
目的还是为了能够让客户端能够用统一的方式处理不同类型的对象。

 三、断言和防御式编程

1.断言

        断言是为了在开发阶段检验某些应该成立的条件在此时是否成立,如果成立则表明程序正常,如果不成立则表明存在错误。断言即是对代码中程序员所做假设的文档化,在实际使用的时候assertion都会被disabled,就不会影响性能了。

不带信息的断言:assert condition;

带信息的断言:assert condition : message;

使用的地方:

内部不变量:判断某个局部变量应该满足的条件
表示不变量:常用的checkRep()
控制流不变量:如:不想让程序走向switch-case的某个分支,则可以用断言直接在分支上assert false;
方法的前置条件:判断传入参数是否满足前置条件,以fail fast
方法的后置条件:判断结果时候满足后置条件,程序一定要执行正确。 

注意:

        不要把正常的功能语句和断言在一起,如assert list.remove(x);这样在禁掉断言的时候会把功能语句一并禁掉。
        程序之外的事,不受控制,不要乱断言,只有涉及到内部执行逻辑的时候才使用Assert

Assertion vs. Exception

错误/异常处理是提高健壮性,处理外部行为;断言是提高正确性,处理内部行为

使用异常来处理你“预料到可以发生”的不正常情况;使用断言处理“绝不应该发生”的情况

2.防御式编程

  • 对来自外部的数据源要仔细检查,例如:文件、网络数据、用户输入等

  • 对每个函数的输入参数合法性要做仔细检查,并决定如何处理非法输入

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值