在Java中,关闭父输入流是否也会关闭其子输入流?

9
FileInputStream fis = new FileInputStream(gzipFile);
GZIPInputStream gis = new GZIPInputStream(fis);
gis.close();
fis.close();

fis.close() 是否必要?虽然我运行这段代码似乎没有出现任何错误。

3个回答

9
你应该看到GZIPInputStream.close()的实现。
/**
 * Closes this input stream and releases any system resources associated
 * with the stream.
 * @exception IOException if an I/O error has occurred
 */
public void close() throws IOException {
    if (!closed) {
        super.close();  
        eos = true;
        closed = true;
    }
}

如果您查看GZIPInputStream的构造函数,它应该是这样的:
/**
 * Creates a new input stream with the specified buffer size.
 * @param in the input stream
 * @param size the input buffer size
 * @exception IOException if an I/O error has occurred
 * @exception IllegalArgumentException if size is <= 0
 */
public GZIPInputStream(InputStream in, int size) throws IOException {
super(in, new Inflater(true), size);
    usesDefaultInflater = true;
        readHeader(in);
}

请注意变量in。 注意它是如何传递给超类的,这里是InflaterInputStream

现在,如果我们看一下InflaterInputStream.close()方法的实现,我们会发现这个:

/**
 * Closes this input stream and releases any system resources associated
 * with the stream.
 * @exception IOException if an I/O error has occurred
 */
public void close() throws IOException {
    if (!closed) {
        if (usesDefaultInflater)
            inf.end();
    in.close();
        closed = true;
    }
}

很明显,正在调用in.close()。因此,在调用GZIPInputStream.close()时,包装的(装饰的)FileInputStream也会关闭。这使得调用fis.close()成为多余的。


1
super 不是 FileInputStream,而是 GZIPInputStream 超类的 close 方法。这与构造函数的输入参数(直接)无关。 - T.J. Crowder
我在您提供的源代码中没有看到它,但是如果您查看super.close()的实现,您会看到一行代码in.close();,其中in是传递给构造函数的输入流(在OP的情况下是fis)。 - Thomas
实际上,这似乎是必要的。调用 super.close() 并不意味着关闭通过参数传递的流(事实上,您可以确定 super.close() 不会这样做,因为它没有访问该属性)。 - SJuan76
是的,我同意super不是FileInputStream这一事实。但我会让答案更清晰。包装流是间接关闭的。 - adarshr
所有这些对实现的调查(可以随时更改)都是由于GZIPInputStreamInflaterInputStream中的文档不良引起的。叹气 - T.J. Crowder
1
非常感谢!我同意@T.J.Crowder的观点,文档质量很差——因为我甚至试图去查看它。 - OkonX

2
这是人们需要清晰记录的事情之一。不幸的是,GZIPInputStream 覆盖了其父类中的 close 方法,并没有对其进行文档说明(文档质量较差)。但是,很有可能(即使不看代码)它最终会调用 super.close()(实际上从 adarshr 的回答中我们可以看到它确实这样做了,尽管您不应该假设实现不会改变)。如果是这样的话,那么我们就要查看父类(InflaterInputStream)的文档。不幸的是,它也完全相同地覆盖了方法而没有文档说明。但是请假设它也在某个时候调用了 super.close()。查看其父类(FilterInputStream)的文档,它明确说明in 成员上执行了 close 操作,该成员是通过构造函数设置的。(另一个假设是,GZIPInputStreamInflaterInputStream 将构造函数参数传递给它们的超类,但这非常有可能。)
“FilterInputStream”清楚地告诉您它将关闭您在构造函数中提供的流。尽管其他文档不太好,但很有可能它们会调用“super.close()”,所以是的,它应该为您关闭它,您不需要自己这样做。但是这里涉及到一些假设。”

2
是的,它确实会这样做。javadoc 上说:
关闭此输入流并释放与流相关的任何系统资源。
而被包装的流绝对是这样的一个系统资源。
此外,GZIPInputStream 是一个 FilterInputStream,而 FilterInputStream 的 javadoc 上说:
关闭此输入流并释放与流相关的任何系统资源。此方法只是执行 in.close()。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接