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



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

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
}

如何保证mustclosed被释放?

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

对于这个问题有什么最好的实践或原则吗?

修改后的版本如下:

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
    }
}
然而,这需要太多的代码,而且远远不够优雅。更重要的是,似乎所有权层次结构中的所有类都应该遵循相同的样式,这导致了大量的代码。

任何建议吗?

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

任何分配资源的对象构造,在资源分配后,在构造过程中都有可能失败,必须在异常级联到调用堆栈之前释放该资源。

有两种解决方法:

1) Make sure资源分配是最后执行的操作。在您的示例中,这意味着将字段n移到字段mustBeClosed之前。

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

使用包装器方法优雅地关闭所有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,他们是邪恶的,会减慢你的应用程序。

相关内容

  • 没有找到相关文章

最新更新