在本教程中,我们将学习try-with-resources语句以自动关闭资源。
try-with-resources语句在语句末尾自动关闭所有资源。资源是程序结束时要关闭的对象。
其语法为:
try (resource declaration) { // use of the resource } catch (ExceptionType e1) { // catch block }
从上面的语法可以看出,我们通过以下方式声明try-with-resources语句:
在try子句中声明和实例化资源。
指定并处理关闭资源时可能引发的所有异常。
注意:try-with-resources语句关闭实现AutoCloseable接口的所有资源。
让我们以实现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 =>"+line); } } catch (IOException e) { System.out.println("IOException in try block =>" + e.getMessage()); } } }
如果找不到test.txt文件,则输出。
IOException in try-with-resources block =>test.txt (No such file or directory)
如果找到了test.txt文件,则输出。
Entering try-with-resources block Line =>test line
在此示例中,我们使用的实例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和更高版本中,可以通过Throwable.getSuppressed()从try块引发的异常中调用方法来检索抑制的异常。
此方法返回所有抑制的异常的数组。我们在catch块中得到了抑制的异常。
catch(IOException e) { System.out.println("Thrown exception=>" + e.getMessage()); Throwable[] suppressedExceptions = e.getSuppressed(); for (int i=0; i<suppressedExceptions.length; i++) { System.out.println("Suppressed exception=>" + suppressedExceptions[i]); } }
这是使用try-with-resources的优点:
在Java 7引入此功能之前,我们必须使用该finally块来确保关闭资源以避免资源泄漏。
这是一个类似于示例1的程序。但是,在此程序中,我们使用了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 =>"+line); } } catch (IOException e) { System.out.println("IOException in try block =>" + e.getMessage()); } finally { System.out.println("进入 finally 块"); try { if (br != null) { br.close(); } } catch (IOException e) { System.out.println("IOException in finally block =>"+e.getMessage()); } } } }
输出结果
进入try 块 Line =>line from test.txt file 进入 finally 块
从上面的示例可以看出,使用finally块来清理资源使代码更加复杂。
您注意到finally块中的try ... catch块吗? 这是因为在关闭此finally块内的BufferedReader实例时也可能发生IOException,因此也将其捕获并处理。
try-with-resources语句执行自动资源管理。我们不需要显式关闭资源,因为JVM会自动关闭它们。这使代码更具可读性,更易于编写。
我们可以try-with-resources使用分号将多个资源分开,从而在语句中声明多个资源;
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()); } } } }
如果执行该程序时未生成任何异常,则Scannerobject从testRead.txt文件中读取一行并将其写入新testWrite.txt文件中。
进行多个声明时,try-with-resources语句以相反的顺序关闭这些资源。在此示例中,首先关闭PrintWriter对象,然后关闭Scanner对象。
在Java 7中,该try-with-resources语句有一个限制。该资源需要在其块内本地声明。
try (Scanner scanner = new Scanner(new File("testRead.txt"))) { // code }
如果我们在Java 7中在块外部声明资源,则将抛出错误消息。
Scanner scanner = new Scanner(new File("testRead.txt")); try (scanner) { // code }
为了解决此错误,Java 9 改进了该try-with-resources语句,以便即使未在本地声明资源引用也可以使用。上面的代码现在将执行,不会抛出任何编译错误。