try/finally 没有捕获和 return 值

try/finally without catch and return value

我有一个程序如下:

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 块中 return 时,没有向 main 方法抛出异常?

  1. 如果您在 finally 部分使用 return,您将失去例外。方法将以正常类型的 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 部分抛出异常或 return 值是一种不好的做法。例如,创建此部分是为了关闭外部资源。

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

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

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

如果您的 finally 块 return 是一个值, 则根本不再传播异常。

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

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

最后看一下try catch的执行情况

来自java language specification -jls-14.20.2

If the run-time type of V is not assignment compatible with a catchable exception class of any catch clause of the try statement, then the finally block is executed. Then there is a choice:

If the finally block completes normally, then the try statement completes abruptly because of a throw of the value V.

If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and the throw of value V is discarded and forgotten).

The return statementin the finally block was basically stopping the exception that happened in the try block from propagating up even though it wasn't caught.

但是 Java 编译器在编写这段代码时会发出警告。 虽然 return statements 应该总是位于 try block,而 finally 块通常用于 releasing/closing connections, pointers etc.

这似乎是 Java 的行为方式。

看看here

如果您阅读了 java 的最终文档,那么它会说,

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

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

在第一种情况下,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方法没有异常

来自 JLS(强调我的):

If execution of the try block completes abruptly because of a throw of a value V, then there is a choice:
[...]
If the run-time type of V is not assignment compatible with a catchable exception class of any catch clause of the try statement, then the finally block is executed. Then there is a choice:

  • If the finally block completes normally, then the try statement completes abruptly because of a throw of the value V.

  • If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and the throw of value V is discarded and forgotten).

这意味着如果您在 finally 块中 return,方法 returns 不会抛出异常。

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

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

因为finally 块总是被执行,无论是否发生异常,并且如果你return from finally 意味着,您正在将执行发送到调用方法并且您丢失了异常。所以它也会产生警告。

In first program when ArithmeticException occur in try block then call the finally block and after execute the finally block , exception occur . because exception is not handled by program . Second Program when finally block execute after that return statement execute and no exception occur because after the return statement execute compiler return in main method and remaining execution will not execute in finally block . So exception will not occur .