先执行finally块还是catch块?

26
考虑以下测试用例:
public class Main {

    static int a = 0;

    public static void main(String[] args) {
        try {
            test();
            System.out.println("---");
            test2();
        }
        catch(Exception e) {
            System.out.println(a + ": outer catch");
            a++;
        }
    }

    public static void test()
    {
        try {
            throw new Exception();
        }
        catch (Exception e) {
            System.out.println(a + ": inner catch");
            a++;
        }
        finally {
            System.out.println(a + ": finally");
            a++;
        }
    }

    public static void test2() throws Exception
    {
        try {
            throw new Exception();
        }
        finally {
            System.out.println(a + ": finally");
            a++;
        }
    }
}

输出结果为:

0: inner catch
1: finally
---
2: finally
3: outer catch

为什么在test()中,catch会在finally之前发生,而在test2()中则相反?

9个回答

19
重点如下:
- 在try-catch-finally块中,finally模块是最后执行的。 - 您可以将try块嵌套在另一个try块中,并且每个嵌套的try块都可以有自己的finally,这些finally将为那些特定的嵌套try块最后执行。 - 因此,是的,finally模块最后被执行,但仅适用于它所附加到的try块。
因此,考虑以下代码片段:
try {
    try {
        throw null;
    } finally {
        System.out.println("Finally (inner)");
    }
} catch (Throwable e) {
    System.out.println("Catch (outer)");
}

这将输出 (如在 ideone.com 上所示):

Finally (inner)
Catch (outer)

注意以下内容:

  • (inner) 中,Finally 在最后执行,无论是否有任何一个 catch 成功。
  • Catch (outer) 接在 Finally (inner) 后面执行,但这是因为 Finally (inner) 嵌套在 (outer) 中的另一个 try 块内部。

类似地,以下代码片段:

    try {
        try {
            throw null;
        } catch (Throwable e) {
            System.out.println("Catch (inner)");
        } finally {
            System.out.println("Finally (inner)");
            throw null;
        }
    } catch (Throwable e) {
        System.out.println("Catch (outer)");
    }

这会打印出 (如在ideone.com上所见):

Catch (inner)
Finally (inner)
Catch (outer)

参考文献

相关问题


如果在打印之后加入catch块会怎样? - Snackoverflow

16

因为 test2() 中的 try 块没有 catch 块,只有一个 finally 块。所以代码不会"跳回"到调用者中的 catch 块,然后"跳转"到 finally 块继续执行,这似乎是您所认为的。


7

在相同的try-catch-finally作用域中,catch位于finally之前。

在test2中,try-catch-finally作用域中没有catch,因此在离开该作用域并进入更高层次的catch之前先执行finally。


4

因为finally块总是在退出范围之前执行。调用test2()后的事件顺序如下:

  1. test2()中抛出异常。
  2. 由于test2没有catch块,异常向上传播到调用者。
  3. 由于test2finally块,它会在从方法返回之前被执行。
  4. 主方法中的catch捕获异常。

2
因为无论是否抛出异常,抛出并处理异常或根本没有抛出异常,finally都是在try..catch块中最后执行的代码。
实际上,唯一不会调用finally的情况是如果JVM在执行它之前退出,或者执行try代码的线程被终止或中断。
回应评论: Java Description of finally
Note: If the JVM exits while the try or catch code is being executed,

then the finally block may not execute. Likewise, if the thread executing the try or catch code is interrupted or killed, the finally block may not execute even though the application as a whole continues.

然而,您是正确的,会抛出ThreadDeath异常,并且我在Sun的冲突信息方面找不到太多具体信息。这本身可能是一个问题。
最后(请原谅双关语),@brainimus,如果您在finally中抛出异常,则finally代码确实正在执行,我的观点是在哪些条件下finally代码不会被执行。

我认为杀死线程会导致抛出java.lang.ThreadDeath错误,因此在这种情况下finally块会被执行。(中断当然是普通异常,它们会干扰执行路径。) - Donal Fellows
如果 finally 块中抛出异常,它也会停止执行。 - brainimus

2

try-catch和finally用于避免程序在执行过程中出现不需要的错误而被终止。

以下几点很重要...

1)对于一个块,只有一个try,任意数量的catch语句和一个finally

2)finally是可选的。

3)catch也是可选的,但如果缺少catch语句,则必须出现finally。

4)所有与子异常对应的catch都必须出现在父异常的catch之前。

5)无论是否发生异常,在finally块中出现的语句总是被执行,除了一种情况。

即:如果遇到System.out.exit()语句,则程序立即终止,因此在这种情况下不能执行finally。

注意:即使在try块中出现return语句,finally中的代码也会被执行。


0
如果您用方法的代码替换函数,您将得到以下内容:
public class Main {

    static int a = 0;

    public static void main(String[] args) {
        try {
            try {
                throw new Exception();
            }
            catch (Exception e) {
                // I catch only the *first* exception thrown   
                System.out.println(a + ": inner catch");
                a++;
                // let's go to the finally block
            }
            finally {
                System.out.println(a + ": finally");
                a++;
                // we go on
            }
            System.out.println("---");
            try {
                throw new Exception();
            }
            finally {
                // executed because at the same level
                System.out.println(a + ": finally");
                a++;
            }
        }
        catch(Exception e) {
            // I catch only the *second* exception thrown
            System.out.println(a + ": outer catch");
            a++;
        }
    }

第一个异常使得catch块被执行,然后执行第一个finally块。由于第一个catch块的存在,它在外层不可见。第二个异常被外层的catch块拦截,但位于内层的finally先被执行。


0

首先执行try块。如果try块中有需要捕获的异常,则执行catch块。无论是否有异常,finally块都会运行。如果try块中有返回语句,则在try块中返回之前,会先执行finally块,然后再执行try块中的返回语句。


0
虽然这并不适用于具体的问题,但了解到在catch和finally子句之前资源会被关闭是很好的。
@Test
public void test() {
    try (AutoCloseable closeable = () -> System.out.println("closeable")) {
        throw new Exception("Test");
    } catch (Exception ex) {
        System.out.println("catch");
    } finally {
        System.out.println("finally");
    }
}

输出:

closeable
catch
finally

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接