1 程序异常
理想状态下用户输入的数据永远是正确的,然而,在现实世界中却充满了不良的数据和带有问题的代码。
如果一个用户在运行程序期间,由于程序的错误或一些 外部环境的影响造成用户数据的丢失,用户就有可能不再使用这个程序了, 为了避免这类事情的发生,至少应该做到以下几点:
- 向用户通告错误;
- 保存所有的工作结果;
- 允许用户以妥善的形式退出程。
针对这种情况 Java 使 用 一 种 称 为 异 常 处 理 ( exception handing) 的错误捕获机制处理。如果程序需要处理异常情况,那就必须研究程序中可能出现的问题,以及哪类问题是需要我们开发者关注的:
- 用户输入错误
- 代码错误
- 设备错误(硬件、软件、系统、等非程序引起的错误)
针对这些错误,传统方法是返回一个错误码(如-1),但是某些方法(求和) 返回-1并不能区分是错误还是结果。
在Java中,如果某个方法不能够采用正常的途径完成任务,就可以通过异常退出方法。在这种情况下,方法并不返回任何值,而是抛出 (throw) 一个封装了错误信息的对象。方法将会立刻退出,并不返回任何值,调用这个方法的代码也将无法继续执行,取而代之的是,异常处理机制开始搜索能够处理这种异常状况的异常处理器(exception handler)
1.1 异常分类
在 Java 程序设计语言中, 异常对象都是派生于 Throwable 类的一个实例。如果 Java 中内置的异常类不能够满足需求,用户可以创建自己的异常类。
所有的异常都是由 Throwable 继承而来,但在下一层立即分解为两个分 支:Error 和 Exception:
(1)Error(错误)
Error类层次结构描述了 Java 运行时系统的内部错误和资源耗尽错误。 应用程序不应该 抛出这种类型的对象。 如果出现了这样的内部错误, 除了通告给用户,并尽力使程序安全地 终止之外, 再也无能为力了。这种情况很少出现
(2)Exception(异常)
在设计 Java 程序时, 需要关注 Exception 层次结构。 这个层次结构又分解为两个分支: 一个分支派生于 RuntimeException(运行时异常); 另一个分支包含其他异常。
划分两个分支的规则是:由 程序错误导致的异常属于 RuntimeException; 而程序本身没有问题, 但由于像 I/O 错误这类 问题导致的异常属于其他异常
Java 语 言 规 范 将 派 生 于 Error 类 或 RuntimeException 类的所有异常称为非受查 ( unchecked ) 异常,所有其他的异常称为受查( checked) 异常。 编译器将核查是否为所有的受査异常提供了异常处理器。
1.2 声明受检异常
如果用到无法处理的情况,可以抛出一个异常,告诉编译器可能会发生什么错误,方法的首部应该声明所有的可能抛出的异常,这样可以从首部反映出这个方法可能抛出 需要抛出异常的情况:
- 调用一个抛出受査异常的方法, 例如, FilelnputStream 构造器。
- 程序运行过程中发现错误, 并且利用 throw语句抛出一个受查异常。
- 程序出现错误, 例如,a[-l] =0 会抛出一个 ArraylndexOutOffloundsException 这样的 非受查异常。
- Java 虚拟机和运行时库出现的内部错误
声明异常可以通过继承异常类来实现
总之,一个方法必须声明所有可能抛出的受查异常, 而非受查异常要么不可控制( Error), 要么就应该避免发生( RuntimeException)。如果方法没有声明所有可能发生的受查异常,编译器就会发出一个错误消息
1.3 抛出异常
对于一个已经存在的异常类, 将其抛出非常容易D 在这种情况下:
- 1 ) 找到一个合适的异常类。
- 2 ) 创建这个类的一个对象。
- 3 ) 将对象抛出。
一旦方法抛出了异常,这个方法就不可能返回到调用者。也就不必为返回的默认值或错误代码担忧。
1.4 创建异常类
当一个异常是任何标准异常类都没有能够充分地描述清楚的时候,就需要自定义一个异常类,只需要派生于Exception或者Exception的子类。
定义的类应该包含两个构造器, 一个是默认的构造器;另一个是带有详细描述信息的构造器
throwable基本方法
1.2 异常捕获
如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台 上打印出异常信息, 其中包括异常的类型和堆栈的内容。 要想捕获一个异常,使用下列一种方式就可以
- 设置 try/catch语句块
try{
操作
}catch (异常类型 e) {
异常处理器
}final{
程序必须执行操作
}
- 使用throws 在方法后抛出异常
方法 () throws 异常名
1.3 多个异常捕获
catch可以使用多个异常,但是最终只会走一个异常,所以优先将颗粒度小的异常放前面
1.4 再次抛出异常和异常链
在 catch 子句中可以抛出一个异常,这样做的目的是改变异常的类型,如果开发了一个 供其他程序员使用的子系统,那么,用于表示子系统故障的异常类型可能会产生多种解释。 ServletException 就是这样一个异常类型的例子。执行 servlet 的代码可能不想知道发生错误的 细节原因,但希望明确地知道 servlet 是否有问题。
try {
database error
} catch (SQLException e) {
//对于Servlet来说,不关心关于数据库的具体异常,只知道数据库异常就够了
throw new ServletException("database error: " + e.getMessage());
}
这种方式只保留抛出的异常,原来的异常并不会记录,如果想要保存最早抛出的异常,推荐使用
try {
database error
} catch (SQLException e) {
//声明一个异常
Throwable se = new ServletException ("database error");
//将初始抛出的异常储存
se.ini tCause(e);
throw se;
}
Throwable e = se.getCauseO ; //这样就可以获取到初始异常