try-with-resources 语句
Version >= Java SE 7
正如 try-catch-final 语句示例所示,使用 finally
子句进行资源清理需要大量的样板代码才能正确实现边缘情况。Java 7 以 try-with-resources 语句的形式提供了一种更简单的方法来处理这个问题。
什么是资源?
Java 7 引入了 java.lang.AutoCloseable
接口,允许使用 try-with-resources 语句管理类。实现 AutoCloseable
的类的实例称为资源。这些通常需要及时处理,而不是依靠垃圾收集器来处理它们。
AutoCloseable
接口定义了一个方法:
public void close() throws Exception
close()
方法应以适当的方式处理资源。规范声明在已经处理的资源上调用该方法应该是安全的。另外,强烈建议实现 Autocloseable
的类声明 close()
方法抛出比 Exception
更具体的异常,或者根本没有例外。
各种标准 Java 类和接口实现了 AutoCloseable
。这些包括:
InputStream
,OutputStream
及其子类Reader
,Writer
及其子类Socket
和ServerSocket
及其子类Channel
及其子类,和- JDBC 接口
Connection
,Statement
和ResultSet
及其子类。
应用程序和第三方类也可以这样做。
基本的 try-with-resource 语句
try-with-resources 的语法基于经典的 try-catch , try-finally 和 try-catch-finally 形式。这是一个基本形式的例子; 即没有 catch
或 finally
的形式。
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
}
在 try
子句之后,要管理的资源在 (...)
部分中声明为变量。在上面的例子中,我们声明了一个资源变量 stream
并将其初始化为新创建的 PrintStream
。
资源变量初始化后,执行 try
块。完成后,将自动调用 stream.close()
以确保资源不会泄漏。请注意,无论块如何完成,都会发生 close()
调用。
增强的 try-with-resource 语句
在试穿与资源语句可以用 catch
和 finally
块增强,与 Java 的前 7 的 try-catch-终于语法。下面的代码片段为我们之前的代码片段添加了一个 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
块中捕获。
管理多个资源
上面的代码段显示了一个被管理的资源。事实上, try-with-resources 可以在一个语句中管理多个资源。例如:
try (InputStream is = new FileInputStream(file1);
OutputStream os = new FileOutputStream(file2)) {
// Copy 'is' to 'os'
}
这表现得像你期望的那样。is
和 os
都在 try
块结束时自动关闭。有几点需要注意:
- 初始化发生在代码顺序中,后来的资源变量初始化程序可以使用先前的值。
- 将清除已成功初始化的所有资源变量。
- 资源变量按其声明的相反顺序进行清理。
因此,在上面的例子中,is
在 os
之前被初始化并且在它之后被清理,并且如果在初始化 os
时存在异常则将清除 is
。
try-with-resource 和经典 try-catch-finally 的等价性
Java 语言规范根据经典的 try-catch-finally 语句指定 try-with-resource 表单的行为。 (有关详细信息,请参阅 JLS。) **
例如,这个基本的资源尝试 :
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
}
被定义为等同于此 try-catch-finally :
// Note that the constructor is not part of the try-catch statement
PrintStream stream = new PrintStream("hello.txt");
// This variable is used to keep track of the primary exception thrown
// in the try statement. If an exception is thrown in the try block,
// any exception thrown by AutoCloseable.close() will be suppressed.
Throwable primaryException = null;
// The actual try block
try {
stream.println("Hello world!");
} catch (Throwable t) {
// If an exception is thrown, remember it for the finally block
primaryException = t;
throw t;
} finally {
if (primaryException == null) {
// If no exception was thrown so far, exceptions thrown in close() will
// not be caught and therefore be passed on to the enclosing code.
stream.close();
} else {
// If an exception has already been thrown, any exception thrown in
// close() will be suppressed as it is likely to be related to the
// previous exception. The suppressed exception can be retrieved
// using primaryException.getSuppressed().
try {
stream.close();
} catch (Throwable suppressedException) {
primaryException.addSuppressed(suppressedException);
}
}
}
(JLS 指定实际的 t
和 primaryException
变量对于普通的 Java 代码是不可见的。)
增强形式的 try-with-resources 被指定为与基本形式的等价。例如:
try (PrintStream stream = new PrintStream(fileName)) {
stream.println("Hello world!");
} catch (NullPointerException ex) {
System.err.println("Null filename");
} finally {
System.err.println("All done");
}
相当于:
try {
try (PrintStream stream = new PrintStream(fileName)) {
stream.println("Hello world!");
}
} catch (NullPointerException ex) {
System.err.println("Null filename");
} finally {
System.err.println("All done");
}