46

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

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

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

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

4

4 回答 4

30

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

尽管 Java 仍然不提供有关最终确定时间的保证,但它确实获得了类似于 C#using语句的特性:try-with-resources 语句


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

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

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

于 2009-01-25T08:54:05.933 回答
10

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

它称为Execute Around,之前已在此处讨论过

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

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

于 2009-02-14T13:18:26.703 回答
2

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

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

于 2009-01-25T09:17:57.320 回答
-1

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

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

于 2009-01-25T10:30:09.703 回答