12 面向正确性与健壮性的软件构造

1什么是稳健性和正确性/h2>

1.健壮性

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

2.面向健壮性的编程

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

错误信息有助于进行debug

3.稳健性原则(Postel定律)

1.总是假定用户恶意、假定自己的代码可能失败

2.把用户想象成白痴,可能输入任何东西,返回给用户的错误提示信息要详细、准确、无歧义

3.(Postel’s Law):对别人宽容点,对自己狠一点

4.封闭实现细节,限定用户的恶意行为

5.考虑极端情况,没有“不可能”

4.正确性

程序按照spec加以执行的能力,是最重要的质量指标。

正确性与健壮性比较:

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

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

正确性倾向于直接报错(error),健壮性则倾向于容错(fault-tolerance)

12 面向正确性与健壮性的软件构造

运行时异常:由程序员在代码里处理不当造成,,是程序源代码中引入的故障所造成的

如果在代码中提前进行验证,这些故障就可以避免

其他异常:由外部原因造成,是程序员无法完全控制的外在问题所导致的

– 即使在代码中提前加以验证(文件是否存在),也无法完全避免失效发生

3 checked和unchecked exception

1.checked exception

必须捕获并指定错误处理器handler,否则编译无法通过,类似于编程语言中的static type checking

发生异常时
–您必须捕获并处理该异常,或者通过声明您的方法引发该异常来告诉编译器您无法处理它,
–然后,使用您的方法的代码将必须处理该异常(如果无法处理该异常,可以选择声明它抛出该异常)。
编译器将检查我们是否完成了两件事之一(catch或declare)

2.unchecked exception

可以不处理,编译没问题,但执行时出现就导致程序失败,代表程序中的潜在bug类似于编程语言中的dynamic type checking

编译器不会检查错误error和运行时异常runtime exception
–在这些情况下,您不能做任何事情,但必须重新编写程序代码。所以编译器不会检查这些。
–这些运行时异常将在开发和测试期间发现。然后,我们必须重构代码以删除这些错误。

Java checked exception处理 throws/try catch二选一

(throws) 声明“本方法可能会发生XX异常”

(throw) 抛出XX异常

(try, catch, finally) 捕获并处理XX异常

Unchecked异常也可以使用throws声明或try/catch进行捕获,但大多数时候是不需要的,也不应该这么做——掩耳盗铃,对发现的编程错误充耳不闻

如何选择exception

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

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

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

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

充分利用Java API中提供的丰富unchecked exception,如NullPointerException , IllegalArgumentException和IllegalStateException等,使用这些标准的异常类而不需亲自创建新的
异常类,使代码易于理解并避免过多消耗内存。

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

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

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

总结:

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

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

错误可预料,但无法预防,但可以有手段从中恢复,此时使用checked exception。如果做
不到这一点,则使用unchecked exception

合理的恢复
–告诉调用者预测他们无法恢复的异常是没有意义的。
–如果用户试图读取不存在的文件,调用者可以提示他们输入新文件名。
–另一方面,如果该方法由于编程错误(无效的方法参数或错误的方法实现)而失败,则应用程序无法在执行过程中修复该问题。它所能做的最好的事情就是记录问题,并等待开发人员稍后修复。
除非抛出的异常满足上述所有条件,否则它应该使用Unchecked Exception.

对特殊结果(即预期情况)使用checked exception
使用unchecked exception发出错误信号(意外故障)

12 面向正确性与健壮性的软件构造

5 如何throw exception

在可能出问题处throw exception,并给出提示信息

找到一个能表达错误的Exception类/或者构造一个新的Exception类

构造Exception类的实例,将错误信息写入 ,并抛出它
一旦抛出异常,方法不会再将控制权返回给调用它的client,因此也无需考虑返回错误代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AxaatyEa-1655136815875)(img/image-20220610213910708.png)]

(不想每个方法都throws声明新定义的异常时)您可能不想强制每个方法在其throws子句中声明您的异常实现。这时可以让新异常类继承runtimeexception

方法可以throw或传播FooRuntimeException异常,而无需声明它。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WPbmZ0Y4-1655136815876)(img/image-20220610215013868.png)]

7 捕捉异常 try catch

A.如果try块中的任何代码引发catch子句中指定的类的异常,则程序跳过try块中的其余代码。执行catch子句中的处理程序代码。
B.如果try块中没有任何代码引发异常,则程序跳过catch子句。
C.如果方法中的任何代码引发catch子句中指定类型以外的异常,则此方法将立即退出。

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

尽量在自己这里处理,实在不行就往上传——要承担责任!
但有些时候自己不知道如何处理,那么提醒上家,由client自己处理

如果父类型中的方法没有抛出异常,那么子类型中的方法必须捕获所有的checked exception

子类型方法中不能抛出比父类型方法更多的异常,必须更加具体化或不变

利用 e.getMessage() 获得详细异常信息,利用e.getClass().getName() 获得异常种类

8 re-throw和链接异常

本来catch语句里面是用来做异常处理的,但也可以在catch里抛出异常。

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

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

捕获异常时,可以检索原始异常

强烈建议使用这种包装技术。它允许您在子系统中抛出高级异常(se),而不会丢失原始故障的详细信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nT1xXZm2-1655136815877)(img/image-20220610221846610.png)]

(根据栈中的顺序,逐级弹出,所以最上行是C)

假设methodD()遇到异常情况,并向JVM抛出XxxException。
JVM在调用堆栈中向后搜索匹配的异常处理程序。
它发现methodA()具有XxxException处理程序并将异常对象传递给处理程序

请注意,methodC()和methodB()需要在其方法签名中按顺序声明“throws XxxException”
来编译程序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MiPTi5qS-1655136815878)(img/image-20220610224817312.png)]

5.不要assert外部情况

断言只是检查程序的内部状态是否符合规约

你的代码无法保证不出现此类外部错误

外部错误要使用Exception机制去处理

断言非常影响运行时的性能

3 如何使用assertion

断言->正确性,异常处理->健壮性

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

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

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

您可以使用断言来测试非public方法的前提条件,如果您认为无论客户机如何处理该类,该前提条件都是真的。

您可以使用public和nonpublic方法中的断言来测postcondition。

开发阶段用断言尽可能消除bugs,在发行版本里用异常处理机制处理漏掉的错误

5 防御式编程

1.防御式编程技术

保护程序免受无效输入的影响:对每个函数的输入
参数合法性要做仔细检查,并决定如何处理非法输入
断言
exception
特定的错误处理技术
路障
调试辅助工具

2.路障

出于防御编程目的设置路障的一种方法是将某些接口指定为“安全”区域的边界。检查跨越安全区域边界的数据的有效性,如果数据无效,则做出明智的响应。

1.类的public方法接收到的外部数据都应被认为是dirty的,需要处理干净再传递到private方法——隔离舱

2.另一种方法是操作间技术 – 数据在进入操作间之前已消毒。操作间里的任何东西都被认为是安全的。

路障的使用将断言和错误处理区分开来。

路障之外的例程应该使用错误处理,因为对数据进行任何假设都是不安全的。
on。

开发阶段用断言尽可能消除bugs,在发行版本里用异常处理机制处理漏掉的错误

文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树首页概览91513 人正在系统学习中

来源:Hanasaka Yui

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年5月11日
下一篇 2022年5月11日

相关推荐