我有三个问题。
为了解释,我正在审查某人的代码,并注意到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在内部类中 不 关闭,可能会发生什么?
答案 0 :(得分:6)
我想说,因为他们围绕给定的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 block中创建和关闭,它为你做结束)。
答案 1 :(得分:2)
在这种情况下,您可能不想关闭BufferedReader
。传递给构造函数的InputStream
是可能与系统资源相关联的对象。 BufferedReader
和InputStreamReader
只是围绕它的包装。关闭BufferedReader
也会关闭InputStream
,这可能不是调用者想要的。
答案 2 :(得分:1)
BuffereddReader close()
关闭此流并释放与之关联的所有系统资源 它。如果流已经关闭,则调用此方法没有 效果。
因此,如果您不关闭(),系统资源仍可能与阅读器关联,这可能会导致内存泄漏。
为什么eclipse没有突出显示:如果忽略调用close(),则不是编译时错误,所以eclipse不要突出显示。
答案 3 :(得分:1)
无论你在哪里打开流,你都应该在finally块中关闭它,同时测试null
的流也是一个好习惯,因为如果文件不存在,那么流将是{{ 1}},将抛出异常(null
),但最终的bock已完成。即调用方法的内容应该是:
FileNotFoundException
或者,如果您被允许使用Java 7,您可以使用 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;
接口和新语言结构的优势来实现此目的。
见http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html
答案 4 :(得分:1)
不突出显示它是真的,因为可以在调用方法之外的某个地方关闭流。
如果它在调用方法中已关闭,则其他线程此刻可能正在使用它。
BufferdRreader
没有任何内容,但如果您放弃了对流的引用而无法关闭它,这会导致内存泄漏。