Java 7 try-with-resources 语法(也称为 ARM block (Automatic Resource Management))在只使用一个 AutoCloseable 资源。但是,当我需要声明多个相互依赖的资源时,我不确定什么是正确的习惯用法,例如一个 FileWriter 和一个包装它的 BufferedWriter。当然,这个问题涉及到一些 AutoCloseable 资源被包装的任何情况,而不仅仅是这两个特定的类。
我想出了以下三个替代方案:
我见过的天真的习惯用法是只在 ARM 管理的变量中声明顶级包装器:
static void printToFile1(String text, File file) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
这很好,很短,但它坏了。因为底层的 FileWriter 没有在变量中声明,所以它永远不会在生成的 finally block 中直接关闭。它将仅通过包装 BufferedWriter 的 close 方法关闭。问题是,如果 bw 的构造函数抛出异常,它的 close 将不会被调用,因此底层的 FileWriter < strong="">不会关闭。
static void printToFile2(String text, File file) {
try (FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
这里,底层资源和包装资源都声明在 ARM 管理的变量中,所以它们都肯定会被关闭,但是底层的 fw.close() 会是调用两次:不仅直接调用,还通过包装bw.close()。
对于这两个都实现 Closeable(它是 AutoCloseable 的子类型)的特定类来说,这应该不是问题,它们的契约(Contract)规定多次调用 关闭是允许的:
Closes this stream and releases any system resources associated with it. If the stream is already closed then invoking this method has no effect.
但是,在一般情况下,我可以拥有仅实现 AutoCloseable(而不是 Closeable)的资源,这并不能保证 close 可以多次调用:
Note that unlike the close method of java.io.Closeable, this close method is not required to be idempotent. In other words, calling this close method more than once may have some visible side effect, unlike Closeable.close which is required to have no effect if called more than once. However, implementers of this interface are strongly encouraged to make their close methods idempotent.
static void printToFile3(String text, File file) {
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
这个版本理论上应该是正确的,因为只有fw代表了需要清理的真实资源。 bw 本身并不持有任何资源,它只委托(delegate)给 fw,因此只关闭底层 fw 就足够了。
另一方面,语法有点不规则,而且 Eclipse 发出警告,我认为这是一个误报,但它仍然是一个必须处理的警告:
Resource leak: 'bw' is never closed
那么,采用哪种方法呢?还是我错过了其他一些正确的成语?
最佳答案
这是我对替代方案的看法:
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
}
对我来说,15 年前从传统 C++ 转向 Java 的最大好处是您可以信任您的程序。即使事情很糟糕并且出错了,他们经常这样做,我希望其余的代码是最好的行为和玫瑰的气味。实际上,BufferedWriter 可能会在这里抛出异常。例如,内存不足并不少见。对于其他装饰器,您知道哪些 java.io 包装类从它们的构造函数中抛出检查异常吗?我不。如果您依赖于那种晦涩的知识,那么代码的可理解性就不会太好。
还有“破坏”。如果出现错误情况,那么您可能不想将垃圾刷新到需要删除的文件(未显示的代码)。当然,删除文件也是另一个有趣的错误处理操作。
通常,您希望 finally block 尽可能短且可靠。添加刷新无助于实现这一目标。对于许多版本,JDK 中的一些缓冲类有一个错误,即 close 内的 flush 异常导致装饰对象上的 close叫。虽然这已经修复了一段时间,但请期待其他实现。
try (
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)
) {
bw.write(text);
}
我们仍然在隐式 finally block 中刷新(现在重复 close - 随着您添加更多装饰器,情况会变得更糟),但是构造是安全的,我们必须隐式 finally block 所以即使失败的 flush 不会阻止资源释放。
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
}
这里有一个错误。应该是:
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
bw.flush();
}
一些实现不佳的装饰器实际上是资源,需要可靠地关闭。此外,某些流可能需要以特定方式关闭(也许它们正在进行压缩并需要写入位以完成,并且不能只刷新所有内容。
虽然 3 在技术上是一种优越的解决方案,但软件开发原因使 2 成为更好的选择。但是,try-with-resource 仍然是一个不充分的修复方法,您应该坚持使用 Execute Around idiom。 ,在 Java SE 8 中应该有更清晰的闭包语法。
关于java - 在 try-with-resources block 中管理多个链接资源的正确习惯用法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12552863/
这似乎应该有一个直截了当的答案,但在Google上花了很多时间,所以我找不到它。这可能是缺少正确关键字的情况。在我的RoR应用程序中,我有几个模型共享一种特定类型的字符串属性,该属性具有特殊验证和其他功能。我能想到的最接近的类似示例是表示URL的字符串。这会导致模型中出现大量重复(甚至单元测试中会出现更多重复),但我不确定如何让它更DRY。我能想到几个可能的方向...按照“validates_url_format_of”插件,但这只会让验证干给这个特殊的字符串它自己的模型,但这看起来很像重溶液为这个特殊的字符串创建一个ruby类,但是我如何得到ActiveRecord关联这个类模型
我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我正在从erb文件切换到HAML。我将hamlgem添加到我的系统中。我创建了app/views/layouts/application.html.haml文件。我应该只删除application.html.erb文件吗?此外,仍然有/public/index.html文件被呈现为默认页面。我想创建自己的默认index.html.haml页面。我应该把它放在哪里以及如何使系统呈现该文件而不是默认索引文件?谢谢! 最佳答案 是的,您可以删除任何已转换为HAML的View的ERB版本。至于你的另一个问题,删除public/index/h
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/