【问题标题】:Does Java support RAII/deterministic destruction?Java 是否支持 RAII/确定性破坏?
【发布时间】:2010-10-03 09:45:43
【问题描述】:

我使用 Java 已经至少 5 年了,那时,任何时候你想分配一个需要清理的对象(例如套接字、数据库句柄),你必须记得添加一个 finally 块并在那里调用清理方法。

相比之下,在 C++(或其他对象生命周期是确定性的语言,例如 Perl)中,类实现者将定义一个析构函数,该函数在该类的对象超出范围时执行清理。这种方法的优点是对象的用户不会忘记清理它——析构函数会自动调用,即使抛出异常也是如此。这种方法采用了相当糟糕的 RAII 名称——“资源获取即初始化”。

根据我的经验,“以 RAII 方式”做事为我节省了很多精神开销,因为我不必担心是否以及何时会发生资源释放。我们正在考虑将 Java 用于一个中型项目,我想知道自从我上次查看 Java 以来添加到该语言的许多新特性中是否存在某种确定性破坏。 (我希望我的抱怨“Java 没有 RAII”被斥责on this thread,但到目前为止,我无法通过谷歌搜索找到任何细节。)

因此,如果有人可以向我指出一些关于如何在 Java 中进行此操作的介绍性材料,那就太好了!

【问题讨论】:

  • @Raedwald:虽然我的问题实际上与您所链接的问题本质上是重复的,但我注意到对该问题的最高投票和接受的答案声称,因为 Java 使用 GC,它 不能具有确定性破坏。但是在这里 Jon Skeet 的回答下的 cmets 表明这两件事并不矛盾,我认为这很有趣和有用。 (当然,这可能只是我的自私自利 ;-)

标签: java raii


【解决方案1】:

编辑:下面的答案是在 2009 年初编写的,当时 Java 7 还在不断变化中。

虽然 Java 仍然不提供有关完成时间的保证,但它确实获得了类似于 C# 的 using 语句的功能:try-with-resources statement


不,Java 在这方面根本没有改变。您仍然需要使用 try/finally。

已经讨论过将相当于 C# 的“using”语句(它是 try/finally 上的语法糖)添加到 Java 中,但我认为这将不再是 Java 7 的一部分。 (大部分语言改进似乎已被放弃。)

值得理解的是,确定性销毁没有在 Java 和 .NET 中以引用计数垃圾收集器的形式实现是有原因的 - 顺便说一下 - a) 影响性能和 b) 由于循环引用而失败. Brian Harry 写了一个 detailed email 关于这个 - 它是关于 .NET 的,它相当古老,但值得仔细阅读。

【讨论】:

  • 谢谢乔恩。有趣的是,支持某些确定性类/对象将需要确定性“感染”一切。在任何情况下,对我来说重要的是“作用域本地”的情况——“使用”是朝着正确方向迈出的一步,因为它强制对析构函数进行一致的命名。
  • D 设法在 GC 上下文中进行确定性破坏 - 因此可以完成
  • @Earwicker:C# 以何种方式具有确定性破坏?
  • @JohnMcFarlane:我会进行编辑,尽管要完全准确地做到这一点很棘手,因为语言所保证的内容与特定的 VM 实现之间存在差异。
【解决方案2】:

这里有一个有用的模式。它不如基于析构函数的 RAII 好,但它确实意味着可以将资源清理移动到库中(所以你不能忘记调用它)。

它叫Execute Around, and has been discussed here before

有趣的是,我看到 Jon Skeet 在该线程上插话,但他没有在这里提及 - 真遗憾 Jon - 错过了在那里获得一些代表点的机会!

顺便说一句,虽然我很高兴 Brian Harry(再次参见 Jon 的评论)竭尽全力写了他所做的电子邮件 - 显然它确实反映了这个过程中的很多想法 - 而我很高兴我们确实在 C# 中“使用”了它——我不同意他的所有结论。特别是,我不明白为什么,如果我们可以使用,我们就无法在没有“使用”的情况下将类型标记为行为方式。当然,它限制了使用——但“使用”也是如此——而且大多数时候它正是你想要的。 “使用”的麻烦在于客户端代码仍然必须记住使用它。对于 C++ 风格的 RAII,它是类型的属性。 “使用”的一个可以说是更大的问题,或者更准确地说是 Dispose 习语,是它比大多数人意识到的要正确得多,而且更容易出错——主要是因为对象有可能起死回生。

【讨论】:

  • 感谢 Phil,这是在没有确定性析构函数的情况下解决问题的好方法。
  • 不确定“nice”是不是这个词,尤其是在没有闭包语法的情况下——但它可以完成工作:-) 也可以在这里查看我关于该主题的博客文章:metatechnology.blogspot.com/2007/02/…
  • @Phil:当然,语法并不完全“好”,但正如您所说,它可以完成工作。有趣的文章顺便说一句。据我所知,这实际上是一种比 ctors/dtors 更通用的解决方案,因为它是一种具有良好闭包支持的语言。
【解决方案3】:

不。没有用于在堆栈上分配对象的工具。每个对象都在堆上分配,并且可以比它初始化的任何块都活得更久。或者它可能在块中间被收集,这取决于所有强大的垃圾收集器的变幻莫测。

如果您正在使用服务器,您可能需要查看Java EE。它与 RAII 无关,但它确实有一个不错的系统来管理数据库连接等昂贵对象的生命周期。对于很多问题空间,Java EE 5 实际上非常适合使用。

【讨论】:

  • 谢谢杰。对于我们需要做的事情,JEE 听起来可能有点重量级,但我会向其他人提及,看看他们的想法。
  • 很好的答案。 C++ 没有任何垃圾收集,但您可以避免必须记住通过在堆栈上分配的简单权宜之计进行破坏。无论范围如何退出(通过正常返回或异常),编译器都保证调用析构函数。
【解决方案4】:

我采用的方法是使用分层产品(有时是一种简单的静态方法)来处理资源分配。您不希望这种资源分配在您的程序中乱扔垃圾。

有很多图书馆可以做到这一点。在大多数情况下,您不必担心。

【讨论】:

  • 谢谢彼得,但我关心的是可靠地释放资源。我不明白这个责任如何转移到一个库——你仍然必须记住在正确的时间调用那个库代码。
猜你喜欢
  • 2011-06-23
  • 2021-05-29
  • 2012-01-07
  • 2013-02-20
  • 1970-01-01
  • 2021-10-08
  • 2016-09-26
  • 1970-01-01
相关资源
最近更新 更多