我有三个问题。
为了解释,我正在查看某人的代码,并注意到BufferedReader有时没有关闭。通常,Eclipse会警告您这是潜在的内存泄漏(我已修复)。但是,在Callable内部类中,没有警告。
BufferedReader
class outerClass { ... public void someMethod() { Future<Integer> future = outputThreadPool.submit(new innerClass(this.myProcess.getInputStream(), threadName)); ... } class innerClass implements Callable<Integer> { private final InputStream stream; private final String prepend; innerClass(InputStream stream, String prepend) { this.stream = stream; this.prepend = prepend; } @Override public Integer call() { BufferedReader stdOut = new BufferedReader(new InputStreamReader(stream)); String output = null; try { while ((output = stdOut.readLine()) != null) { log.info("[" + prepend + "] " + output); } } catch (IOException ignore) { // I have no idea why we're ignoring this... :-| } return 0; } } }
编写代码的人都是经验丰富的Java开发人员,所以我首先想到的是它是故意的…但是可能是他们在编写代码时只是匆忙而忽略了它。
我的问题是:
Eclipse为什么不突出显示这一点(可以通过以下问题的答案来回答)?
如果在call()方法中将其关闭,可能发生的最坏情况是什么?(我想不出一个很好的理由…并且我已经搜索了一段时间…但是也许是有意不关闭BufferedReader的原因)
如果在内部类中 未 关闭BufferedReader,可能发生的最坏情况是什么?
我要说的是,由于他们正在BufferedReader围绕给定对象创建InputStream代码,因此可以安全地不调用该代码close()。调用的代码close()应该始终是创建流并使用try / finally完成的代码。
InputStream
close()
public static void read(String str) throws IOException { FileInputStream stream = null try { stream = new FileInputStream(str); readStreamToConsole(stream); } finally { if (stream != null) stream.close(); } } private static void readStreamToConsole(InputStream stream) { BufferedReader stdOut = new BufferedReader(new InputStreamReader(stream)); String output = null; while ((output = stdOut.readLine()) != null) System.out.println(output); }
另一个注意事项:您的代码似乎正在记录其他进程的输出。无论如何,您可能无法关闭流。如果不自己测试,我不确定如果关闭另一个进程的流会发生什么。
哦,这IOException不太可能发生,因为流来自另一个进程。除非发生一些不可恢复的错误,否则这不太可能发生。但是,以某种方式记录异常仍然不是一个坏主意。
IOException
编辑以解决您对混合答案的评论:
让我们使用输出流,BufferedWriter这次以一个示例为例:
BufferedWriter
private static final String NEWLINE = System.getProperty("line.separator"); public static void main(String[] args) throws IOException { String file = "foo/bar.txt"; FileOutputStream stream = null; try { stream = new FileOutputStream(file); writeLine(stream, "Line 1"); writeLine(stream, "Line 2"); } finally { if (stream != null) stream.close(); } } private static void writeLine(OutputStream stream, String line) throws IOException { BufferedWriter writer = new BufferedWriter(new InputStreamWriter(stream)); writer.write(line + NEWLINE); }
这可行。writeLine方法用作创建writer单个line文件并将其实际写入文件的委托。当然,这种逻辑可能更复杂,例如将一个对象变成a String并将其写入。这也使该main方法更易于阅读。
writer
line
String
main
现在,如果相反,我们关闭了BufferedWriter呢?
private static void writeLine(OutputStream stream, String line) throws IOException { BufferedWriter writer = null; try { writer = new BufferedWriter(new InputStreamWriter(stream)); writer.write(line + NEWLINE); } finally { if (writer != null) writer.close(); } }
尝试以此运行它,它将在每次第二次writeLine调用时失败。最好始终在创建流的地方而不是在传递流的地方关闭流。最初可能没问题,但是随后尝试更改该代码可能会导致错误。如果我只writeLine使用坏方法进行了一次调用,而其他人想添加第二个调用,那么他们将不得不重构代码,以致writeLine始终无法关闭流。变得近距离开心可能会引起头痛。
writeLine
还要注意,从技术上讲,BufferedWriteris不是系统资源的实际句柄,FileOutputStream而是is,因此无论如何,您都应该关闭实际资源。
FileOutputStream
因此,经验法则:仅在创建流的地方关闭流,并始终在try / finally块(或Java 7很棒的try / resource块,为您关闭)中进行创建和关闭。