使用 try-catch 捕获异常
可以使用 try...catch 语句捕获和处理异常。
(事实上,try 语句采用其他形式,如关于 try...catch...finally 和 try-with-resources 的其他示例中所述。
)
用一个 catch 块尝试捕获
The most simple form looks like this:
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”。
由于 RuntimeException 是 Exception 的子类型,因此将匹配第一个(更一般的)捕获。
第二个(更具体的)catch 永远不会被执行。
多异常捕获块
从 Java SE 7 开始,单个 catch 块可以处理一系列不相关的异常。
列出了异常类型,用竖线 (|) 符号分隔。
例如:
try {
doSomething();
} catch (SomeException | SomeOtherException e) {
handleSomeException(e);
}
多异常捕获的行为是单异常情况的简单扩展。
如果抛出的异常匹配(至少)列出的异常之一,则捕获匹配。
e 的类型是列表中异常类型的合成联合。
当使用 e 的值时,它的静态类型是联合类型中最不常见的超类型。
但是,如果在 catch 块中重新抛出 e,则抛出的异常类型是联合中的类型。
例如:
public void method() throws IOException, SQLException
try {
doSomething();
} catch (IOException | SQLException e) {
report(e);
throw e;
}
在上面,IOException 和 SQLException 是检查异常,其最不常见的超类型是 Exception。
这意味着 report 方法必须匹配 report(Exception)。
但是,编译器知道 throw 只能抛出 IOException 或者 SQLException 。
因此,方法可以声明为 throws IOException 、SQLException 而不是 throws Exception。
try-with-resources 语句
正如 try-catch-final 语句示例所示,使用 finally 子句的资源清理需要大量“样板”代码才能正确实现边缘情况。
Java 7 以 try-with-resources 语句的形式提供了一种更简单的方法来处理这个问题。
Java 7 引入了 java.lang.AutoCloseable 接口以允许使用 try-withresources 语句管理类。
实现 AutoCloseable 的类的实例称为资源。
这些通常需要及时处理,而不是依赖垃圾收集器来处理。
AutoCloseable 接口定义了一个方法:
public void close() throws Exception
close() 方法应该以适当的方式处理资源。
规范指出,在已经处理的资源上调用该方法应该是安全的。
此外,强烈鼓励实现 Autocloseable 的类声明 close() 方法以抛出比 Exception 更具体的异常,或者根本不抛出异常。
####基本的 try-with-resource 语句
try-with-resources 的语法基于经典的 try-catch、try-finally 和 try-catch-finally 形式。
这是一个“基本”形式的例子;例如:没有捕获或者最后的形式。
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
}
要管理的资源在 try 子句之后的 (...) 部分中声明为变量。
在上面的示例中,我们声明了一个资源变量流并将其初始化为新创建的 PrintStream。
一旦资源变量被初始化,就会执行 try 块。
完成后,将自动调用 stream.close() 以确保资源不会泄漏。
请注意,无论代码块如何完成都会调用close()。
增强的 try-with-resource 语句
try-with-resources 语句可以使用 catch 和 finally 块来增强,就像 Java 7 之前的 try-catchfinally 语法一样。
下面的代码片段在我们之前的代码片段中添加了一个 catch 块来处理
PrintStream 构造函数可以抛出的 FileNotFoundException:
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
} catch (FileNotFoundException ex) {
System.err.println("Cannot open the file");
} finally {
System.err.println("All done");
}
如果资源初始化或者 try 块抛出异常,则将执行 catch 块。
finally 块将始终被执行,就像传统的 try-catch-finally 语句一样。
不过有几点需要注意:
- 资源变量在 catch 和 finally 块中超出范围。
- 资源清理将在语句尝试匹配 catch 块之前发生。
- 如果自动资源清理抛出异常,则可以在 catch 块之一中捕获该异常。
Java 中的异常处理是处理运行时错误以维持应用程序正常流程的强大机制之一。
Throwable 类型的对象及其子类型可以使用 throw 关键字向上发送堆栈,并使用 try...catch 语句捕获。
