我正在审查某人的代码,并注意到
BufferedReader
有时没有被关闭。 通常,Eclipse 会发出警告,指出这是潜在的内存泄漏(我会修复它)。 但是,在 Callable
内部类中,没有警告。
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 完成的代码。
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
不太可能发生,因为流来自另一个进程。除非发生一些不可恢复的错误,否则这种情况不太可能发生。不过,以某种方式记录异常仍然不是一个坏主意。
编辑以解决您对混合答案的评论:
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
写入文件的委托。当然,这个逻辑可能更复杂,比如将一个对象变成 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
调用时它都会失败。最好始终在创建流的位置而不是传递流的位置关闭流。最初可能没问题,但稍后尝试更改该代码可能会导致错误。如果我一开始只用错误的方法调用了 1 个 writeLine
,而其他人想要添加第二个,他们就必须重构代码,以便 writeLine
无论如何都不会关闭流。过于快乐可能会导致日后头痛。
另请注意,从技术上讲,
BufferedWriter
不是系统资源的实际句柄,FileOutputStream
才是,因此无论如何您都应该关闭实际资源。
因此,经验法则:仅在创建流的地方关闭流,并且始终在 try/finally 块中进行创建和关闭(或 Java 7 很棒的 try/resource 块,它会为您完成关闭)。
在这种情况下,您可能不想关闭
BufferedReader
。传递给构造函数的 InputStream
是可能与系统资源关联的对象。 BufferedReader
和 InputStreamReader
只是它的包装。关闭 BufferedReader
也会关闭 InputStream
,这可能不是调用者想要的。
BuffereddReader close()
关闭此流并释放与相关的任何系统资源 它。如果流已经关闭,则调用此方法不会 效果。
所以,如果不close(),系统资源可能仍然与reader相关联,这可能会导致内存泄漏。
为什么 eclipse 不高亮:如果你忽略调用 close(),这不是编译时错误,所以 eclipse 不高亮。
不突出显示,这是真的,因为流可以在调用方法之外的其他地方关闭。
如果它在调用方法内关闭,则其他线程此时可能正在使用它。
使用
BufferdRreader
什么也没有,但是如果你失去了对流的引用并且无法关闭它,这会导致内存泄漏。无论您在哪里打开流,都应该在finally块中将其关闭,并且测试流是否为
null
也是一个很好的做法,因为如果文件不存在,流将是null
,异常将会被抛出(FileNotFoundException
),但最终完成了。 IE。调用方法的内容应该是:
BufferedReader stdOut = null;
String output = null;
try {
stdOut = new BufferedReader(new InputStreamReader(stream));
while ((output = stdOut.readLine()) != null) {
log.info("[" + prepend + "] " + output);
}
} catch (FileNotFoundException ex) {
log.warn("Unable to open nonexisten file " + whichOne);
} catch (IOException ex) {
log.warn("Unable to read from stream");
} finally {
if (stdOut != null) {
try {
stdOut.close();
} catch (IOException e) {
log.warn("Unable to close the stream");
}
}
}
return 0;
或者,如果您被允许使用 Java 7,您可以利用
AutoCloseable
接口和新语言结构的优势来实现此目的。
请参阅 http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html