【问题标题】:How to properly close input streams?如何正确关闭输入流?
【发布时间】:2021-06-26 10:19:04
【问题描述】:

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

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);
    }

哪种方法更正确?

【问题讨论】:

    标签: java try-catch inputstream bufferedreader inputstreamreader


    【解决方案1】:

    第二个永远不正确。 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 个选项中,您会陷入一些争论;第一个选项看起来不错;这些所谓的“过滤流”的实现(那些是“包装”另一个流的读取器/写入器/输出流/输入流)的处理是close()ing 他们将关闭他们包装的东西。因此,通常#1 看起来不错,但如果在过滤器流的构造函数中发生异常,那么您就会泄漏资源。会不会出现这些异常?通常不可能,但并非总是,这是导致常用过滤器流在构造中崩溃的简单方法:

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

    因此,我强烈建议不要使用第一个。剩下 2 号门和 3 号门:真的没关系;我认为第二个可能是最易读的,但第二个选项的问题是各种 IDE 和 linting 工具会抱怨它,他们很难区分资源表示流和过滤器/内存之间的区别流式样。这不是他们的错,真的:他们怎么可能知道您的 execPipelineAndGetInputStream 方法返回的 InputStream 是否应该是“您需要关闭的东西”或“您可以关闭但没关系的东西”或“您应该关闭的东西”根本不关门吗?

    【讨论】:

      【解决方案2】:

      第一个更正确,因为第二个在抛出异常时不会关闭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();
          }
      

      【讨论】:

      • “input”和“reader”在 finally 块中不可用
      • 您在这里对“更正确”有一个奇怪的定义。为什么你觉得关闭 ISR 和 BR 很“重要”?未能关闭这两件事不会泄漏任何资源。您可以使用相同的心态说在退出方法之前将 null 显式分配给所有局部变量是“更正确的”。换句话说,要么这个答案不正确,要么你对“正确”的定义都需要解释,因为它不是一个普遍持有的信念,并且这是一个错误的定义,因为它导致难以维护代码。
      猜你喜欢
      • 1970-01-01
      • 2017-03-29
      • 2012-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-27
      • 2021-07-28
      • 1970-01-01
      相关资源
      最近更新 更多