没有 catch 块的finally 块是java 反模式吗?

发布于 2024-07-14 19:02:26 字数 576 浏览 10 评论 0原文

我刚刚在对一些如下所示的代码进行故障排除时经历了一次非常痛苦的故障排除经历:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

回眸一遍 2024-07-21 19:02:26

一般来说,不,这不是反模式。 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.

单身情人 2024-07-21 19:02:26

我认为这里真正的“反模式”是在finally块中做一些可以抛出的事情,而不是没有捕获。

I think the real "anti-pattern" here is doing something in a finally block that can throw, not not having a catch.

冰之心 2024-07-21 19:02:26

一点也不。

问题出在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.

宣告ˉ结束 2024-07-21 19:02:26

尝试一下finally 却没有catch 绝对没有错。 请考虑以下情况:

InputStream in = null;
try {
    in = new FileInputStream("file.txt");
    // Do something that causes an IOException to be thrown
} finally {
    if (in != null) {
         try {
             in.close();
         } catch (IOException e) {
             // Nothing we can do.
         }
    }
}

如果引发异常并且此代码不知道如何处理它,则该异常应该在调用堆栈中向上冒泡到调用者。 在这种情况下,我们仍然想要清理流,所以我认为有一个没有 catch 的 try 块是非常有意义的。

There is absolutely nothing wrong a try with a finally and no catch. Consider the following:

InputStream in = null;
try {
    in = new FileInputStream("file.txt");
    // Do something that causes an IOException to be thrown
} finally {
    if (in != null) {
         try {
             in.close();
         } catch (IOException e) {
             // Nothing we can do.
         }
    }
}

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.

你曾走过我的故事 2024-07-21 19:02:26

我认为这远不是一种反模式,而是在释放方法执行期间获得的资源至关重要时我经常做的事情。

在处理文件句柄(用于写入)时,我做的一件事是在使用 IOUtils.closeQuietly 方法关闭流之前刷新流,该方法不会引发异常:


OutputStream os = null;
OutputStreamWriter wos = null;
try { 
   os = new FileOutputStream(...);
   wos = new OutputStreamWriter(os);
   // Lots of code

   wos.flush();
   os.flush();
finally {
   IOUtils.closeQuietly(wos);
   IOUtils.closeQuietly(os);
}

喜欢这样做,原因如下:

  • 我 关闭文件时忽略异常 - 如果还有尚未写入文件的字节,则文件可能未处于调用者期望的状态;
  • 因此,如果在flush()方法期间引发异常,它将传播到调用者,但我仍然会确保所有文件都已关闭。 IOUtils.closeQuietly(...) 方法比相应的 try ... catch ...ignore me 块更简洁;
  • 如果使用多个输出流,flush() 方法的顺序很重要。 通过将其他流作为构造函数传递而创建的流应首先刷新。 同样的事情对于 close() 方法也是有效的,但在我看来,flush() 更清楚。

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:


OutputStream os = null;
OutputStreamWriter wos = null;
try { 
   os = new FileOutputStream(...);
   wos = new OutputStreamWriter(os);
   // Lots of code

   wos.flush();
   os.flush();
finally {
   IOUtils.closeQuietly(wos);
   IOUtils.closeQuietly(os);
}

I like doing it that way for the following reasons:

  • It's not completely safe to ignore an exception when closing a file - if there are bytes that were not written to the file yet, then the file may not be in the state the caller would expect;
  • So, if an exception is raised during the flush() method, it will be propagated to the caller but I still will make sure all the files are closed. The method IOUtils.closeQuietly(...) is less verbose then the corresponding try ... catch ... ignore me block;
  • If using multiple output streams the order for the flush() method is important. The streams that were created by passing other streams as constructors should be flushed first. The same thing is valid for the close() method, but the flush() is more clear in my opinion.
总以为 2024-07-21 19:02:26

我想说没有 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".

零度° 2024-07-21 19:02:26

我以以下形式使用 try/finally :

try{
   Connection connection = ConnectionManager.openConnection();
   try{
       //work with the connection;
   }finally{
       if(connection != null){
          connection.close();           
       }
   }
}catch(ConnectionException connectionException){
   //handle connection exception;
}

与 try/catch/finally 相比,我更喜欢它(+ 最后嵌套 try/catch)。
我认为它更简洁,并且我不重复 catch(Exception)。

I use try/finally in the following form :

try{
   Connection connection = ConnectionManager.openConnection();
   try{
       //work with the connection;
   }finally{
       if(connection != null){
          connection.close();           
       }
   }
}catch(ConnectionException connectionException){
   //handle connection exception;
}

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).

丑疤怪 2024-07-21 19:02:26
try {
    doSomeStuff()
    doMore()
} catch (Exception e) {
    log.error(e);
} finally {
   doSomeOtherStuff()
}

也不要这样做...您只是隐藏了更多错误(并不是完全隐藏了它们...而是使处理它们变得更加困难。当您捕获 Exception 时,您还捕获了任何类型的 RuntimeException (例如 NullPointer 和 ArrayIndexOutOfBounds)一般来说

,捕获必须捕获的异常(检查异常)并在测试时处理其他异常。运行时异常旨在用于程序员错误,而程序员错误是不应该在正确调试的程序中发生的事情。

try {
    doSomeStuff()
    doMore()
} catch (Exception e) {
    log.error(e);
} finally {
   doSomeOtherStuff()
}

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.

输什么也不输骨气 2024-07-21 19:02:26

在我看来,更可能的是 finally 带有 catch 表明存在某种问题。 资源习惯用法非常简单:

acquire
try {
    use
} finally {
    release
}

在 Java 中,几乎任何地方都可以出现异常。 通常,acquire 会抛出一个已检查的异常,处理该异常的明智方法是在 how much 周围放置一个 catch。 不要尝试一些可怕的空检查。

如果你真的要肛门,你应该注意到例外之间存在隐含的优先级。 例如,ThreadDeath 应该破坏所有内容,无论它来自获取/使用/释放。 正确处理这些优先事项是不雅观的。

因此,请使用 Execute around 惯用法抽象您的资源处理。

In my opinion, it's more the case that finally with a catch indicate some kind of problem. The resource idiom is very simple:

acquire
try {
    use
} finally {
    release
}

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.

梦开始←不甜 2024-07-21 19:02:26

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).

雨后咖啡店 2024-07-21 19:02:26

如果方法有多个 return 语句,try-finally 可以帮助您减少复制粘贴代码。 考虑以下示例 (Android Java):

boolean doSomethingIfTableNotEmpty(SQLiteDatabase db) {
    Cursor cursor = db.rawQuery("SELECT * FROM table", null);
    if (cursor != null) { 
        try {
            if (cursor.getCount() == 0) { 
                return false;
            }
        } finally {
            // this will get executed even if return was executed above
            cursor.close();
        }
    }
    // database had rows, so do something...
    return true;
}

如果没有 finally 子句,您可能必须编写 cursor.close() 两次:就在 return false< 之前/code> 以及周围的 if 子句之后。

try-finally may help you to reduce copy-paste code in case a method has multiple return statements. Consider the following example (Android Java):

boolean doSomethingIfTableNotEmpty(SQLiteDatabase db) {
    Cursor cursor = db.rawQuery("SELECT * FROM table", null);
    if (cursor != null) { 
        try {
            if (cursor.getCount() == 0) { 
                return false;
            }
        } finally {
            // this will get executed even if return was executed above
            cursor.close();
        }
    }
    // database had rows, so do something...
    return true;
}

If there was no finally clause, you might have to write cursor.close() twice: just before return false and also after the surrounding if clause.

揽月 2024-07-21 19:02:26

我认为尝试而不捕获是反模式。 使用 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文