我需要关闭Reader后还需要关闭InputStream吗?

54

我想知道,在我关闭Reader之后,是否需要关闭InputStream?

try {
    inputStream = new java.io.FileInputStream(file);
    reader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
} catch (Exception exp) {
    log.error(null, exp);
} finally {
    if (false == close(reader)) {
        return null;
    }
    // Do I need to close inputStream as well?
    if (false == close(inputStream)) {
        return null;
    }
}

25
请使用 if (!close(reader) 而不是 if (false == close(reader)) - Jacob Tomaw
5
@Jacob Tomaw - 为什么?!我视力很差。“false”比“!”占据更多的屏幕空间。这至少会让我的阅读体验更好。 - Cheok Yan Cheng
10
@Yan Cheng CHEOK 这种写法很不规范,而且让下一个读你代码的人阅读起来更加困难。如果你看不清感叹号,你应该要么调大电脑字体大小,要么换强度更高的眼镜,而不是创造自己的编码约定。 - bwawok
2
@bwawok 或者将 if (!check) { thing(); return; } otherthing(); 重排为 if (check) { otherthing() } else { thing(); } - Tom Hawtin - tackline
9
哦,finally 中的 return?啊啊啊啊啊啊啊啊啊! - Tom Hawtin - tackline
显示剩余6条评论
5个回答

50
不需要,Java中用于流的装饰器方法可以通过将它们附加到其他流或读取器来构建新的流或读取器,这将自动由InputStreamReader实现处理。如果您查看它的源代码InputStreamReader.java,您会发现:
private final StreamDecoder sd;

public InputStreamReader(InputStream in) {
  ...
  sd = StreamDecoder.forInputStreamReader(in, this, (String)null);
  ...
}

public void close() throws IOException {
  sd.close();
}

因此,关闭操作实际上关闭了流阅读器下的InputStream

编辑:我想确保StreamDecoder的关闭也适用于输入流,请继续关注。

已检查,在StreamDecoder.java中进行。

void implClose() throws IOException {
  if (ch != null)
    ch.close();
  else
    in.close();
}

当调用sd的close方法时会调用该函数。


每个具体的StreamDecoder代码也会在调用sd.close()时关闭底层输入流;-) - helios
1
如果我关闭inputStream会有任何损害吗? - Cheok Yan Cheng
2
你在文档中有没有明确写到这个?我不确定它是否是所有Java实现的限制。@Yan Cheng CHEOK:如果在关闭阅读器之后关闭输入流,不会有任何影响。 - Vivien Barousse
但它没有在finally中关闭它,对吧?所以在异常情况下可能无法关闭它?直接查看源代码是很危险的,你应该只查看文档。 - Sanjay Manohar
2
@Viven Barousse:“关闭流并释放与其关联的任何系统资源。”-- java.io.Reader JavaDoc for close() - Powerlord
真实的(除了错误),但请考虑下面的回答https://dev59.com/rXA65IYBdhLWcg3wyh57#3629116。 - Mr_and_Mrs_D

11

从技术上讲,关闭Reader将关闭InputStream。但是,如果在打开InputStream和创建Reader之间出现故障,您仍应该关闭InputStream。如果您关闭了InputStream[资源],没有充分的理由关闭Reader[装饰器]。同时,还有一些常见的bug,关闭装饰器可能会在关闭被修饰的资源之前抛出异常。因此:

Resource resource = acquire();
try {
    Decorator decorated = decorate(resource);
    use(decorated);
} finally {
    resource.release();
}

需要注意一些复杂情况。由于实现方式,某些装饰器可能实际上包含原生资源。输出装饰器通常需要被刷新,但只在正常情况下(即在try块中而不是finally块中)。


2
如果“Decorator”是例如“BufferedWriter”,则此方法会失败。没有明确的文档说明程序员在使用后应该显式调用“flush()”。 - BalusC
@BalusC 我有点困惑。BufferedWriter API 文档的第一行说它会缓冲字符。 - Tom Hawtin - tackline
BufferedWriter(装饰过的)在 close() 时会自动执行 flush() 操作,而例如 FileWriter(资源)则不会。因此,如果你仅关闭 FileWriter 而没有手动执行 flush() 操作,则可能存在未刷新的字符。 - BalusC
@BalusC 正如我在答案中所说,输出修饰符通常需要被刷新。它们是一种比较不寻常的资源。FileWriter 是一场灾难。 - Tom Hawtin - tackline
你能否详细解释一下你回答中的“装饰器由于其实现方式可能包含本地资源”(也可以将“复杂性”部分格式化为列表)?非常有用,+1。 - Mr_and_Mrs_D
@Mr_and_Mrs_D 对于OpenJDK,一些与压缩有关的流使用在C堆上分配缓冲区的本地代码,需要显式释放。对于这些类型的流,仅关闭底层流在技术上是不足够的(尽管最终器应该会在适当的时候释放内存)。讨厌的是,这种行为并没有得到记录。 - Tom Hawtin - tackline

5
如果你使用close()关闭读取器,则不需要关闭流。

关闭流并释放与之相关的所有系统资源。一旦流被关闭,进一步调用read()、ready()、mark()、reset()或skip()将抛出IOException异常。关闭以前已经关闭的流没有任何效果。


3

不需要,读者将关闭底层的InputStream


0
根据源代码嗅探,读取器会关闭其底层输入流。根据Javadoc,当调用reader.close()时,InputStreamReader似乎会“关闭流”。
我不确定是否任何读取器都必须在执行reader.close()时关闭其来源。我认为这很重要,这样您的代码可以使用任何具体类型的读取器。
无论如何,强制执行这一点是有意义的。

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