软件构造课笔记:第12章——面向正确性与健壮性的软件构造(简述软件构造)

软件构造课笔记:第12章——面向正确性与健壮性的软件构造面向正确性与健壮性的软件构造
什么是稳健和正确性?
健壮性:系统在不 正常输入或不正常外部环境下仍能够表现正常的程度 处理未期望的行为和错误终止 要准确

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

什么是鲁棒性和准确性?

鲁棒性:系统在输入异常或外部环境异常的情况下仍能正常运行的程度。

处理意外行为和错误退出

即使执行终止,也能准确、清晰地向用户显示全面的错误信息。

错误消息对于调试很有用

稳健性原理(Postel 定律)

1. 偏执:总是假设用户是恶意的,并假设代码可能会失败。

2.白痴:将用户视为可以输入任何内容的白痴。

3、对别人宽容,对自己残酷。对代码必须保守,对用户行为必须开放

原则:封闭实现细节,限制恶意用户行为。让我们考虑一种极端情况。没有什么是不可能的

准确性:按照规范运行程序的能力是最重要的质量指标。

声音和准确:在天平的两端。

准确性:永远不会给用户错误的结果

鲁棒性:让软件尽可能长时间地运行,而不是总是终止它

正确性倾向于直接报告错误,稳健性倾向于容错。

稳健性:不要给用户施加太大的压力,这样他们就可以容忍一些问题。

稳健:用户易于使用。错误是可以容忍的,并且程序中存在容错机制。

准确性:让开发人员更容易。如果用户输入错误,则直接退出。 (不满足前提条件的调用)

鲁棒性和准确性比较

在安全关键型应用中,准确性往往优先于稳健性。

– 不返回结果比返回错误结果要好。

消费者应用程序往往强调稳健性和准确性。

– 通常比关闭软件会产生更好的结果。

可靠性。系统在指定条件下、无论何时需要、且平均故障间隔时间较长的能力。

可靠性=稳健性+ 准确性

提高稳健性和准确性的步骤

第0 步:使用断言、防御性编程、代码审查、形式验证等以健壮性和正确性为目标来编写代码。

步骤1:观察故障的症状(内存转储、堆栈跟踪、执行日志、测试)

步骤2:识别潜在故障(错误定位、调试)

第3 步:修复错误(代码修复)

如何衡量稳健性和准确性?

外部:平均故障间隔时间(MTBF) 是操作系统固有的故障之间的预期经过时间。

内部:根据KLOC 的说法,残余缺陷率是指“软件发布后留下的缺陷”,是每1,000 行代码留下的错误数量。

Java 错误和异常

内部错误:发生此错误时程序员通常无能为力。找到一种优雅退出程序的方法。

例外:由专有程序引起的问题。这可以被捕获并处理。

错误类型:用户输入错误、设备错误、物理限制

异常处理

什么是例外?

异常是指程序执行过程中发生的异常事件,中断了程序的正常运行。

程序执行期间发生异常事件,导致程序无法按预期运行。

异常是代码向调用它的代码传达错误或异常事件的特定方式。将错误信息传递给更高级别的调用者并报告“犯罪现场”信息。如果任务无法以正常方式完成,Java 允许每个方法有一个替代的退出路径。除了返回之外的第二种退出方式

– 该方法抛出一个封装错误信息的对象。

– 该方法立即退出并且不返回任何值。

此外,调用该方法时不会恢复执行。

相反,异常处理机制开始搜索可以处理此特定错误情况的异常处理程序。

如果没有找到异常处理程序,则整个系统完全退出。

异常情况分类

运行时异常:是由于程序员对代码处理不当引起的。如果事先在代码中执行验证,则可以避免这些错误。

其他异常:是由外部原因引起的。非运行时异常是由完全超出程序员控制范围的外部问题引起的。即使代码经过预先验证(文件存在与否),也无法完全避免失败。

检查和非检查异常

异常情况如何处理?

发生异常时检查

您必须捕获并处理异常,或者声明一个抛出异常的方法以告诉编译器它无法处理异常。

使用该方法的代码必须处理异常(或者如果不能的话,可以选择声明它以引发异常)。

– 编译器检查您是否完成了以下两件事之一:捕获或声明。编译器可以帮助您查看程序是否引发或处理了异常。

编译器不检查错误或运行时异常。错误表示应用程序外部发生的情况,例如系统崩溃。运行时异常通常是由应用程序逻辑错误引起的。在这种情况下,唯一的选择就是重写程序代码。所以编译器不会检查这些。这些运行时异常是在开发和测试期间发现的。然后您需要重构代码以消除这些错误。

未经检查的异常

编译程序不需要任何操作,但如果不捕获异常,程序将失败。编译时不需要使用try.catch或者其他机制。但是,如果在执行过程中发生这种情况,程序就会失败,这表明程序中存在潜在的错误。

检查异常

您必须捕获并指定错误处理程序。否则编译不会通过。

您还可以使用throws 语句或try/catch 来捕获异常,但在大多数情况下这是不必要的,也不应该这样做。 —— 捂住耳朵、偷铃并忽略发现的编程错误。

在决定使用检查异常还是非检查异常时,请询问以下问题:“如果抛出此异常,客户端将采取什么补救措施?”

当客户端可以通过其他方式从异常中恢复时,请使用已检查异常。

当客户端无法对异常执行任何操作时,请使用未经检查的异常。

如果发生异常,请采取一些操作来尝试恢复,而不仅仅是打印该信息。

不要创建无意义的异常。客户端从检查的异常中获取更有价值的信息(犯罪现场到底是什么样子),并使用异常返回的信息来找出操作失败的原因。 如果客户端只想查看异常信息,则可以直接抛出未经检查的异常。检查异常应该为客户端提供丰富的信息。 为了使代码更易于阅读,我们倾向于使用未经检查的异常来处理程序中的错误。

错误是可以预料到的,但无法避免,但有一种方法可以使用错误预检查来恢复。

如果不可能,请使用未经检查的异常

错误是可以预测的,但无法预防

如果读取时发现该文件不存在,用户可以选择另一个文件,但如果调用该方法时传递了错误的参数,则在不中止执行的情况下无法进行恢复。

异常设计注意事项

对特殊结果(即预期情况)使用检查异常。

使用未经检查的异常的信号错误(意外失败)

使用throw 声明已检查的错误

通过throws 声明受检查的异常允许Java 方法在遇到无法处理的情况时抛出异常。

方法不仅告诉Java编译器可以返回什么值,还告诉Java编译器什么可能会出错。 “例外”也是方法和客户端的条件

程序员必须在方法规范中清楚地记录此方法抛出的任何已检查异常,以便调用该方法的客户端可以处理它。

如果一个方法可以抛出多个已检查的异常类型,则所有异常类必须在标头中列出。

不要抛出错误或未经检查的异常

不需要暴露继承自Error 的内部Java 错误异常。

任何代码都可能引发这些异常,并且完全超出您的控制范围。

不要公开从RuntimeException 继承的未检查异常。

考虑亚型多态性

如果子类型重写了父类型的函数,则子类型的方法抛出的异常类型不能比父类型抛出的异常类型宽。子类型方法可以抛出更具体的异常或根本不抛出异常。如果超类型的方法不抛出异常,那么子类型的方法也不能抛出异常。

请参阅LSP 原则。目标是亚型多态性。客户端可以以统一的方式处理不同类型的对象,子类型可以替换父类型。

里氏替换原则

LSP 是子类型关系的特殊定义,称为(强)行为子类型。 强行为亚型

在编程语言中,LSP 依赖于以下约束:

1. 不能对子类型强加前提条件。

2. 子类型不能削弱后置条件。

3. 超类型不变量必须保留在子类型中。

4. 子类型方法参数的反面子类型方法参数:逆变

5. 子类型内返回类型的协方差。子类型方法返回值:协方差

6. 子类型的方法不得引发新的异常,除非它们本身是其超类型的方法引发的异常的子类型。

如何抛出异常

使用Exception构造函数将现场错误信息完整传递给客户端。

找到一个可以表达错误的异常类,或者构造一个新的异常类。

构造Exception 类的实例,写入并引发错误消息。

当抛出异常时,该方法不会将控制权返回给调用客户端,因此无需考虑返回错误代码。

创建异常类

如果JDK 提供的异常类不能充分描述程序中发生的错误,您可以创建自己的异常类。

只需派生自Exception 或Exception 的子类即可,例如IOException。

通常会提供默认构造函数和带有详细消息的构造函数。

Throwable 超类的toString 方法返回包含详细消息的字符串。这对于调试很有用。

异常信息还包括“犯罪现场信息”的异常类定义和辅助函数

当抛出异常时,本地信息会记录在异常中。

使用这些信息可以为用户在处理异常时提供更有帮助的帮助。

捕获异常

如果发生异常后找不到处理器,则执行程序将终止并将堆栈跟踪打印到控制台。

如果try 块中的代码引发catch 子句中指定的类的异常,

– 程序将跳过try 块中的其余代码。

– 程序执行catch 子句中的处理程序代码。

– 如果try 块中的代码未引发异常,则程序将跳过catch 子句。

– 如果方法内的代码抛出除catch 子句中指定的异常类型之外的异常类型,则该方法立即退出。

– 优选地,调用者之一提供该类型的catch 子句。

处理异常的另一种选择是不执行任何操作并将异常传递给调用者。

如果您尝试自行处理无效,——负责上传。

然而,有时你不知道如何处理它,所以要小心你的主机,让你的客户自己处理。

消息:

如果超类型的方法不抛出异常,则子类型的方法必须捕获所有已检查的异常。

子类型上的方法不能抛出比其父类型上的方法更多的异常。

重新抛出异常并连接

Catch 语句主要用于异常处理,但也可以在catch 语句内引发异常。

目的是改变异常的类型,使客户端更容易检索和处理错误信息。

最后条款

当您的代码引发异常时,该方法中的其余代码将停止处理,并且该方法中正常运行的任何代码(文件、数据库连接等)都会终止。由于该方法知道这些资源,因此当需要清理这些资源时就会出现问题。如果异常发生前应用了某些资源,则异常发生后必须妥善清理这些资源。

一种解决方案是捕获并重新抛出所有异常。然而,这个解决方案很麻烦,因为它需要在两个地方进行资源分配清理:在常规代码中和在异常代码中。

Java有一个更好的解决方案:finally子句。

分析堆栈跟踪元素

异常调用栈

当Java 方法中发生异常时,该方法会创建一个异常对象并将其传递给JVM(即该方法“抛出”异常)。 Exception对象包含异常的类型以及异常发生时程序的状态。

JVM 负责寻找异常处理程序来处理异常对象。在调用堆栈中向后搜索,直到找到与特定异常对象类匹配的异常处理程序(在Java 术语中,这称为“捕获”异常)。

如果JVM 在调用堆栈中找不到任何方法的匹配异常处理程序,它将终止程序。

分析堆栈跟踪元素

堆栈跟踪是程序执行中特定点的所有挂起方法调用的列表。

每当您的Java 程序因未捕获的异常而退出时,您几乎肯定会看到堆栈跟踪列表。

您可以通过调用Throwable 类的printStackTrace 方法来访问堆栈跟踪的文本描述。

更灵活的方法是getStackTrace 方法。

可以通过编程方式分析的StackTraceElement 对象的数组。

分析框架

StackTraceElement 类具有检索文件名和行号以及执行该代码行的类名和方法名的方法。

toString 方法生成包含所有这些信息的格式化字符串。

断言

第一道防线:杜绝bug

防止错误的最佳方法是通过设计使它们不可能出现。最好的防御是不引入错误

– 静态检查:通过在编译时检测来消除许多错误。

– 动态检查:Java动态检测数组溢出错误并使其不可能发生。如果您尝试在数组或列表中使用越界索引,Java 会自动生成错误。 —— 未经检查的异常/运行时错误

-Immutable:不可变类型是指一旦创建其值就不会改变的类型。

– 不可变值:final 导致它们仅被分配一次,但永远不会重新分配。

– 不可变引用:最终,引用变得不可分配,但它指向的对象可能是可变的或不可变的。

第二道防线:本地化错误

如果您无法防止错误,您可以尝试将其限制在程序的一小部分,这样您就不必费力寻找错误的原因。如果无法避免,尽量将程序误差限制在尽可能小的范围内。

– 当本地化到单个方法或小模块时,您可以通过检查程序文本来发现错误。限制在方法内且不扩散

– 更快的故障:越早发现问题(越接近原因),就越容易修复。发生故障的时间越早,就越容易检测到,修复的速度也就越快。

断言:如果不满足先决条件,此代码将引发AssertionError 异常并终止程序。防止调用者错误传播。很快就会崩溃

前提条件检查是防御性编程的一个例子

前提条件检查是防御性编程的经典形式

– 真正的程序很少是没有错误的。

– 防御性编程提供了一种减少错误影响的方法,即使您不知道错误在哪里。

你主张什么以及为什么主张它?

断言:在开发过程中嵌入代码中,以测试某些“假设”是否正确。如果为true 则表示程序运行成功,否则表示发生错误。

每个断言都包含一个布尔表达式,程序运行时该表达式预计为true。否则,JVM 会抛出断言错误。

此错误意味着存在需要纠正的无效假设。断言失败意味着内部假设被违反

此断言证实了您对程序行为的假设,并增加了程序没有错误的信心。

程序员对其代码的质量有很高的信心: 对代码所做的假设仍然是正确的。

断言记录了程序员在代码中所做的假设,并且不会影响运行时性能(在实际使用中,断言没有任何作用)。

断言通常包括两个参数,它们描述一个布尔表达式,假设该表达式为真,如果不为真则显示一条消息。

Java语言有关键字Assert,它有两种形式:

– 断言条件;

-断言条件:消息;

– 如果布尔表达式的计算结果为false,则两个语句都会计算条件并抛出AssertionError。

– 在第二个语句中,表达式被传递到AssertionError 对象的构造函数并转换为消息字符串。当断言失败时,错误消息中会打印描述,该描述可用于向程序员提供有关失败原因的其他详细信息。

当发生错误时,创建的消息会显示给用户,以便于查找错误所在。

什么是被主张的,什么是未被主张的?

#以上关于软件构建类的注释:第12章—— 软件构建相关内容,强调准确性和鲁棒性来源网络,仅供参考。相关信息请参见官方公告。

原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/91426.html

(0)
CSDN's avatarCSDN
上一篇 2024年6月22日 上午8:30
下一篇 2024年6月22日 上午9:06

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注