使用 try-catch 捕获异常

可以使用 try...catch 语句捕获和处理异常。 (事实上​​,try 语句采用其他形式,如其他关于 try...catch...finallytry-with-resources例子所述 。)

尝试捕获一个捕获块

最简单的表单如下所示:

try {
    doSomething();
} catch (SomeException e) {
    handle(e);
}
// next statement

一个简单的 try...catch 的行为如下:

  • 执行 try 块中的语句。
  • 如果 try 块中的语句没有抛出异常,则控制转到 try...catch 之后的下一个语句。
  • 如果在 try 块中抛出异常。
    • 测试异常对象以查看它是 SomeException 的实例还是子类型。
    • 如果是,那么 catch 块将捕获异常:
      • 变量 e 绑定到异常对象。
      • 执行 catch 块中的代码。
      • 如果该代码抛出异常,则传播新抛出的异常代替原始异常。
      • 否则,控制传递到 try...catch 之后的下一个语句。
    • 如果不是,则原始异常继续传播。

尝试捕获多个捕获

一个 try...catch 也可以有多个 catch 块。例如:

try {
    doSomething();
} catch (SomeException e) {
    handleOneWay(e)
} catch (SomeOtherException e) {
    handleAnotherWay(e);
}
// next statement

如果有多个 catch 块,则从第一个块开始一次尝试一个,直到找到异常的匹配。执行相应的处理程序(如上所述),然后将控制权传递给 try...catch 语句后的下一个语句。即使处理程序代码抛出异常总是跳过匹配的 catch 块之后的块。

自上而下匹配策略会对 catch 块中的异常不是不相交的情况产生影响。例如:

try {
    throw new RuntimeException("test");
} catch (Exception e) {
    System.out.println("Exception");
} catch (RuntimeException e) {
    System.out.println("RuntimeException");
}

此代码段将输出 Exception 而不是 RuntimeException。由于 RuntimeExceptionException 的子类型,因此第一个(更一般的)catch 将匹配。第二个(更具体的)catch 永远不会被执行。

从中学到的教训是,最具体的 catch 块(就异常类型而言)应该首先出现,而最常见的块应该是最后一块。 (如果永远不能执行 catch,某些 Java 编译器会发出警告,但这不是编译错误。)

多异常捕获块

Version >= Java SE 7

从 Java SE 7 开始,单个 catch 块可以处理不相关的异常列表。列出了异常类型,使用竖线(|)符号分隔。例如:

try {
    doSomething();
} catch (SomeException | SomeOtherException e) {
    handleSomeException(e);
} 

多异常捕获的行为是单异常情况的简单扩展。如果抛出的异常与(至少)列出的一个异常匹配,则 catch 匹配。

规范中还有一些额外的细微之处。e 的类型是列表中异常类型的合成联合。当使用 e 的值时,其静态类型是 union 类型的最不常见的超类型。但是,如果 ecatch 块中重新抛出,则抛出的异常类型是 union 中的类型。例如:

public void method() throws IOException, SQLException
    try {
        doSomething();
    } catch (IOException | SQLException e) {
        report(e);
        throw e;
    }

在上面,IOExceptionSQLException 是检查的例外,其最不常见的超类型是 Exception。这意味着 report 方法必须匹配 report(Exception)。但是,编译器知道 throw 只能投掷 IOException 或者 SQLException。因此,method 可以声明为 throws IOException, SQLException 而不是 throws Exception。 (这是一件好事:看看陷阱 - 投掷 Throwable,Exception,Error 或 RuntimeException 。)