跳到主要内容

Java try-with-resources 语句

提示
  1. 自动资源管理try-with-resources 语句自动关闭实现 AutoCloseable 接口的资源,简化了资源管理并减少代码。
  2. 异常处理:允许在 try 子句中捕获并处理异常,同时捕获和抑制从 try 块和资源关闭中抛出的多个异常。
  3. 适用于多资源:可同时声明并管理多个资源,且 Java 9 以上版本允许在 try 块外声明资源。

try-with-resources 语句在语句结束时自动关闭所有资源。资源是程序结束时需要关闭的对象。

其语法为:

try (资源声明) {
// 资源的使用
} catch (ExceptionType e1) {
// catch 块
}

从上述语法可以看出,我们通过以下步骤声明 try-with-resources 语句:

  1. try 子句中声明并实例化资源。
  2. 指定并处理在关闭资源时可能抛出的所有异常。

注意try-with-resources 语句关闭所有实现了 AutoCloseable 接口 的资源。

让我们看一个实现了 try-with-resources 语句的例子。

示例 1:try-with-resources

import java.io.*;

class Main {
public static void main(String[] args) {
String line;
try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
while ((line = br.readLine()) != null) {
System.out.println("行 =>" + line);
}
} catch (IOException e) {
System.out.println("try 块中的 IOException =>" + e.getMessage());
}
}
}

如果未找到 test.txt 文件,则输出

try-with-resources 块中的 IOException =>test.txt (没有这样的文件或目录)

如果找到了 test.txt 文件,则输出

进入 try-with-resources 块
=>测试行

在这个示例中,我们使用 BufferedReader 的实例从 test.txt 文件中读取数据。

try-with-resources 语句中声明并实例化 BufferedReader 确保了无论 try 语句是否正常完成或抛出异常,它的实例都会被关闭。

如果发生异常,可以使用异常处理块或 throws 关键字 来处理。

被抑制的异常

在上面的示例中,当以下情况发生时,try-with-resources 语句可能抛出异常:

  • 未找到文件 test.txt
  • 关闭 BufferedReader 对象。

try 块中也可能抛出异常,因为文件读取可能由于多种原因在任何时候失败。

如果从 try 块和 try-with-resources 语句都抛出了异常,try 块的异常将被抛出,而 try-with-resources 语句的异常将被抑制。

检索被抑制的异常

在 Java 7 及以后的版本中,可以通过调用 try 块抛出的异常的 Throwable.getSuppressed() 方法来检索被抑制的异常。

该方法返回所有被抑制异常的数组。我们在 catch 块中获取被抑制的异常。

catch(IOException e) {
System.out.println("抛出的异常=>" + e.getMessage());
Throwable[] suppressedExceptions = e.getSuppressed();
for (int i=0; i<suppressedExceptions.length; i++) {
System.out.println("被抑制的异常=>" + suppressedExceptions[i]);
}
}

使用 try-with-resources 的优势

以下是使用 try-with-resources 的优势:

1. 不需要 finally 块来关闭资源

在 Java 7 引入此功能之前,我们必须使用 finally 块来确保资源被关闭以避免资源泄漏。这是一个与 示例 1 类似的程序。但在这个程序中,我们使用了 finally 块来关闭资源。

示例 2:使用 finally 块关闭资源

import java.io.*;

class Main {
public static void main(String[] args) {
BufferedReader br = null;
String line;

try {
System.out.println("进入 try 块");
br = new BufferedReader(new FileReader("test.txt"));
while ((line = br.readLine()) != null) {
System.out.println("行 =>" + line);
}
} catch (IOException e) {
System.out.println("try 块中的 IOException =>" + e.getMessage());
} finally {
System.out.println("进入 finally 块");
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
System.out.println("finally 块中的 IOException =>" + e.getMessage());
}

}
}
}

输出

进入 try
=>来自 test.txt 文件的行
进入 finally

从上述示例中可以看出,使用 finally 块清理资源使代码更加复杂。

注意 finally 块中的 try...catch 块吗?这是因为在关闭 finally 块内的 BufferedReader 实例时也可能发生 IOException,因此它也被捕获并处理。

try-with-resources 语句进行自动资源管理。我们不需要显式地关闭资源,因为 JVM 会自动关闭它们。这使得代码更易于阅读和编写。

2. 带有多个资源的 try-with-resources

我们可以在 try-with-resources 语句中通过使用分号 ; 分隔来声明多个资源。

示例 3:带有多个资源的 try

import java.io.*;
import java.util.*;
class Main {
public static void main(String[] args) throws IOException{
try (Scanner scanner = new Scanner(new File("testRead.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
while (scanner.hasNext()) {
writer.print(scanner.nextLine());
}
}
}
}

如果此程序执行时没有产生任何异常,Scanner 对象将从 testRead.txt 文件中读取一行并将其写入新的 testWrite.txt 文件。

当进行多个声明时,try-with-resources 语句会按照相反的顺序关闭这些资源。在此示例中,首先关闭 PrintWriter 对象,然后关闭 Scanner 对象。

Java 9 try-with-resources 的增强

在 Java 7 中,try-with-resources 语句有一个限制。资源需要在其块内本地声明。

try (Scanner scanner = new Scanner(new File("testRead.txt"))) {
// 代码
}

如果我们在 Java 7 中在块外声明了资源,它会生成错误消息。

Scanner scanner = new Scanner(new File("testRead.txt"));
try (scanner) {
// 代码
}

为了解决这个错误,Java 9 改进了 try-with-resources 语句,以便即使资源没有在本地声明,也可以使用资源的引用。上述代码现在将执行而不会产生任何编译错误。