当前教程
什么是异常?
系列中的下一个

系列中的下一个: 捕获和处理异常

什么是异常?

 

什么是异常?

术语异常是“异常事件”的简写。

定义异常是在程序执行期间发生的事件,它会中断程序指令的正常流程。

当方法中发生错误时,该方法会创建一个对象并将其传递给运行时系统。该对象称为异常对象,包含有关错误的信息,包括其类型以及错误发生时程序的状态。创建异常对象并将其传递给运行时系统称为抛出异常。

方法抛出异常后,运行时系统会尝试找到处理它的东西。处理异常的可能“东西”集是已调用以到达发生错误的方法的已排序方法列表。方法列表称为调用堆栈(参见下图)。

The call stack

调用堆栈

运行时系统在调用堆栈中搜索包含可以处理异常的代码块的方法。此代码块称为异常处理程序。搜索从发生错误的方法开始,并按方法调用的反向顺序遍历调用堆栈。找到合适的处理程序后,运行时系统会将异常传递给处理程序。如果抛出的异常对象的类型与处理程序可以处理的类型匹配,则异常处理程序被认为是合适的。

选择的异常处理程序被称为捕获异常。如果运行时系统在调用堆栈上彻底搜索所有方法,但没有找到合适的异常处理程序(如下图所示),则发生错误的线程将终止。如果此线程是主线程,则运行时系统(以及程序)将终止。

Searching the call stack for the exception handler

在调用堆栈中搜索异常处理程序

使用异常来管理错误比传统的错误管理技术有一些优势。您可以在 异常的优势 中了解更多信息。

 

捕获或指定要求

有效的 Java 编程语言代码必须遵守捕获或指定要求。这意味着可能抛出某些异常的代码必须包含在以下两种情况之一中

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

不遵守捕获或指定要求的代码将无法编译。

并非所有异常都受捕获或指定要求的约束。为了理解原因,我们需要查看三种基本类型的异常,其中只有一种受要求约束。

 

三种类型的异常

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

检查异常受捕获或指定要求的约束。所有异常都是检查异常,除了由ErrorRuntimeException及其子类指示的异常。

第二种异常是错误。这些是应用程序外部的异常情况,应用程序通常无法预料或从中恢复。例如,假设一个应用程序成功地打开了一个文件以供输入,但由于硬件或系统故障而无法读取该文件。不成功的读取将抛出java.io.IOError。应用程序可以选择捕获此异常,以通知用户问题——但程序也可能更有意义地打印堆栈跟踪并退出。

错误不受捕获或指定要求的约束。错误是由Error及其子类指示的那些异常。

第三种异常是运行时异常。这些是应用程序内部的异常情况,应用程序通常无法预料或从中恢复。这些通常表示编程错误,例如逻辑错误或 API 的不当使用。例如,考虑前面描述的将文件名传递给FileReader构造函数的应用程序。如果逻辑错误导致将null传递给构造函数,构造函数将抛出NullPointerException。应用程序可以捕获此异常,但消除导致异常发生的错误可能更有意义。

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

错误和运行时异常统称为未检查异常

 

绕过捕获或指定

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


上次更新: 2021 年 9 月 14 日


当前教程
什么是异常?
系列中的下一个

系列中的下一个: 捕获和处理异常