7

由于这是一个关于 try/finally 子句行为的学术问题,因此我尝试使用一个非常通用的示例。像这样嵌套 try/finally 子句有什么危险吗?

openDatabaseConnection();
try {
    // Methods unrelated to cursor
    // ...

    String cursor_id = openCursor();
    try {
        useCursor(cursor_id);
    } finally {
        closeCursor(cursor_id);
    }

    // Methods unrelated to cursor
    // ...
} catch (Exception e) {
    genericLogError();
} finally {
    closeDatabaseConnection();
}

具体来说,我很想知道是否closeCursor()保证在之前被调用closeDatabaseConnection()。是否有理由嵌套一个 finally 子句之类的应该被视为不好的做法?

4

5 回答 5

5

是的,这是有保证的。

假设在 期间发生了一些异常useCursor(cursor_id),现在将执行内部的 finally块。(由于异常发生在内部try块中)。在此之后将被调用(因为在包含内部genericLogError()的外部发生了异常。并且只有在它被执行之后(外部的)。这是一个可以更好地解释它的图表:trytrycloseDatabasConnection()finallytry

(内部try的异常→内部try的finally)这被认为是外部try的异常→外部try的catch →外部try的finally。

您可以通过从块中打印来测试它。

但为什么不这样做呢?

try {
  useCursor(cursor_id);
} catch(Exception e) {
  genericLogError();
} finally {
  closeCursor(cursor_id);
  closeDatabaseConnection();
}
于 2013-03-18T09:42:29.377 回答
3

假设您首先需要像这样嵌套 try-catch 块,这没有任何问题。对于您提供的示例, Maroun 的回答会更好。如果您在“光标”清理中涉及大量局部变量,则您建议的方法更适合的示例可能是:

openDatabaseConnection();
try {
    Cursor cursor = openCursor();
    CursorHelper helper = new CursorHelper(cursor);
    String cursor_id = cursor.getId();
    String something_else = cursor.getSomethingElse();
    try {
        useCursor(cursor_id, something_else, helper);
    } finally {
        closeCursor(cursor_id, something_else, helper);
    }
} catch (Exception e) {
    genericLogError();
} finally {
    closeDatabaseConnection();
}

父 try 块将捕获嵌套块抛出的异常,但嵌套finally块将首先被调用。这将游标变量的范围保持在第一个try/catch块中。

如果您想将所有这些代码拉到一个try/catch块中,则必须在该块之外声明一些变量,这可能会开始影响可读性:

openDatabaseConnection();
CursorHelper helper = null;
String cursor_id = null;
String something_else = null;
try {
    Cursor cursor = openCursor();
    helper = new CursorHelper(cursor);
    cursor_id = cursor.getId();
    something_else = cursor.getSomethingElse();
    useCursor(cursor_id, something_else, helper);
} catch (Exception e) {
    genericLogError();
} finally {
    if (cursor_id != null) {
        closeCursor(cursor_id, something_else, helper);
    }
    closeDatabaseConnection();
}
于 2013-03-18T09:59:13.430 回答
1

是的,在这种情况下,closeCursor()保证在之前被调用。closeDatabaseConnection()

您可以将两个调用放在一个finally块中(将调用移动到您调用closeCursor()的同一块),但这需要在外部范围内声明游标,这可能是不可取的。例如,您可能希望将所有使用光标的代码放入单独的方法中;在这种情况下,您可能希望在这个新方法中拥有关闭光标的块。finallycloseDatabaseConnection()finally

一般来说,我不认为嵌套finally子句应该被认为是一种不好的做法。人们应该考虑代码的一般可读性,比如一个方法中的行数——也许最好将你的方法分成两个,在这种情况下会有两个finally块,每个方法一个。或者,如果您的代码足够简单且足够短,可以采用一种方法,您可以将代码重新组织为只有一个finally块。

于 2013-03-18T09:45:20.410 回答
1

当控制离开 try 块时,finally 子句的执行得到保证。这里控制在外部块之前离开内部try块,因此执行顺序得到保证。

没有理由认为这应该被认为是不好的做法,除了你正在制作一个可以分成两个或更多的大方法这一事实之外。

于 2013-03-18T09:45:45.287 回答
0

使用 try/chatch 然后最后。
然后你的代码就很好了。
始终关闭光标和连接是一种很好的编码习惯,在这里你必须在调用closeCursor(cursor_id)之前调用closeDatabaseConnection()你已经完成的操作。
由于 finally 块将始终执行,尽管Exception在各个块的执行过程中会发生一些情况,所以在您的代码中,光标将首先关闭,然后数据库连接将关闭,所以这是完美的。

请找到修改后的代码片段供您参考:

 openDatabaseConnection()
    try {
        // Methods unrelated to cursor
        // ...

        String cursor_id = openCursor();
        try {
            useCursor(cursor_id);
        } 
        catch (Exception e) { //Added the catch block.
        genericLogError();
       } 
        finally {
            closeCursor(cursor_id);
        }

        // Methods unrelated to cursor
        // ...
    } catch (Exception e) {
        genericLogError();
    } finally {
        closeDatabaseConnection();
    }

或者您可以删除内部尝试并可以在外部最终本身中关闭两者,例如:

//outer try block
try{
....
}catch(Exception e){
genericLogError();
}
finally {
         closeCursor(cursor_id);
        closeDatabaseConnection();
    }

这也行。

于 2013-03-18T09:45:01.167 回答