关于健壮性
Robustness: “the degree to which a system or component can function correctly in the presence of invalid inputs or stressful environmental conditions“ (IEEE Std 610.12-1990)
健壮性:系统在不 正常输入或不正常外部环境下仍能够表现正常的程度。
*外部质量因素
面向健壮性的编程需要做到:
处理未期望的行为和错误终止。
即使终止执行,也 要准确/无歧义的向用户展示全面的错误信息。
错误信息有助于进行debug。
原则:1、总是假定用户恶意、假定自己的代码可能失败。2、把用户想象成白痴,可能输入任何东西。3、返回给用户的错误提示信息要详细、准确、无歧义。4、对别人宽容点,对自己狠一点。
要压力自己的代码。
封闭实现细节,限定用户的恶意行为。
This information should be hidden from the user so that the user doesn't accidentally modify them and introduce a bug in the code. – When such interfaces are correctly built, users use them without finding loopholes to modify the interface. – The user therefore focuses solely on his or her own code。
让代码内部对用户不可见,使得用户不能意外修改内部信息并导致错误。让用户不能钻空子修改介面,这样用户就只能专注于他们自己的代码。
Can‘t happen - Code is modified and may introduce a possibility that an “impossible” case occurs. – Impossible cases are therefore assumed to be highly unlikely instead. – The developer thinks about how to handle the case that is highly unlikely, and implements the handling accordingly.
考虑极端情况,没有“不可能”
关于健壮性与正确性:
正确性:第一重要的质量指标,着眼于给用户正确无误的结果。注重报错。
健壮性:尽可能保持软件运行而不退出。注重容错。
量化健壮性的指标:外部视角:MTBF,平均失效间隔时间。
内部视角:Residual defect rates 残余缺陷率 per KLoC(每千行代码有多少遗留bug)
应用于java:java中的异常处理
关于错误和异常
错误一般程序员无能为力。而异常可以捕获并处理。
异常:程序执行中的非正常事件,程序无法再按预想的流程执行。
异常可以将异常或者错误信息传递给调用者,并报告“案发现场”的信息
如果无法以常规流程完成人物,java提供程序以return之外的第二种退出途径,若找不到异常处理程序,整个系统完全退出。
java中异常对象是throwable的派生类的实例。
异常可以分为两类:运行时异常,一般由代码处理不当导致;其他异常,由外部原因(如IO)导致。着重注意运行时异常。
例子:引用空指针,数组越界,转换失败等。其他异常比如eof越界、打开不存在的文件等等。运行时异常一般可以通过程序员手动避免,但是其他异常难以预测。
关于java中的异常处理机制
checked和没有checked的异常:1、对于checked的异常要么catch并handle异常,要么声明自己无法处理,让编译器知道这个方法抛出异常(甩锅)。这样一来使用了该方法的程序需要catch并handle异常,要么声明无法处理,继续甩锅。编译器会帮助程序员检查程序是否抛出或者处理了可能发生的异常。checked异常需要从Exception派生出子类型。不处理将会无法通过静态测试。2、错误和运行时异常不能被编译器check。比如系统崩溃等外部错误,编译器无法预测;程序逻辑存在bug导致运行时异常,编译器也无法预测。以上情况中除了重写代码别无他法。unchecked的异常,如果不处理也不会编译器报错,但是执行时就会导致程序失败,类似dynamic type-checking。
-如何handle被编译器checked的异常:try – catch – finally – throws – throw
try{可能引起异常的语句}catch(异常对象){处理语句} 对应代码提示的
方法 throws 异常类型{} 对应代码提示的
unchecked的异常也可以捕获并处理,但是没什么用。大多数时候是不需要的,也不应该这么做——掩耳盗铃, 对发现的编程错误充耳不闻。
throw:throw一个throwable派生类。
使用throws来声明受编译器检查的异常
方法的头部使用throws来说明该方法可能产生哪些异常。一个方法不仅告诉编译器返回值,也告诉编译器会出什么岔子。“异常”也是方法和 client端之间spec的一部分,在post-condition中刻画。
public FileInputStream(String name) throws FileNotFoundException
spec中怎么说明异常的存在:Javadoc @throws clause 指出特殊情况。必须在spec里和方法头部把所有的checked exception说明。
而unchecked的异常就不需要throws,也不需要@throws来体现。如果不仅抛出一种checked异常,可以用逗号连接来并列。
When you write your own methods, you don’t have to advertise every possible throwable object that your method might actually throw.
你的方法应该throws什么异常?
You call a method that throws a checked exception—for example, the FileInputStream constructor. 你所调用的其他函数抛出了一个checked exception——从其他函数传来的异常
You detect an error and throw a checked exception with the throw statement. 当前方法检测到错误并使用throws抛出了一个checked exception——你自己造出的异常
LSP原则与异常:如果子类型中override了父类型中的函数,那么子类型中方法抛出的异常不能比父类型抛出的异常类型 更宽泛。子类型方法可以抛出更具体的异常,也可以不抛出任何异常。如果父类型的方法未抛出异常,那么子类型的方法也不能抛出异常,需要在子类型内捕获所有的checked exception
例子:父类的方法throws FileNotFoundException,子类override的方法throws IOException,这甚至不能通过静态检查。