目录
防御性编程(Defensive programming)是防御式设计的一种具体体现,它是为了保证,对程序的不可预见的使用,不会造成程序功能上的损坏。它可以被看作是为了减少或消除墨菲定律效力的想法。防御式编程主要用于可能被滥用,恶作剧或无意地造成灾难性影响的程序上。
防御式编程有这样一种思维,类似于驾驶汽车,永远不能确定其他司机会做什么,同时要保护好自己,哪怕是其他司机犯的错误。
也就是说,子程序不因传入错误数据而被破坏,哪怕是其他子程序产生的错误数据。
因此,如何处理错误数据?有三种方式:
(1)检查来源于外部数据的值
(2)检查子程序所有输入参数的值
(3)决定如何处理错误的输入数据
一、断言
1.断言的概念
编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设。程序员相信在程序中的某个特定点该表达式值为真,可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。
2.断言的形式
断言可以有两种形式:assert Expression1;assert Expression1:Expression2。
其中Expression1应该总是一个布尔值,Expression2是断言失败时输出的失败消息的字符串。如果Expression1为假,则抛出一个 AssertionError,这是一个错误,而不是一个异常,也就是说是一个不可控制异常(unchecked Exception),AssertionError由于是错误,所以可以不捕获,但不推荐这样做,因为那样会使你的系统进入不稳定状态。
3.断言的使用
(1)可以在预计正常情况下程序不会到达的地方放置断言 :assert false
(2)断言可以用于检查传递给私有方法的参数。(对于公有方法,因为是提供给外部的接口,所以必须在方法中有相应的参数检验才能保证代码的健壮性)
(3)使用断言测试方法执行的前置条件和后置条件
(4)使用断言检查类的不变状态,确保任何情况下,某个变量的状态必须满足。(如age属性应大于0小于某个合适值)
二、错误处理
1.概念
错误是指导致系统不能按照用户意图工作的一切原因、事件。在程序设计过程中,由于某些错误的存在,致使程序无法正常运行,处理这些错误以使程序正确运行就称为错误处理。错误处理功能是衡量编译器性能的重要方面,它在帮助程序员尽快修改程序方面起到了非常重要的作用。
2.错误类型
(1)语法错误
语法错误是因为源程序中不正确的代码产生的,即在编写程序时没有遵守语法(或词法)规则,书写了错误的语法代码,从而导致编译器无法正确解释源代码而产生的错误,通常是由于录入的错误引起的,它在词法分析或语法分析时检测出来。如“非法字符”、“括号不匹配”、“缺少;”之类的错误。图1为一些常见语法错误。
(2)语义错误
语义错误是指源程序中不符合语义规则的错误,即一条语句试图执行一条不可能执行的操作而产生的错误。语义错误有的在语义分析时检测处来,有的在运行时才能检测出来。如变量声明错误、作用域错误、数据存储区的溢出等错误。
(3)逻辑错误
逻辑错误是指程序的运行结果和程序员的设想有出入时产生的错误。这类错误并不直接导致程序在编译期间和运行期间出现错误,但是程序未按预期方式执行,产生了不正确的运行结果,较难发现。这种错误只能通过分析结果,将结果与设计方案进行对比来发现。
3.错误分类
(1)按照错误类型
操作员错误 | 与人机界面交互时不满足输入规则、输入范围等发生的错误 | |
运行时错误 | 与外部资源交互时发生的错误,如网络、文件系统、数据库、其它业务应用系统等 | |
程序员错误 | 与客户模块交互时不满足前置条件后置条件发生的错误,如类库被其他程序员调用时参数超出范围等 |
(2)按照调用类型
分为同步调用和异步调用。
(3)按照展现方式
分为界面提示和记录日志。
三、异常
异常:程序在运行过程中发生由于外部问题(如硬件错误、输入错误)等导致的程序异常事件。
(在Java等面向对象的编程语言中)异常本身是一个对象,产生异常就是产生了一个异常对象。
异常都是从类Throwable类派生出来的,而Throwable类是直接从Object类继承而来,可见Java对异常处理的重视程度。
Error:系统内部错误,这类错误由系统进行处理,程序本身无需捕获处理;
Exception(可以处理的异常)
RuntimeException:可以捕获,也可以不捕获的异常。
继承Exception的其他类:必须被捕获的Exception,查阅API帮助中方法后面会有说明此方法抛出哪些异常,这些异常必须被捕获。
捕获:
通过try…catch语句进行捕获异常。
通过throw抛出异常,throws向上一级调用方法抛出异常。
如何使用异常?
(1)只在真正的例外情况下,才使用异常,不要推卸责任。
错误情况可以局部处理,就去处理,不要抛异常。
比如空指针异常NullPointerException,数组越界异常IndexOutOfBoundsException,可以提前检查。检查不到才抛异常。
(2)抛异常要在同一抽象层次,并且异常要做好分类。
也就是说,不要在业务层次抛底层的异常,也不要try一大段代码,区分稳定代码和非稳定代码。
(3)被捕获后不要用来做流程控制和条件控制,或者不处理。
做流程控制效率太慢,不处理的话,就抛给调用方。
(4)异常消息应包含所有导致异常发生的所有信息。
比如数组越界异常,应该包含上界,下界,非法下标值等。
(5)推荐使用全局异常处理机制。
四、隔离程序
这是一种在设计上简化错误处理的策略,事实上,如果所有的代码都做异常和错误处理,会使代码变得臃肿,可读性下降,我们需要在高层次上面避免这种情况的发生。本质上,它是将错误和异常处理集中化,通常的软件设计实际上都是对数据进行处理和再加工,以及展现,很大一部分的错误都是由于不正确的数据设置导致的,那么我们可以把数据的错误处理专门用一层来处理以使得内部的逻辑可以不用对数据进行检测。