在错误的情况下正确关闭构造函数和关闭方法中的资源

问题描述

给定一个 AutoCloseableMyClass,它在其构造函数中创建多个 SomeResource 类型的资源。 SomeResource 还实现了 AutoCloseable,它的构造函数和它的 close 方法都可能抛出一个 IOException。我如何最好地确保:

这是示例类:

public class MyClass implements AutoCloseable {

    private final SomeResource resource1;
    private final SomeResource resource2;
    private final SomeResource resource3;

    MyClass() throws IOException {
        resource1 = new SomeResource();
        resource2 = new SomeResource();
        resource3 = new SomeResource();
    }

    @Override public void close() throws IOException {
        resource1.close();
        resource2.close();
        resource3.close();
    }
}

解决方法

a) 构造函数不能使用 try-with-resources 或 try-finally,因为如果资源可以成功创建,则不应关闭资源。相反,资源的创建应该跟在一个 try-catch 块之后,以确保资源在其创建后出现任何错误时关闭。在 catch 块中,必须重新抛出异常,以免丢失。

b) close 方法可以使用 try-with-resource 块来确保关闭所有资源

public class MyClass implements AutoCloseable {

  private final SomeResource resource1;
  private final SomeResource resource2;
  private final SomeResource resource3;

  MyClass() throws IOException {
    resource1 = new SomeResource();
    try {
      resource2 = new SomeResource();
      try {
        resource3 = new SomeResource();
      } catch (IOException | RuntimeException e) {
        closeIgnoreException(resource2);
        throw e;
      }
    } catch (IOException | RuntimeException e) {
      closeIgnoreException(resource1);
      throw e;
    }
  }

  private static void closeIgnoreException(AutoCloseable autoCloseable) {
    try { autoCloseable.close(); } catch (Exception ignore) {}
  }

  @Override
  public void close() throws IOException {
    try (resource1;
        resource2;
        resource3) {}
  }
}

在 Java 9 之前,try-with-resources 语句不接受任何引用。因此,close 方法必须通过以下方式实现:

  public void close() throws IOException {
    try (SomeResource r1 = resource1;
        SomeResource r2 = resource2;
        SomeResource r3 = resource3) {}
  }