我只有一个非常痛苦的故障排除经验在解决一些代码,是这样的:

try {
   doSomeStuff()
   doMore()
} finally {
   doSomeOtherStuff()
}

问题是很难解决,因为doSomeStuff()把一个异常的,这又引起doSomeOtherStuff()也要扔一个例外。第二种例外情况(扔通过的最后块)被扔到我的代码,但它没有处理在第一个例外(扔从doSomeStuff()),这是真正的根本原因的问题。

如果代码说了这话,而不是,问题已经显而易见:

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

因此,我的问题是这个:

是最后一块使用,没有任何捕捉块一个众所周知java防模式?(它当然似乎是一个不容易-显而易见的子显然众所周知的反模式"不要狼吞虎咽例外!")

有帮助吗?

解决方案

一般来说,不,这不是反模式。 finally块的重点是确保无论是否抛出异常,都会清理内容。异常处理的重点在于,如果你不能处理它,你可以通过相对干净的带外信令异常处理提供给它的人。如果你需要确保在抛出异常时清理了东西,但是无法在当前范围内正确处理异常,那么这是正确的做法。你可能想要更加小心确保你的finally块不会抛出。

其他提示

我认为真正的“反模式”这是在 finally 块中执行的操作,可以抛出,而不是没有捕获。

完全没有。

最终内部的代码出了什么问题。

请记住,最终总是会被执行,并且只是冒着风险(正如你刚刚目睹的那样)放置可能会在那里引发异常的东西。

尝试最终没有任何问题绝对没有错。请考虑以下事项:

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

如果抛出异常并且此代码不知道如何处理它,那么异常应该将调用堆栈冒泡到调用者。在这种情况下,我们仍然希望清理流,所以我认为有一个没有捕获的try块是完全合理的。

我认为这是远远不是一个反模式,是我做的事情非常频繁时,它是至关重要的做释放资源过程中获得的方法执行。

有一件事我当处理与文件处理(对于写作)是冲洗的流关闭它之前使用的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(...)小详细然后相应的尝试...抓住...忽视我块;
  • 如果使用多个流输出为了flush()方法是重要的。流创建的通过传递的其他流以构造应该是冲第一次。同样的事情是有效的close()方法,但flush()更清楚我的意见。

我会说没有catch块的try块是反模式。说“没有最后没有捕获”是“没有捕获的尝试”的子集。

我以下面的形式使用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(在finally中使用+嵌套的try / catch)。 我认为它更简洁,我不复制catch(Exception)。

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

不要那么做......你只是隐藏了更多的bug(好吧并没有完全隐藏它们......但是更难以处理它们。当你捕获Exception时,你也会捕获任何类型的RuntimeException(如NullPointer)和ArrayIndexOutOfBounds)。

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

在我看来,更多的情况是 finally 带有 catch 表示某种问题。资源习惯很简单:

acquire
try {
    use
} finally {
    release
}

在Java中,您可以在任何地方获得异常。通常,获取会抛出一个经过检查的异常,处理这种情况的合理方法是捕获大量的数据。不要尝试一些可怕的空检查。

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

因此,使用Execute Around成语来抽象你的资源处理。

Try / Finally是一种正确释放资源的方法。 finally块中的代码应该永远不会抛出,因为它应该只对在进入try块之前获取的资源或状态起作用。

顺便说一句,我认为log4J 几乎是一种反模式。

如果您想要检查运行程序,请使用正确的检查工具(即调试器,IDE,或者极端意义上的字节代码编织器,但不要在每个错误的路径中记录日志!)。

在两个示例中,您展示的第一个看起来是正确的。第二个包括记录器代码并引入了一个bug。在第二个例子中,如果前两个语句抛出一个异常(即你捕获它并记录它但不重新抛出),你可以抑制异常。这是我在log4j使用中常见的东西,它是应用程序设计的一个真正问题。随着你的改变,你使程序失败的方式对于系统来说很难处理,因为你基本上前进就好像你从来没有异常一样(类似VB基础的错误恢复下一个构造)。

如果方法有多个 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()两次:在返回false 之前以及之后周围的 if 子句。

我认为尝试没有捕获是反模式。使用try / catch来处理异常条件(文件IO错误,套接字超时等)不是反模式。

如果您正在使用try / finally进行清理,请考虑改为使用块。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top