【问题标题】:Should I close a StringReader?我应该关闭 StringReader 吗?
【发布时间】:2011-09-01 14:17:54
【问题描述】:

我使用StringReader 将字符串转换为可以上传到 SFTP 服务器的内容(它需要一个流)。之后关闭StringReader 有什么意义吗?据我在源代码中看到的,它只是将字符串设置为null...

我可以这样做,但是由于 close 方法被标记为抛出 IOException 并且我必须将它包装在 try catch 中,并且代码最终看起来比它可能需要的要可怕得多.

【问题讨论】:

    标签: java stringreader


    【解决方案1】:

    如果您知道您正在处理一个您将丢弃的StringReader,我认为没有任何理由关闭它。我无法想象在关闭它之后您会持有对它的引用的任何原因,因此将字符串设置为 null 以进行垃圾收集并没有真正的好处。如果您正在创建一个采用 Reader 的方法,那么关闭它可能是有意义的,因为您不知道基础类型。

    【讨论】:

    • 是的,如果我不知道我在处理什么样的Reader,我肯定会关闭它。
    【解决方案2】:

    它的作用不止于此。如果我可以引用 JavaDoc:

    /**
     * Closes the stream and releases any system resources associated with
     * it. Once the stream has been closed, further read(),
     * ready(), mark(), or reset() invocations will throw an IOException.
     * Closing a previously closed stream has no effect.
     */
    

    所以是的,你应该关闭那个阅读器。不是为了资源,而是为了好的风格和可能跟随你的程序员。你不知道这个实例将被传递到哪里以及其他人会尝试用它做什么。有一天,您可能还会选择更改接口并接受任何 Reader 实现,在这种情况下,您可能会处理需要调用 close() 以释放资源的 Reader。

    所以,一旦你完成了这个实例,防止进一步(可能是错误的)使用它是一种很好的风格。而且由于它没有伤害,它只会防止将来可能出现的错误。

    编辑: 既然您说您的 close() 方法正在声明它可能会抛出的异常,我会说您 需要 调用 close() 因为 StringReader.close() 不会抛出异常。但是, Reader.close() 可以。因此,您已经允许 Reader 的其他实现,因此您必须关闭它,因为您不知道最终会获得哪些 Reader 实现。如果我们谈论的是永远不会离开该范围的三行代码,请声明您的变量 StringReader 并无论如何调用 close(在这种情况下不进行异常处理)。

    【讨论】:

    • 假设 StringReader 从 String 中读取,如果它持有的只是一个 String,它将释放哪些系统资源?
    • 正如我所说,对于 StringReader 而言,这不是资源问题。但是 Reader 的其他实现可能会有所不同。根据您的界面和未来可能的变化,它可能有一天会变得相关,即使它只是一个 StringReader,它仍然是很好的风格。
    • 我知道文档是这么说的,但是在查看源代码时,它只是将字符串设置为 null(这当然会使其他方法崩溃)
    • 也许我的编辑有帮助?看起来你刚刚用错误的类型声明了你的变量。 StringReader.close() 在我的 Java6u20 副本中没有声明异常。
    【解决方案3】:

    虽然严格来说没有必要,因为 StringReader 只保留一个字符串,作为一种良好的形式,无论如何关闭所有 Reader 总是一个好主意。今天,您的代码可能正在使用 StringReader,但如果您将其更改为另一个确实需要关闭的 Reader,则您的代码未关闭将是错误的,而您的关闭则没问题。

    【讨论】:

      【解决方案4】:

      如果您的变量 的类型为StringReader,而不是Reader,则您不需要捕获异常,因为StringReader#close() 不会引发异常:只有Reader#close() 会.因此,您可以使用try-with-resources 自动关闭阅读器,而无需使用样板来处理不会发生的异常。 Reader#close() throwing IOException 表示子类型可以抛出这种类型的异常,而不是它们必须。这是您要声明具有子类型而不是超类型的变量的罕见情况之一;更多信息请参见Use interface or type for variable definition in java?

      因此,我建议如下,它只需要一层嵌套,这对于资源来说是同等的:

      try (StringReader reader = new StringReader(string)) {
          // Do something with reader.
      }
      

      但是,关闭StringReader 没有什么价值,因为它不保存外部资源(例如,只有 Java 管理的内存,而不是文件句柄或本机内存),所以可以省略它,不过我建议发表评论说明为什么这是安全的,因为否则不关闭读者是令人惊讶的。正如您所注意到的,close() 只是将字段清空,每个 JDK 8 来源:StringReader.java:198。如果你想避免嵌套和关闭,你可以这样写:

      // Don't need to close StringReader, since no external resource.
      StringReader reader = new StringReader(string);
      // Do something with reader.
      

      ...或(使用更通用的变量类型):

      // Don't need to close StringReader, since no external resource.
      Reader reader = new StringReader(string);
      // Do something with reader.
      

      正常的 try-with-resources 在这里有效,因为 StringReader#close() 会覆盖 Reader#close() 并幸运地声明它不会抛出 IOException

      请注意 不是 StringWriter 的情况:StringWriter#close() 确实 声明它抛出 IOException,尽管它是不!这大概是为了向前兼容,所以它可能会在未来的实现中抛出异常,尽管这不太可能。见my answer Will not closing a stringwriter cause a leak?.

      在这种情况下(如果方法没有抛出异常,但接口声明它可以),编写这个的紧凑方法,你大概是在暗示,是:

      Reader reader = new StringReader(string);
      try {
          // Do something with reader, which may or may not throw IOException.
      } finally {
          try {
              reader.close();
          } catch (IOException e) {
              throw new AssertionError("StringReader#close() cannot throw IOException", e);
          }
      }
      

      这个级别的样板文件是必要的,因为您不能只在整个 try 块上添加一个 catch,否则您可能会不小心吞下代码主体抛出的 IOException。即使目前没有,将来也可能会添加一些,并且您希望编译器会警告您这一点。另请注意,记录当前行为的AssertionError 也将掩盖由 try 语句的主体引发的异常,尽管这绝不应该发生。如果这是替代方案,您显然最好省略 close() 并评论原因。

      这个答案取决于您自己创建StringReader 的事实;当然,如果您从其他地方收到Reader(例如,作为工厂的返回类型),那么您需要关闭它并处理可能的异常,因为您不知道它可能拥有什么资源,并且可能会抛出异常。

      【讨论】:

        【解决方案5】:

        如果您关闭流并释放与之关联的任何系统资源。关闭流后,进一步的 read()、ready()、mark() 或 reset() 调用将引发 IOException。关闭以前关闭的流没有效果。 指定者: 在接口Closeable中关闭 指定者: 关闭课堂阅读器

        【讨论】:

          猜你喜欢
          • 2013-08-05
          • 2023-01-06
          • 1970-01-01
          • 2018-07-02
          • 1970-01-01
          • 2019-07-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多