内联创建的 InputStream 是否被 GC 自动关闭?

Is inline created InputStream closed automatically by GC?

我已经找到了几个类似的问题,但我仍然找不到我的问题的答案。

我知道关闭外部流就足够了,它将关闭在线创建的内部流。

BufferedInputStream br = new BufferedInputStream(new FileInputStream(file));
br.close();

考虑下一个例子

Properties props = new Properties();
props.load(new FileInputStream(configPath));

是否应该将 FileInputStream 分配给变量然后显式关闭(或使用 try-with-resource 构造)或者 Java GC 会在 props.load() 方法后立即自动关闭它调用,因为没有对资源的引用?

Javadoc 指出

The specified stream remains open after this method returns.

所以是的,如果你想编写干净的代码,你应该自己关闭它。 GC 最终将关闭它,if 它开始调用流上的 finalize() 方法(如下所示),但是你 shouldn't rely on that.

始终关闭您的资源,这是唯一可以确定的方法。

/**
 * Ensures that the <code>close</code> method of this file input stream is
 * called when there are no more references to it.
 *
 * @exception  IOException  if an I/O error occurs.
 * @see        java.io.FileInputStream#close()
 */
protected void finalize() throws IOException {
    if ((fd != null) &&  (fd != FileDescriptor.in)) {
        /* if fd is shared, the references in FileDescriptor
         * will ensure that finalizer is only called when
         * safe to do so. All references using the fd have
         * become unreachable. We can call close()
         */
        close();
    }
}

必须强调的是,使用像

这样的代码
BufferedInputStream br = new BufferedInputStream(new FileInputStream(file));
// you likely insert actual operations on br here
br.close();

强烈建议不要这样做,因为如果流构造和 close() 调用之间的操作抛出异常,则不会发生关闭。

您应该改用“try with resource”结构:

try(BufferedInputStream br = new BufferedInputStream(new FileInputStream(file))) {
    // your actual operations on br here
}

这确保即使在try 主体中出现异常,也会调用close()。但是,此代码依赖于 BufferedInputStreamclose() 方法将调用 FileInputStreamclose() 方法这一已知事实,但这在构造BufferedInputStream 已经完成。如果 BufferedInputStream 的构造函数抛出异常,它的 close() 方法将不会被调用,因为没有对象可以调用 close() on.

一个真正安全的解决方案是

try(FileInputStream    fis = new FileInputStream(file);
    BufferedInputStream br = new BufferedInputStream(fis)) {
    // your actual operations on br here
}

即使 BufferedInputStream 的构造函数抛出异常,也会关闭 FileInputStream。这在这里看起来像是一个罕见的极端情况,因为在该构造函数中唯一可能出错的是缓冲区分配,它可能会抛出 OutOfMemoryError,无论如何在这种情况下你都会遇到很大的麻烦。

但是请考虑这样一个例子

try(FileInputStream   fis = new FileInputStream(file);
    ObjectInputStream ois = new ObjectInputStream(fis)) {
    // your actual operations on ois here
}

由于ObjectInputStream的构造函数已经读取了header,所以可能会抛出IOExceptions,而且header也可能无效,同样会引发异常。因此,还有更多可能出错的地方,确保底层 FileInputStream 即使在这些情况下也能正确关闭更为重要。