一尘不染

没有在callable.call中关闭的BufferedReader会发生什么?

java

我有三个问题。

为了解释,我正在查看某人的代码,并注意到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开发人员,所以我首先想到的是它是故意的…但是可能是他们在编写代码时只是匆忙而忽略了它。

我的问题是:

  1. Eclipse为什么不突出显示这一点(可以通过以下问题的答案来回答)?

  2. 如果在call()方法中将其关闭,可能发生的最坏情况是什么?(我想不出一个很好的理由…并且我已经搜索了一段时间…但是也许是有意不关闭BufferedReader的原因)

  3. 如果在内部类中 关闭BufferedReader,可能发生的最坏情况是什么?


阅读 260

收藏
2020-12-03

共1个答案

一尘不染

我要说的是,由于他们正在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文件并将其实际写入文件的委托。当然,这种逻辑可能更复杂,例如将一个对象变成a
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始终无法关闭流。变得近距离开心可能会引起头痛。

还要注意,从技术上讲,BufferedWriteris不是系统资源的实际句柄,FileOutputStream而是is,因此无论如何,您都应该关闭实际资源。

因此,经验法则:仅在创建流的地方关闭流,并始终在try / finally块(或Java 7很棒的try /
resource块
,为您关闭)中进行创建和关闭。

2020-12-03