如何使用嵌套对象层次结构确保资源被释放?

时间:2015-09-16 06:53:13

标签: java resource-management

我有这样的代码(简化):

class A {

    B b = new B();

    void close() {
        b.close();
    }
}

class B {

    Closeable mustBeClosed = new Closeable() {
        {
            System.out.println("create");
        }
        @Override
        public void close() {
            System.out.println("close");
        }
    };

    int n = 0 / 0;

    void close() {
        mustBeClosed.close();
    }
}

//code
try (A a = new A()) {
    //do something
}

如何保证mustBeClosed被释放?

当对象层次结构很复杂时,可能会发生这种情况。覆盖B的最终化可能不是一个完美的解决方案。

针对此问题的最佳做法或原则是什么?

修订后的版本如下:

class B {
    Closeable mustBeClosed;
    B() {
        try {

            mustBeClosed = ...

            //other initialization which might raise exceptions

        } catch (throwable t) {
            close();
            throw t;
        }
    }

    void close() {
        if (mustBeClosed != null) {
            try {
                mustBeClosed.close();
            } catch (Throwable t) {
            }
        }
        //all other resources that should be closed
    }
}

然而,这需要太多代码并且远非优雅。更重要的是,似乎所有权层次结构中的所有类都应遵循相同的样式,从而产生大量代码。

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

你的问题是,如果构造函数抛出异常,try-with-resources将不会(实际上不能)调用close()

任何分配资源的对象构造,并且在分配资源之后构建期间可能会失败,必须在异常级联之前释放该资源调用堆栈。

解决这个问题的两种方法:

1)确定确定资源分配是最后执行的操作。在您的情况下,这意味着在字段n之前向上移动字段mustBeClosed

2)处理构造函数中的资源构造,而不是字段初始化程序,因此您可以捕获任何后续异常并在重新抛出异常之前再次释放资源,如备用解决方案所示。
但是,您不必在close()方法中进行空值检查,因为如果对象构造成功,mustBeClosed将始终为非null,并且如果对象无法调用close()施工失败。

答案 1 :(得分:0)

使用包装器方法正常关闭所有Closeable个实例

closeGraceFully(Closeable c) { // call this guy for all instances of closeable
   try{
       c.close();
   } catch(IOException ex) {
      // nothing can be done here, frankly.
  }
}

然后调用此包装器方法。不要直接致电close()。不要使用finalizers他们是邪恶的,会减慢你的应用程序。