首页 > 代码库 > 第8章 异常
第8章 异常
一个定义良好的API,应该包含了使用该方法的前置条件和后置条件。然而,程序运行的环境是复杂的,程序在执行过程中可能遇到各种错误。为此,源代码要为可能遇到错误做一些准备。编程时要预见到:方法执行时可能会遇到意外情况。错误的根源,可能是不恰当的外部环境,可能是方法调用者传递了不恰当的实参——即错误环境或非法参数。
服务的设计者,必须考虑到可能发生的异常事件、必须考虑抛出异常对象、做好本方法的文档使方法具有良好的接口。
方法的设计者有义务对方法参数的有效性进行检查。不管方法的文档中是否使用前置条件对参数加以明确地说明,非法参数的检查是方法实现的第一步。假设不采用异常对象而使用传统的错误处理机制(例如C语言那样),charAt()就得给异常事件返回一个特殊的值,调用者判断该返回值以处理异常,请现在就阅读[例程 8-4代码混杂] (例程放在[8.1.4自定义异常类]中是为了便于比较传统手段和自定义异常)。这种传统手段存在诸多问题:
- 代码混杂。正常的功能代码与大量的、对系统中可能出现的各种错误进行处理的代码混杂在一起,严重降低了程序的可读性。将业务逻辑与异常处理分离,可以说是Java异常机制最重要的目的。
- 同一个错误没有加以数据抽象。以某种数据类型封装异常或错误,而不是将检测同一个错误的代码分布在程序的各个角落,能够有效地增强复用性。标准的异常类库的建设,使程序员不再需要在异常处理问题上花太多精力。
- 特殊的返回值问题。
练习8-3:说明面向对象的异常处理机制的优点。
当程序执行遇到exception,程序是就此结束运行还是应该从异常事件中恢复呢?这就得看exception的严重程度。
(1)病入膏肓就让程序安乐死,(2)大病小灾可以救死扶伤,(3)程序bug引起的(不应该出现的异常事件)则不予理会(打电话给编写那段导致异常代码的程序员,让他修改)。java.lang.Throwable类是 Java 语言中所有错误或异常的父类。情况(1)即严重的系统错误封装为Error、情况(3)即需要剔除的异常封装为RuntimeException。这两种情况都不需要对异常事件加以处理,而是要在源代码中寻找逻辑或用法错误并修改。情况(2)则是应该处理的异常,它们与情况(3)都是Exception的子类,因而Exception及其非RuntimeException子类,编译器认为方法的调用者必须处理这类异常,也会监督、检查调用者是否处理了该异常,故称它们为被检查的异常(checked Exceptions),一般称为检查型异常,口语化为“要处理的异常”。
所以:
- Error:程序员不抛出、不捕捉、不处理。
- RuntimeException:由系统通过默认的异常处理程序自动抛出,自行处理。不强制要求程序员捕捉和处理。调试程序时改正。
- 检查型异常:方法头中用throws子句声明,方法体中以throw语句抛出;调用者以throws子句向上推卸处理责任、或者使用try-catch捕捉和处理。
JUnit中有很多的例子可以作为学习Java异常的补充材料。在相关小节中补充。
第8章 异常