【Java】finally中不允许使用return/break/continue/throw/goto跳转语句的原因

文章探讨了Java中finally语句块为何不应包含return/break/continue/throw/goto跳转语句。尽管语法上允许,但这样做可能导致意外行为,如改变执行流程或阻止未处理异常的抛出。作者通过示例代码说明,当finally块内有跳转语句时,可能覆盖try/catch中的跳转,导致异常处理丢失,不利于错误发现和定位。正确做法是将跳转语句移出finally块,确保异常能正常传播和处理。

使用sonar检查代码时,出现了下面的问题

Jump statements should not occur in "finally" blocks

finally语句块中不能使用跳转语句——return/break/continue/throw/goto,其实与语法规则无关。也就是说,你要是非这么写,你的编译器不会爆出任何异常。但是它可能会产生一些出人意料的问题,我在网上搜到一些博客,都是比较早的,说的不是很准确,于是专门写一篇博客。本文参考:sona--Jump statements should not occur in "finally" blocks。sonar里写的很简明和清晰,但如果不想看英文,或者想看更详细的解释,就往下接着看吧。

“Jump statement”跳转语句,即我们标题中所说的return/break/continue/throw/goto。try…catch…finally的语句我们比较熟悉,finally是无论如何都要执行的,除非在try/catch的语句块中使用的“System.exit()”。大致如下。

        try{
            //有可能产生异常
        }catch(Exception e){
            //捕获产生的异常
        }finally {
            //无论是否异常,都要最终执行的语句
        }

在finally中出现跳转语句,可能会导致两个问题(欢迎补充):

  • try/catch/finally中同时出现不同跳转语句,以finally中为准。
  • 阻止未处理的异常抛出。

首先来看第一个问题。如果try/catch的语句块有跳转语句,finally语句块先行。因为在执行的过程中,如果在try/catch中看到跳转语句,会将finally中的语句块前置,先执行,然后再去执行原try/catch中的跳转语句。这时,如果finally中的有跳转语句,则按照逻辑,就不会再去执行try/catch中的跳转语句了。示例代码:

    public static void main(String[] args) {
        try{
            System.out.println(test());
        }catch(Exception e){
            System.out.println("Exception from main");
            e.printStackTrace();
        }finally {
            System.out.println("finally from main");
        }
    }

    public static boolean test() throws Exception{
        try {
            throw new Exception("Some error");
        } catch (Exception e) {
            System.out.println("Exception from test");
            return true;    //catch中的返回值无效
        } finally {
            System.out.println("finally word");
            return false;    //以finally的返回结果为准,返回false
        }
    }

执行后的结果如下:

“阻止未处理的异常抛出”。finally中的跳转语句会阻止异常的传递,也就是未经处理的异常,经过finally之后就会被吃掉。具体我们看下面的例子。和第一个例子比,去掉test()中的catch语句。这里我们很有可能会认为,异常会被test()抛出,再在主函数中被捕获,但实际上,异常经过test()的finally就被吃掉了。

    public static void main(String[] args) {
        try{
            System.out.println(test());
        }catch(Exception e){
            System.out.println("Exception from main");
            e.printStackTrace();
        }finally {
            System.out.println("finally from main");
        }
    }

    public static boolean test() throws Exception{
        try {
            throw new Exception("Some error");
        } finally {
            System.out.println("finally word");
            return false;
        }
    }

我们看一下输出结果,异常的传递被截断了,这样就导致一些未被捕获的异常不会发现,即使程序出现问题,也不容易发现和定位。注意,这里不是finally的问题,而是finally中的跳转语句导致的错误。

我们把跳转语句移出finally程序块,展示一下正确的使用,例子如下:

    public static void main(String[] args) {
        try{
            System.out.println(test());
        }catch(Exception e){
            System.out.println("Exception from main");
            e.printStackTrace();
        }finally {
            System.out.println("finally from main");
        }
    }

    public static boolean test() throws Exception{
        try {
            throw new Exception("Some error");
        }catch (IOException e){
            System.out.println("Exception from test");
        }finally {
            System.out.println("finally word");
        }
        return false;
    }

执行结果如下,异常可以正常捕获。

Java和C#中,`finally`块的设计目标是确保在大多数情况下都能执行,尤其是在需要释放资源或进行清理操作时。然而,是否**总是**执行取决于具体的运行环境和程序状态。 在Java中,`finally`块通常会在`try`或`catch`块执行完成后执行,无论是否有异常发生或是否使用了`return`、`break`、`continue`等语句。即使在`try`块中存在`return`语句,`finally`块仍然会在方法返回之前执行[^3]。例如: ```java public class FinallyExample { public static void main(String[] args) { System.out.println(test()); } public static int test() { try { return 10; } finally { System.out.println("Finally block executed"); } } } ``` 输出结果为: ``` Finally block executed 10 ``` 这表明`finally`块在`return`语句执行后仍然被执行[^3]。 然而,如果程序在`try`或`finally`块执行之前终止(例如通过调用`System.exit()`),或者由于外部因素(如线程被中断、系统崩溃、断电等)导致程序异常终止,则`finally`块可能会执行[^4]。 在C#中,行为与Java类似。`finally`块通常会在`try`或`catch`块执行完成后执行,即使在`try`块中有`return`语句的情况下也是如此。`finally`块中的代码会在实际返回值之前执行,但会影响已计算的返回值。例如: ```csharp using System; class Program { static int Test() { try { return 10; } finally { Console.WriteLine("Finally block executed"); } } static void Main() { Console.WriteLine(Test()); } } ``` 输出结果为: ``` Finally block executed 10 ``` 这表明即使在`return`语句执行后,`finally`块仍然会被执行。 然而,C#中也存在特殊情况。如果程序调用了`Environment.Exit()`,或者线程被强制终止、系统崩溃等情况发生,`finally`块可能会执行[^4]。此外,如果在`finally`块中使用了`return`、`throw`或`goto`等控制转移语句,可能会改变正常的执行流程,但这种做法通常被推荐,因为它会使代码行为变得复杂和难以预测。 ### 总结 在Java和C#中,`finally`块设计用于在大多数情况下执行清理操作,通常会在`try`或`catch`块执行完成后执行,无论是否发生异常或是否包含`return`语句。但在以下情况下,`finally`块可能会执行: - 程序通过`System.exit()`或`Environment.Exit()`强制退出; - 线程被中断或终止; - 系统崩溃或断电等外部因素导致程序异常终止[^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值