Java™ 教程(捕获或指定要求)

捕获或指定要求

有效的Java编程语言代码必须遵照捕获或指定需求,这意味着可能抛出某些异常的代码必须包含如下任一项:java

  • 捕获异常的try语句,try必须为异常提供处理程序,如捕获和处理异常中所述。
  • 一种方法,指定它能够抛出异常,该方法必须提供一个throws子句,列出异常,如经过方法抛出指定异常中所述。

不符合捕获或指定要求的代码将没法编译。程序员

并不是全部异常都受捕获或指定要求的约束,为了理解缘由,咱们须要查看三个基本类别的异常,其中只有一个受要求限制。编程

三种异常

第一种异常是已检查的异常,这些都是编写良好的应用程序应该预料到并从中恢复的异常状况。例如,假设应用程序提示用户输入文件名,而后经过将名称传递给java.io.FileReader的构造函数来打开该文件,一般,用户提供现有可读文件的名称,所以FileReader构造对象成功,而且应用程序的执行正常进行。但有时用户提供不存在的文件的名称,构造函数抛出java.io.FileNotFoundException,一个编写良好的程序将捕获此异常并通知用户该错误,可能提示更正的文件名。segmentfault

已检查的异常状况受捕获或指定要求的约束,除ErrorRuntimeException及其子类表示的异常外,全部异常都是通过检查的异常。数组

第二种异常是错误,这些是应用程序外部的异常状况,应用程序一般没法预测或恢复。例如,假设应用程序对于输入成功打开文件,但因为硬件或系统故障而没法读取文件,不成功的读取将抛出java.io.IOError,应用程序可能会选择捕获此异常,以便通知用户该问题 — 可是程序打印堆栈跟踪并退出也可能有意义。编程语言

错误不受捕获或指定要求的约束,错误是Error及其子类表示的异常。函数

第三种异常是运行时异常,这些是应用程序内部的异常状况,应用程序一般没法预测或恢复,这些一般表示编程bug,例如逻辑错误或API的不当使用。例如,考虑前面描述的应用程序将文件名传递给FileReader的构造函数,若是逻辑错误致使将null传递给构造函数,则构造函数将抛出NullPointerException,应用程序能够捕获此异常,但消除致使异常发生的bug可能更有意义。设计

运行时异常不受捕获或指定要求的约束,运行时异常是RuntimeException及其子类表示的异常。指针

错误和运行时异常统称为未经检查的异常。code

经过方法抛出指定异常

有时,代码能够捕获可能在其中发生的异常,可是,在其余状况下,最好让调用堆栈中进一步的方法处理异常。例如,若是你将ListOfNumbers类做为类包的一部分提供,则可能没法预测包的全部用户的需求,在这种状况下,最好不捕获异常并容许调用堆栈进一步的方法来处理它。

若是writeList方法没有捕获可能在其中发生的已检查异常,则writeList方法必须指定它能够抛出这些异常,让咱们修改原始的writeList方法来指定它能够抛出而不是捕获它们的异常,这是不能编译的writeList方法的原始版本。

public void writeList() {
    PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt"));
    for (int i = 0; i < SIZE; i++) {
        out.println("Value at: " + i + " = " + list.get(i));
    }
    out.close();
}

要指定writeList能够抛出两个异常,请将throws子句添加到writeList方法的方法声明中,throws子句包含throws关键字,后跟逗号分隔的该方法抛出的全部异常列表,该子句在方法名称和参数列表以后以及定义方法范围的大括号以前,这是一个例子。

public void writeList() throws IOException, IndexOutOfBoundsException {

请记住,IndexOutOfBoundsException是未经检查的异常,在throws子句中包含它不是强制性的,你能够仅写下面的内容。

public void writeList() throws IOException {

绕过捕获或指定

一些程序员认为捕获或指定要求是异常机制中的一个严重缺陷,并经过使用未经检查的异常代替已检查的异常来绕过它,一般,不建议这样作,未经检查的异常 — 争议部分讨论什么时候适合使用未经检查的异常。

未经检查的异常 — 争议

由于Java编程语言不须要捕获或指定未经检查的异常的方法(RuntimeExceptionError及其子类),程序员可能会试图编写只抛出未经检查的异常的代码,或者让全部异常子类继承自RuntimeException,这两个快捷方式都容许程序员编写代码而没必要担忧编译器错误,也没必要费心去指定或捕获任何异常。虽然这对程序员来讲彷佛很方便,但它会回避catch的意图或指定要求,而且可能会致使其余人使用你的类时出现问题。

为何设计人员决定强制一个方法指定能够在其范围内抛出的全部未捕获的已检查异常?方法能够抛出的任何Exception都是该方法的公共编程接口的一部分,那些调用方法的人必须知道方法能够抛出的异常,以便他们能够决定如何处理它们,这些异常与该方法的编程接口同样,也是其参数和返回值的一部分。

下一个问题多是:“若是记录方法的API很是好,包括它能够抛出的异常,为何不指定运行时异常呢?”,运行时异常表示编程问题致使的问题,所以,没法合理地指望API客户端代码从它们恢复或以任何方式处理它们,这些问题包括算术异常,例如除以零;指针异常,例如尝试经过空引用访问对象;索引异常,例如尝试经过索引太大或过小来访问数组元素。

运行时异常能够在程序中的任何地方发生,而在典型的程序中,它们能够很是多,必须在每一个方法声明中添加运行时异常会下降程序的清晰度,所以,编译器不要求你捕获或指定运行时异常(尽管你能够)。

抛出RuntimeException的常见作法之一是用户错误地调用方法,例如,方法能够检查其中一个参数是否错误地为null,若是参数为null,则该方法可能会抛出NullPointerException,这是一个未经检查的异常。

通常来讲,不要由于你不但愿指定方法能够抛出的异常而烦恼而抛出RuntimeException或建立RuntimeException的子类。

这是底线指南:若是能够合理地指望客户端从异常中恢复,则将其做为已检查的异常,若是客户端没法执行任何操做以从异常中恢复,请将其设置为未经检查的异常。


上一篇:什么是异常?

下一篇:捕获和处理异常

相关文章
相关标签/搜索