【问题标题】:Is there a clean way to give Java objects scope with destruction?有没有一种干净的方法可以给 Java 对象范围提供破坏?
【发布时间】:2012-11-01 10:13:47
【问题描述】:

想象一下 MyOpenedFile 是用打开的流包装文件的东西。然后假设这段代码:

// method in an Util class
static void safeClose(MyOpenedFile f) {
  if (f != null) {
    try {
      f.close();
    } catch(IOException ex) { /* add logging or console output */ }
  }
}

问题的实际方法:

void doSomeFileOperation(...) throws IOException, ... {
    MyOpenedFile f1 = null;
    MyOpenedFile f2 = null;
    try {

      /* method's "business logic" code beings */
      f1 = new MyOpenedFile(...);
      //do stuff
      f2 = new MyOpenedFile(...);
      // do stuff
      f1.close(); f1 = null;
      // do stuff with f1 closed
      f2.close(); f2 = null;
      // do stuff with f2 closed
      /* method's "business logic" code ends */

    } finally {
      Util.safeClose(f1); f1 = null;
      Util.safeClose(f2); f2 = null; 
    }
}

现在这很混乱,特别容易出错(例如,finally 块中的某些代码可能很难在单元测试中调用)。例如,在 C++ 中,析构函数会负责清理(由作用域指针析构函数调用或直接调用),代码会更干净。

那么,有没有更好/更好/更简洁的方法来包装上面的业务逻辑代码,以便传播任何异常但 f1f2 两个文件都被关闭(或者至少在两者上都尝试关闭,即使失败)?

还有指向任何开源库(例如 Apache Commons)的答案,欢迎提供漂亮的包装器。

【问题讨论】:

  • 顺便说一句,您的代码示例中有一个错误,实际上会导致您泄漏文件句柄,因为您在关闭它之前设置了 f1=null。
  • @Chii 不,据我所知,它没有。有closesafeClose 之前 所有null 分配。
  • 为什么每个文件都调用close两次(在try和finally中)?
  • @home 我没有。我只想要一个虚拟业务逻辑来描述一个关闭文件并将引用设置为 null 的案例。如果 finally 块中的引用为空,则不对其执行任何操作。
  • @hyde:啊,我看错了你的样本。这很有趣,因为它很容易误读使用这种带有内务管理的穿插逻辑风格编写的代码。但是,您不能重写它,因为要求 f1 关闭而 f2 保持打开状态(否则,您可以重写示例,使逻辑被传入,而内务管理则松散在别处

标签: java exception file-io raii resource-cleanup


【解决方案1】:

文件是字符串的包装器,其中包含可能存在或不存在的文件名。它是无状态的,所以你不需要关闭它。

您需要关闭的资源是 FileInputStream 或 BufferedReader,您可以在 Java 7 中使用 ARM 隐式关闭它们

try(BufferedReader br = new BufferedReader(new FileReader(file))) {

}

这将在他退出时关闭br

http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html

【讨论】:

  • 我选择接受你的回答,因为我最中肯,并且有 IMO 好的代码示例(有时小就是漂亮),谢谢。现在剩下的就是,我需要确定我是否可以将 Java7 用于实际的事情......:-/
【解决方案2】:

看看The try-with-resources Statement,它将在try-block结束后关闭资源。

您使用的File 类似乎不是java.io.File,因为它没有任何close() 方法。在这种情况下,请确保您自己的 File 类实现 Closeable 以使其与 ARM 一起使用。

try (FileInputStream f1 = new FileInputStream("test1.txt");
     FileInputStream f2 = new FileInputStream("test2.txt")) {
    // Some code
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

【讨论】:

  • 感谢指出File 的事情,我更改了类名并添加了注释来解释自定义类名背后的逻辑。
  • 一个吹毛求疵,更像是一般的咆哮而不是针对你:我认为应该很少使用那种 catch 块。捕获异常、记录异常并继续进行几乎总是一个坏主意,就好像什么都没发生一样。如果可以安全地忽略异常,那么记录它就是混乱的。如果需要采取行动,那么它应该不仅仅是记录它(例如,在 GUI 应用程序中弹出一个对话框)。大多数情况下,应该允许异常向上传播,有时作为另一个更相关异常的原因。当程序员不确定该做什么时,仅记录日志很有用。
【解决方案3】:

您不需要像这里提到的那样关闭文件(文件系统上文件的表示形式):

Do I need to close files I perform File.getName() on?

我假设您正在询问有关文件流/阅读器的更多信息?

在这种情况下,java 7 有一个不错的新特性: http://www.vineetmanohar.com/2011/03/java-7-try-with-auto-closable-resources/

如果您正在使用旧版本的 java,我会保持简单:

void doSomeFileOperation(...) throws IOException, ... {
  FileInputStream f1 = null;
  FileInputStream f2 = null;
  try {

    // do stuff

  } finally {
    Util.safeClose(f1); 
    Util.safeClose(f2); 
  }
}

【讨论】:

    【解决方案4】:

    一个自动出现在我脑海中的选项是:将处理文件的代码与进行任何处理的代码分开。通过这种方式,您可以封装处理打开、关闭和异常处理的讨厌代码。

    另外一点是,你的样本做了很多额外的、不需要的步骤

    void doSomeFileOperation(...) throws IOException, ... {
        File f1 = null;
        File f2 = null;
        try {
          f1 = new File(...);
          f2 = new File(...);
          // callback to another class / method that does the real work
        } finally {
          Util.safeClose(f1);
          Util.safeClose(f2);
        }
    }
    

    您不需要将 File 实例设置为 null。如果你尝试使用它们,你会得到一个例外。

    我不知道您使用的是什么 File 对象。 java中的标准File类没有close()方法。

    【讨论】:

    • 我最初也是这么想的,但缺陷是不再保留// do stuff with f1 closed 的要求。您需要传入两个不同的回调,因此这并不能真正解决问题。
    • 是的,根据具体的业务代码,可以优化清理。但这会导致针对不同情况的不同样板代码,这可能会更糟。
    猜你喜欢
    • 2022-08-11
    • 2014-11-06
    • 1970-01-01
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    • 2018-06-30
    • 2021-02-18
    • 2015-03-08
    相关资源
    最近更新 更多