没有 catch 块的finally 块是java 反模式吗?
我刚刚在对一些如下所示的代码进行故障排除时经历了一次非常痛苦的故障排除经历:
try {
doSomeStuff()
doMore()
} finally {
doSomeOtherStuff()
}
问题很难解决,因为 doSomeStuff() 引发了异常,这又导致 doSomeOtherStuff() 也引发了异常。 第二个异常(由 finally 块引发)被抛出到我的代码中,但它没有第一个异常(由 doSomeStuff() 引发)的句柄,这是问题的真正根源。
如果代码是这样说的,那么问题就很明显了:
try {
doSomeStuff()
doMore()
} catch (Exception e) {
log.error(e);
} finally {
doSomeOtherStuff()
}
所以,我的问题是这样的:
在没有任何 catch 块的情况下使用的 finally 块是否是众所周知的 java 反模式? (它似乎显然是众所周知的反模式“不要吞噬异常!”的一个不太明显的子类)
I just had a pretty painful troubleshooting experience in troubleshooting some code that looked like this:
try {
doSomeStuff()
doMore()
} finally {
doSomeOtherStuff()
}
The problem was difficult to troubleshoot because doSomeStuff() threw an exception, which in turn caused doSomeOtherStuff() to also throw an exception. The second exception (thrown by the finally block) was thrown up to my code, but it did not have a handle on the first exception (thrown from doSomeStuff()), which was the real root-cause of the problem.
If the code had said this instead, the problem would have been readily apparent:
try {
doSomeStuff()
doMore()
} catch (Exception e) {
log.error(e);
} finally {
doSomeOtherStuff()
}
So, my question is this:
Is a finally block used without any catch block a well-known java anti-pattern? (It certainly seems to be a not-readily-apparent subclass of the obviously well-known anti-pattern "Don't gobble exceptions!")
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
一般来说,不,这不是反模式。 finally 块的目的是确保无论是否抛出异常,内容都会得到清理。 异常处理的重点在于,如果您无法处理它,则可以通过提供的相对干净的带外信令异常处理来将其交给可以处理的人。 如果您需要确保在引发异常时清理内容,但无法在当前范围内正确处理异常,那么这正是正确的做法。 您可能只是想更加小心地确保您的finally 块不会抛出。
In general, no, this is not an anti-pattern. The point of finally blocks is to make sure stuff gets cleaned up whether an exception is thrown or not. The whole point of exception handling is that, if you can't deal with it, you let it bubble up to someone who can, through the relatively clean out-of-band signaling exception handling provides. If you need to make sure stuff gets cleaned up if an exception is thrown, but can't properly handle the exception in the current scope, then this is exactly the correct thing to do. You just might want to be a little more careful about making sure your finally block doesn't throw.
我认为这里真正的“反模式”是在
finally
块中做一些可以抛出的事情,而不是没有捕获。I think the real "anti-pattern" here is doing something in a
finally
block that can throw, not not having a catch.一点也不。
问题出在finally里面的代码。
请记住,finally 总是会被执行,并且将可能引发异常的内容放在那里是有风险的(正如您刚刚目睹的那样)。
Not at all.
What's wrong is the code inside the finally.
Remember that finally will always get executed, and is just risky ( as you have just witnessed ) to put something that may throw an exception there.
尝试一下finally 却没有catch 绝对没有错。 请考虑以下情况:
如果引发异常并且此代码不知道如何处理它,则该异常应该在调用堆栈中向上冒泡到调用者。 在这种情况下,我们仍然想要清理流,所以我认为有一个没有 catch 的 try 块是非常有意义的。
There is absolutely nothing wrong a try with a finally and no catch. Consider the following:
If an exception is thrown and this code doesn't know how to deal with it then the exception should bubble up the call stack to the caller. In this case we still want to clean up the stream so I think it makes perfect sense to have a try block without a catch.
我认为这远不是一种反模式,而是在释放方法执行期间获得的资源至关重要时我经常做的事情。
在处理文件句柄(用于写入)时,我做的一件事是在使用 IOUtils.closeQuietly 方法关闭流之前刷新流,该方法不会引发异常:
喜欢这样做,原因如下:
I think it's far from being an anti-pattern and is something I do very frequently when it's critical do deallocate resources obtained during the method execution.
One thing I do when dealing with file handles (for writing) is flushing the stream before closing it using the IOUtils.closeQuietly method, which doesn't throw exceptions:
I like doing it that way for the following reasons:
我想说没有 catch 块的 try 块是一种反模式。 说“不要在没有捕获的情况下进行最终”是“不要在没有捕获的情况下进行尝试”的子集。
I'd say a try block without a catch block is an anti-pattern. Saying "Don't have a finally without a catch" is a subset of "Don't have a try without a catch".
我以以下形式使用 try/finally :
与 try/catch/finally 相比,我更喜欢它(+ 最后嵌套 try/catch)。
我认为它更简洁,并且我不重复 catch(Exception)。
I use try/finally in the following form :
I prefer this to the try/catch/finally (+ nested try/catch in the finally).
I think that it is more concise and I don't duplicate the catch(Exception).
也不要这样做...您只是隐藏了更多错误(并不是完全隐藏了它们...而是使处理它们变得更加困难。当您捕获 Exception 时,您还捕获了任何类型的 RuntimeException (例如 NullPointer 和 ArrayIndexOutOfBounds)一般来说
,捕获必须捕获的异常(检查异常)并在测试时处理其他异常。运行时异常旨在用于程序员错误,而程序员错误是不应该在正确调试的程序中发生的事情。
Don't do that either... you just hid more bugs (well not exactly hid them... but made it harder to deal with them. When you catch Exception you are also catching any sort of RuntimeException (like NullPointer and ArrayIndexOutOfBounds).
In general, catch the exceptions you have to catch (checked exceptions) and deal with the others at testing time. RuntimeExceptions are designed to be used for programmer errors - and programmer errors are things that should not happen in a properly debugged program.
在我看来,更可能的是
finally
带有catch
表明存在某种问题。 资源习惯用法非常简单:在 Java 中,几乎任何地方都可以出现异常。 通常,acquire 会抛出一个已检查的异常,处理该异常的明智方法是在 how much 周围放置一个 catch。 不要尝试一些可怕的空检查。
如果你真的要肛门,你应该注意到例外之间存在隐含的优先级。 例如,ThreadDeath 应该破坏所有内容,无论它来自获取/使用/释放。 正确处理这些优先事项是不雅观的。
因此,请使用 Execute around 惯用法抽象您的资源处理。
In my opinion, it's more the case that
finally
with acatch
indicate some kind of problem. The resource idiom is very simple:In Java you can have an exception from pretty much anywhere. Often the acquire throws a checked exception, the sensible way to handle that is to put a catch around the how lot. Don't try some hideous null checking.
If you were going to be really anal you should note that there are implied priorities among exceptions. For instance ThreadDeath should clobber all, whether it comes from acquire/use/release. Handling these priorities correctly is unsightly.
Therefore, abstract your resource handling away with the Execute Around idiom.
Try/Finally 是一种正确释放资源的方法。 finally 块中的代码永远不应该抛出异常,因为它应该只作用于在进入 try 块之前获取的资源或状态。
顺便说一句,我认为 log4J几乎是一种反模式。
如果您想检查正在运行的程序,请使用适当的检查工具(即调试器、IDE,或者在极端意义上是字节码编织器,但不要在每一行中都放置日志语句!)。
在您提供的两个示例中,第一个看起来是正确的。 第二个包含记录器代码并引入了一个错误。 在第二个示例中,如果前两个语句抛出异常(即捕获它并记录它但不重新抛出),则抑制异常。这是我发现在 log4j 使用中非常常见的情况,也是应用程序设计的真正问题。基本上通过您的更改,您会使程序以系统难以处理的方式失败,因为您基本上继续前进,就好像您从未遇到过异常一样(有点像 VB 基本错误恢复下一个构造)。
Try/Finally is a way to properly free resources. The code in the finally block should NEVER throw since it should only act on resources or state that was acquired PRIOR to entry into the try block.
As an aside, I think log4J is almost an anti-pattern.
IF YOU WANT TO INSPECT A RUNNING PROGRAM USE A PROPER INSPECTION TOOL (i.e. a debugger, IDE, or in an extreme sense a byte code weaver but DO NOT PUT LOGGING STATEMENTS IN EVERY FEW LINES!).
In the two examples you present the first one looks correct. The second one includes the logger code and introduces a bug. In the second example you suppress an exception if one is thrown by the first two statements (i.e. you catch it and log it but do not rethrow. This is something I find very common in log4j usage and is a real problem of application design. Basically with your change you make the program fail in an way that would be very hard for the system to handle since you basically march onward as if you never had an exception (sorta like VB basic on error resume next construct).
如果方法有多个 return 语句,
try-finally
可以帮助您减少复制粘贴代码。 考虑以下示例 (Android Java):如果没有
finally
子句,您可能必须编写cursor.close()
两次:就在return false< 之前/code> 以及周围的
if
子句之后。try-finally
may help you to reduce copy-paste code in case a method has multiplereturn
statements. Consider the following example (Android Java):If there was no
finally
clause, you might have to writecursor.close()
twice: just beforereturn false
and also after the surroundingif
clause.我认为尝试而不捕获是反模式。 使用 try/catch 处理异常情况(文件 IO 错误、套接字超时等)并不是反模式。
如果您使用 try/finally 进行清理,请考虑使用 using 块。
I think that try with no catch is anti-pattern. Using try/catch to handle exceptional conditions (file IO errors, socket timeout, etc) is not an anti-pattern.
If you're using try/finally for cleanup, consider a using block instead.