继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Java finally 的用法,看这一篇就够了

明明如月
关注TA
已关注
手记 59
粉丝 3857
获赞 1465

本文得到 baeldung team 的翻译许可

1.概述

在本教程中,我们将研究 Java 中的 finally 关键字的用法。 我们将看到如何在错误处理中与 try / catch 块一起使用它。 尽管 finally 的目的是保证代码被执行,但是我们还将讨论 JVM 不执行 finally 代码的特殊情况。

我们还将讨论一些常见的陷阱,在这些陷阱中,finally 块可能会产生意外的结果。

2.什么是finally

try 关键字最后可以定义 finally 代码块。 finally 块中定义的代码,总是在 try 和任何 catch 块之后、方法完成之前运行。

正常情况下,不管是否抛出或捕获异常 finally 块都会执行。

2.1. 一个简单的例子

try {
    System.out.println("The count is " + Integer.parseInt(count));
} catch (NumberFormatException e) {
    System.out.println("No count");
} finally {
    System.out.println("In finally");
}

在这个示例中,不管参数的值是多少,JVM 都执行 finally 块并输出“ In finally”。

2.2 不带 catch 代码块的 finally

try {
    System.out.println("Inside try");
} finally {
    System.out.println("Inside finally");
}

结果

Inside try Insidefinally

2.3 finally 的使用场景

因为不管是否发生异常 finally 都会执行,因此我们可以在 finally 代码块中执行关闭连接、关闭文件和释放线程的的操作。

3. finally 的执行时机

3.1 没异常

当 try 代码块执行完成, finally 代码块就可以执行,哪怕没有发生异常。

try {
    System.out.println("Inside try");
} finally {
    System.out.println("Inside finally");
}

Inside try
Inside finally

3.2 有异常但是没处理器

哪怕 异常没有被 catch , finally 代码块依然会执行。

try {
    System.out.println("Inside try");
    throw new Exception();
} finally {
    System.out.println("Inside finally");
}

即使出现未被处理的异常,JVM 依然会执行 finally 代码块的代码。

Inside try
Inside finally
Exception in thread “main” java.lang.Exception

3.3 有异常处理器

try 代码块发生异常, 被 catch 捕捉, finally 依然会执行。

try {
    System.out.println("Inside try");
    throw new Exception();
} catch (Exception e) {
    System.out.println("Inside catch");
} finally {
    System.out.println("Inside finally");
}

Inside try
Inside catch
Inside finally

3.4 try 代码块中带返回值

即使 try 代码块中返回,也不能阻止 finally 代码块的执行。

try {
    System.out.println("Inside try");
    return "from try";
} finally {
    System.out.println("Inside finally");
}

JVM 会在返回到调用函数前执行 finally 代码块。

Inside try
Inside finally

3.5 在 catch 代码块中返回

在 catch 代码块中添加返回语句,finally 代码依然会执行。

try {
    System.out.println("Inside try");
    throw new Exception();
} catch (Exception e) {
    System.out.println("Inside catch");
    return "from catch";
} finally {
    System.out.println("Inside finally");
}

结果

Inside try
Inside catch
Inside finally

4 啥时候 finally 不会被执行

尽管通常编写 finally 代码块是为了这段代码一定被执行到,但是也有一些特殊情况会导致 JVM 不会执行 finally 代码块。

如果操作系统中断了我们的程序,那么finally 代码块可能就不能被执行。也有很多其他类似的行为导致 finally代码块不被执行。

4.1 调用 System.exit 函数

try {
    System.out.println("Inside try");
    System.exit(1);
} finally {
    System.out.println("Inside finally");
}

结果

Inside try

4.2 调用 halt 函数

try {
    System.out.println("Inside try");
    Runtime.getRuntime().halt(1);
} finally {
    System.out.println("Inside finally");
}

Inside try

4.3 守护线程

如果守护线程刚开始执行到 finally 代码块,此时没有任何其他非守护线程,那么虚拟机将退出,此时 JVM 不会等待守护线程的 finally 代码块执行完成。

Runnable runnable = () -> {
    try {
        System.out.println("Inside try");
    } finally {
        try {
            Thread.sleep(1000);
            System.out.println("Inside finally");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};
Thread regular = new Thread(runnable);
Thread daemon = new Thread(runnable);
daemon.setDaemon(true);
regular.start();
Thread.sleep(300);
daemon.start();

输出

Inside try
Inside try
Inside finally

4.4 try 代码块中无限循环

try {
    System.out.println("Inside try");
    while (true) {
    }
} finally {
    System.out.println("Inside finally");
}

Try 代码块出现无限循环,且不出现异常,finally 也将永远得不到执行。

5. 常见陷阱

我们在使用 finally 关键字时会遇到很多陷阱。

有一些不好的编码方式,如在 finally 代码块中存在返回值或者扔出异常。

5.1 忽视异常

finally 代码块包含返回语句,没有处理未捕获的异常。

try {
    System.out.println("Inside try");
    throw new RuntimeException();
} finally {
    System.out.println("Inside finally");
    return "from finally";
}

此时,try 代码块中的 RuntimeException 会被忽略,函数返回 "from finally"字符串。

5.2 覆盖其他返回语句

如果 finally 代码块中存在返回语句,则 try 和 catch 代码块如果存在返回语句就会被忽略。

try {
    System.out.println("Inside try");
    return "from try";
} finally {
    System.out.println("Inside finally");
    return "from finally";
}

此段代码总是返回 “from finally” 。

5.3 改变 throw 或 return 行为

如果再 finally 代码块中扔出异常,则 try 和 catch 中的异常扔出或者返回语句都将被忽略。

try {
    System.out.println("Inside try");
    return "from try";
} finally {
    throw new RuntimeException();
}

这段代码永远都不会有返回值,总是会抛出 RuntimeException。

6. 结论

本文我们讨论了 Java 的 finally 关键字的用法。然后讨论了 finally 执行和不执行 finally 代码块的情况。

最后给出了开发中关于 finally 常见的使用的陷阱。

需要本文代码,可以去GitHub 配套 项目中下载。


7. 思考题

译者补充:
结合第 4 部分的示例,大家可以思考一下,其他方式可以让 finally 得不到执行吗?
欢迎大家在下方评论探讨。


如果你觉得本文对你有帮助,欢迎点赞、转发、评论,你的支持是我创作的最大动力。
另外想学习,更多开发和避坑技巧,少走弯路,请关注我的专栏:《阿里巴巴Java 开发手册》详解专栏

打开App,阅读手记
2人推荐
发表评论
随时随地看视频慕课网APP