如何正确关闭输入流?

How to properly close input streams?

有两种方法可以做同样的事情。第一个:

public String getIpByName(String name) {
       var builders = NetworkUtil.buildProcess(name);
    try (var ip = new BufferedReader(new InputStreamReader(executor.execPipelineAndGetInputStream(builders)))) {
        return ip.lines().collect(Collectors.joining());
    } catch (IOException exception) {
        throw new NotFoundException(name);
    }
}

第二个:

public String getIpByName(String name) {
    var builders = NetworkUtil.buildProcess(name);
    try (var result = executor.execPipelineAndGetInputStream(builders)) {
        var input = new InputStreamReader(result);
        var reader = new BufferedReader(input);
        var ip = reader.lines().collect(Collectors.joining());
        input.close();
        reader.close();
        return ip;
    } catch (IOException exception) {
        throw new NotFoundException(name);
    }

哪种方法更正确?

第一个更正确,因为第二个在抛出异常时不会关闭 InputStreamReaderBufferedReader,而第一个示例使用 try-with-resources 语句 (https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html).

为了使第一个示例正确,您应该将关闭资源的行移动到 finally 子句:

try (var result = executor.execPipelineAndGetInputStream(builders)) {
        var input = new InputStreamReader(result);
        var reader = new BufferedReader(input);
        var ip = reader.lines().collect(Collectors.joining());
        return ip;
    } catch (IOException exception) {
        throw new NotFoundException(name);
    } finally {
        input.close();
        reader.close();
    }

第二个永远不正确。 close() 调用要么重要,要么不重要。如果它们很重要,它们应该是 try/finally-ied 或 try-with-resourced。不重要那就不重要,你就不用费心写语句了。

因此,我们有 3 个备选方案,而不是 2 个,只有您的第一个备选方案保持不变:

第二个:

public String getIpByName(String name) {
    var builders = NetworkUtil.buildProcess(name);
    try (var result = executor.execPipelineAndGetInputStream(builders)) {
        var input = new InputStreamReader(result);
        var reader = new BufferedReader(input);
        return reader.lines().collect(Collectors.joining());
    } catch (IOException exception) {
        throw new NotFoundException(name);
    }
}

还有第三个,使用 try-with-resources 的资源链功能:

public String getIpByName(String name) {
    var builders = NetworkUtil.buildProcess(name);
    try (var result = executor.execPipelineAndGetInputStream(builders);
      var input = new InputStreamReader(result);
      var reader = new BufferedReader(input)) {

        return reader.lines().collect(Collectors.joining());
    } catch (IOException exception) {
        throw new NotFoundException(name);
    }
}

在这 3 个选项中,您会陷入争论;第一个选项似乎不错;这些所谓的 'filter streams' 的实现(那些是 readers/writers/outputstreams/inputstreams 那个 'wrap' 另一个流)有一个协议,即 close()ing 它们将关闭它们包装的东西。因此,通常 #1 看起来很好,但是如果在过滤器流的构造函数中发生异常,那么您将泄漏资源。这些异常会发生吗?通常不可能,但并非总是如此,这里有一个导致常用过滤器流在构造中崩溃的简单方法:

new InputStreamReader(someData, "some non existing charset");

因此,我强烈反对第一种。剩下 2 号门和 3 号门:真的没关系;我认为第二个可能是最可读的,但是第二个选项的问题是各种 IDE 和 linting 工具会抱怨它,他们很难区分资源代表流和 filters/memory-only 喜欢流媒体。这不是他们的错,真的:他们怎么可能知道您的 execPipelineAndGetInputStream 方法返回的 InputStream 应该是 'thing you need to close' 或 'thing you can close but it doesnt matter' 还是 'thing you should not be closing at all'?