1. public interface AutoCloseable {
  2. void close() throws Exception;
  3. }

接口用途

持有资源的对象自动调用close方法关闭资源

try-with-resources结构

close方法

try-with-resources语句自动调用;
接口的实现者不要让close方法抛出InterruptedException,
Implementers of this interface are also strongly advised to not have the close method throw InterruptedException. This exception interacts with a thread’s interrupted status, and runtime misbehavior is likely to occur if an InterruptedException is suppressed. More generally, if it would cause problems for an exception to be suppressed, the AutoCloseable.close method should not throw it.

close方法幂等性 不像java.io.Closeable的close方法,此处的close方法不要求幂等性, 即多次调用此方法可能会有副作用,建议实现类将此方法实现为幂等性。

实现原理

编译器自动生成finally块,并在里面调用资源的close方法。

异常屏蔽问题

Java 1.7 为Throwable类新增了addSuppressed方法,支持将一个异常附加到另一个异常身上,从而避免异常屏蔽。

正确使用 try-with-resource

  1. public class TryWithResource {
  2. public static void main(String[] args) {
  3. try (FileInputStream fin = new FileInputStream(new File("input.txt"));
  4. GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(new File("out.txt")))) {
  5. byte[] buffer = new byte[4096];
  6. int read;
  7. while ((read = fin.read(buffer)) != -1) {
  8. out.write(buffer, 0, read);
  9. }
  10. }
  11. catch (IOException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. }

在上述代码中,我们从FileInputStream中读取字节,并且写入到GZIPOutputStream中。GZIPOutputStream实际上是FileOutputStream的装饰器。由于try-with-resource的特性,实际编译之后的代码会在后面带上finally代码块,并且在里面调用fin.close()方法和out.close()方法。我们再来看GZIPOutputStream类的close方法:

  1. public void close() throws IOException {
  2. if (!closed) {
  3. finish();
  4. if (usesDefaultDeflater)
  5. def.end();
  6. out.close();
  7. closed = true;
  8. }
  9. }

可以看到,out变量实际上代表的是被装饰的FileOutputStream类。在调用out变量的close方法之前,GZIPOutputStream还做了finish操作,该操作还会继续往FileOutputStream中写压缩信息,此时如果出现异常,out.close()方法会被略过,然而这个才是最底层的资源关闭方法。
正确的做法是应该在try-with-resource中单独声明最底层的资源,保证对应的close方法一定能够被调用。在上面的例子中,需要单独声明每个FileInputStream以及FileOutputStream:

  1. public class TryWithResource {
  2. public static void main(String[] args) {
  3. try (FileInputStream fin = new FileInputStream(new File("input.txt"));
  4. FileOutputStream fout = new FileOutputStream(new File("out.txt"));
  5. GZIPOutputStream out = new GZIPOutputStream(fout)) {
  6. byte[] buffer = new byte[4096];
  7. int read;
  8. while ((read = fin.read(buffer)) != -1) {
  9. out.write(buffer, 0, read);
  10. }
  11. }
  12. catch (IOException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. }

由于编译器会自动生成fout.close()的代码,这样肯定能够保证真正的流被关闭。