OutputStream 对象(作为方法调用中的表达式创建,但未分配给变量)是否仍应关闭?

Should an OutputStream object (created as an expression in method call, but not assigned to a variable) still be closed?

我正在尝试匿名使用 FileOutputStream 来使用 java.util.Property 的存储方法存储 属性。

Properties p = new Properties();
. . .
p.store(new FileOutputStream("nameOfFile"), "Created by me.");

阅读 store 方法的 javadoc,它说在方法 returns 之后流将被刷新并保持打开状态。

我想知道流发生了什么。因为我已经匿名实例化它,所以我假设该对象将立即被垃圾收集(如果那是错误的,请纠正我)。但是,我被教导要在完成时关闭我的流。在这种情况下,我无法关闭流,因为我没有对象引用它。我是否需要担心流在这里保持打开状态,或者 Java 在这种情况下通过立即收集对象来适当地处理它?

那不是匿名对象(或者更确切地说是匿名 class)。这只是一个未分配的对象实例化。

创建它并将其传递给 store 后,就会有一个对它的引用,因此它不会被 GC。一旦 store returns 该引用消失,但您仍应跟踪变量并在 store returns 之后处理它。以相同的标准方式处理所有资源并在完成后调用 close 更安全,这有助于避免某些 finalize 方法调用 [=14= 时出现大量资源泄漏错误] ad other's don’t,并且还避免了由于非确定性 GC 引起的问题。

您还不知道未来版本的终结器实现可能会发生什么变化,因此始终调用 close 是最佳做法。

try-with-resources 块 (Java Tutorials > The try-with-resources statement) 非常适合:

Properties p = new Properties();

try (FileOutputStream stm = new FileOutputStream("nameOfFile"))
{
    p.store(stm, "Created by me.");     
} catch (IOException e) {
    // TODO: handle this
}

当与任何(可能是多个)Autocloseable 一起使用时,该块将在最后调用 close 并很好地处理诸如 close 上的异常或正文中的异常之类的事情在 close 上也是如此。如果正文和 close 都抛出异常,则重新抛出正文异常并且 close 异常可通过 e.getSuppressed().

获得

如果您使用这些块之一,则不应自己调用 Autocloseable.close(),因为该方法不是幂等的 - 调用它两次可能会在第二次产生副作用。如果你有一个 Closeable 那么你可以安全地多次调用它。来自文档:

Note that unlike the close method of Closeable, this close method is not required to be idempotent

在 Java 6 或更少版本中,您必须执行自己的异常处理语句,这很难,因为您必须处理您的主体和 close 都抛出异常的情况。

垃圾收集一般不包括打开的文件或流等资源,除非finalize方法显式关闭对象的底层资源。

检查 finalizeFileOutputStream 代码,似乎可以确保调用 close

/**
 * Cleans up the connection to the file, and ensures that the
 * <code>close</code> method of this file output stream is
 * called when there are no more references to this stream.
 *
 * @exception  IOException  if an I/O error occurs.
 * @see        java.io.FileInputStream#close()
 */
protected void finalize() throws IOException {
    if (fd != null) {
        if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
            flush();
        } else {

            /*
             * Finalizer should not release the FileDescriptor if another
             * stream is still using it. If the user directly invokes
             * close() then the FileDescriptor is also released.
             */
            runningFinalize.set(Boolean.TRUE);
            try {
                close();
            } finally {
                runningFinalize.set(Boolean.FALSE);
            }
        }
    }
}

但是,始终 手动关闭资源或在 try-with-resources 语句中总是更好,因为并非所有流对象都可能在取消引用时关闭,即使他们完成了,也不能保证按照预期发生:

try(FileOutputStream outStream = new FileOutputStream("nameOfFile")) {
     Properties p = new Properties();
     . . .
     p.store(outStream, "Created by me.");
}

创建对象...

FileOutputStream fos = new FileOutputStream("nameOfFile")

然后使用它...

p.store(fos, "Created by me.");

最后关闭它。

Properties.store() 命令不保留对流的引用,因此没有对它的引用,因此它将在方法 returns 之后进行 GC,因为这就是范围流范围。