【问题标题】:try/finally without catch and return value [duplicate]尝试/最终没有捕获和返回值[重复]
【发布时间】:2016-01-10 23:31:36
【问题描述】:

我有一个程序如下:

public class Main {
    public static void main(String[] args)throws Exception
    {
        int res = test();
        System.out.println("after call , res = " + res) ;
    }

    public static int test()throws Exception
    {
        try
        {
            return 10/0;
        }
        finally
        {
            System.out.println("finally") ;
        }
    }
}

上面的程序运行后,在控制台看到如下结果:

finally
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Main.test(Main.java:17)
    at Main.main(Main.java:7)

这种行为是正常的,因为 main 方法抛出了异常。

然后我将代码更改如下:

public class Main {
    public static void main(String[] args)throws Exception
    {
        int res = test();
        System.out.println("after call , res = " + res) ;
    }

    public static int test()throws Exception
    {
        try
        {
            return 10/0;
        }
        finally
        {
            System.out.println("finally") ;
            return 20;
        }
    }
} 

在上面的程序运行时,我在控制台中看到了以下结果:

finally
after call , res = 20

我的问题与第二种格式有关。为什么在 finally 块中返回时,异常没有被抛出到 main 方法?

【问题讨论】:

标签: java exception-handling try-catch-finally


【解决方案1】:

来自JLS(强调我的):

如果 try 块的执行由于抛出 一个值V,那么就有一个选择:
[...]
如果 V 的运行时类型与 try 语句的任何 catch 子句的可捕获异常类的赋值不兼容,则 finally 块被执行。然后有一个选择:

  • 如果 finally 块正常完成,则 try 语句会因为抛出 V 值而突然完成。

  • 如果 finally 块由于原因 S 突然完成,则 try 语句由于原因 S 突然完成(并且值 V 的抛出是 丢弃和遗忘)。

这意味着如果您在finally 块内return,该方法将返回而不抛出异常。

除了return,还有其他语句会导致finally突然完成并忘记异常。它们在JLS Section 14.1 中定义。基本上,它是breakcontinuereturn 或异常(抛出或由语句/方法引起)。完整的try/catch/finally 块然后以这个原因完成。

try/catch/finally 的规范中还有一些情况,尤其是在没有异常或存在匹配的catch 子句的情况下。它归结为finallybeats catchbeats try

【讨论】:

    【解决方案2】:
    1. 如果在finally 部分中使用return,则会丢失异常。方法将以普通类型的返回值结束。
    2. 如果您不在finally 部分中使用return,在您的情况下,方法将异常结束。

    第一种情况:

    try {
        throw new Exception();
    } finally {
        //Exception will be lost, normal shutdown of the method
        return;
    }
    

    第二种情况:

    try {
        throw new Exception();
    } finally {
        //Exception won't be lost, we'll get Exception in the main method
    }
    

    第三种情况:

    try {
        throw new Exception();
    } finally {
        throw new IOException();
        // we lost Exception, IOException will be thrown
    }
    

    注意:使用finally 部分抛出异常或返回值是一种不好的做法。此部分已创建,例如,用于关闭外部资源。

    【讨论】:

      【解决方案3】:

      当您的异常被抛出时,它将首先通过您的finally 块。

      如果您的 finally 块没有返回或抛出任何东西,那么原始异常将被传递。

      另一方面,如果您的 finally 块返回一个值,则根本不再传播异常。

      【讨论】:

        【解决方案4】:

        在第一个程序中,当try块发生ArithmeticException时,调用finally块,执行finally块后,发生异常。因为程序不处理异常。 第二个程序,在该 return 语句执行之后 finally 块执行并且没有发生异常,因为在 return 语句执行之后,编译器在 main 方法中返回,剩余的执行将不会在 finally 块中执行。所以不会发生异常。

        【讨论】:

          【解决方案5】:

          因为 finally 块总是被执行,无论是否发生异常,并且 如果你从 finally 返回意味着,你正在将你的执行发送到调用方法和 你失去了例外。所以它也会产生警告。

          【讨论】:

            【解决方案6】:

            在第一种情况下,finally 块作为其行为执行,但它没有捕获异常,而是通过 main 方法抛出异常。通过这个例子检查一下

            public class HelloWorld{
            
                 public static void main(String []args)throws Exception
                 {
                    try
                    {
                    int res = test();
                    System.out.println("after call , res = " + res) ;
                    }
                    catch(Exception ex)
                    {
                        System.out.println("Main Catch") ;
                    }
                 }
                 public static int test()throws Exception
                 {
                    try
                    {
                        return 10/0;
                    }
                    finally
                    {
                        System.out.println("finally") ;
                    }
                }
            }
            

            在上面的代码中,Main Catch 被执行了。

            第二种情况,你返回的是数字,所以main方法中没有异常。

            【讨论】:

              【解决方案7】:

              如果您阅读 finally 的 java 文档,那么它会说,

              它允许程序员避免清理代码被return、continue 或break 意外绕过。将清理代码放在 finally 块中始终是一个好习惯,即使没有预料到异常。

              所以如果你把清理代码放在 finally 块之后,如果有异常就不会被调用。

              【讨论】:

                【解决方案8】:

                finally 块中的 return 语句基本上是停止 try 块中发生的异常甚至无法传播 虽然没被抓到。

                但是 Java 编译器在编写这段代码时会通知警告。 尽管return statements 应该始终位于try block 中,而finally 块通常适用于releasing/closing connections, pointers etc.

                这似乎是Java 的行为方式。

                看看here

                【讨论】:

                  【解决方案9】:

                  看看try catch finally的执行情况。

                  来自java language specification -jls-14.20.2

                  如果 V 的运行时类型与 try 语句的任何 catch 子句的可捕获异常类的赋值不兼容,则执行 finally 块。然后有一个选择:

                  如果 finally 块正常完成,那么 try 语句会因为抛出 V 值而突然完成。

                  如果 finally 块由于原因 S 突然完成,则 try 语句由于原因 S 突然完成(值 V 的抛出被丢弃并忘记)。

                  【讨论】:

                  • 只是想知道,S 的原因是什么?
                  • @bvdb 在这种情况下,例外。
                  • @bvdb JLS 14.1:docs.oracle.com/javase/specs/jls/se8/html/jls-14.html; Sloppy 说:它可以是任何阻止块在其结束括号处结束的语句:A breakcontinuereturn 或异常。
                  【解决方案10】:

                  finally 块中的所有内容都是在抛出异常之前执行的,所以如果在 finally 块中返回,则根本不会抛出异常。由于这个原因,从 finally 块返回通常是一个坏主意。

                  查看this blog 了解有关此的一些信息。

                  【讨论】:

                    【解决方案11】:

                    Java 的 return 并不总是返回,this 可能会很有趣。

                    【讨论】:

                      猜你喜欢
                      • 2016-04-15
                      • 2013-11-17
                      • 2016-05-02
                      • 1970-01-01
                      • 2021-06-10
                      • 2014-11-05
                      • 1970-01-01
                      • 2017-11-05
                      • 2019-04-14
                      相关资源
                      最近更新 更多