尝试-捕获-最终返回澄清

91
通过阅读本论坛中与上述主题相关的所有问题(请参见标题),我充分了解到finally总是会被调用(除了System.exit和无限循环)。 但是,我想知道如果在catch块中调用了一个return,然后在finally块中又调用了另一个return,会发生什么。
例如:
public static void main(String[]args) {
    int a = new TestClass().absorbeTheValue();
}

int absorbeTheValue() {
    try {
        int a = 10/0;
        if (a > 0) return 4;
    } catch(Exception e) {
        return 45;
    } finally {
        return 34;
    }
}    

当调用该方法时,输出结果始终为34。这意味着finally子句总是会被执行。但我认为其他的“return”语句根本没有被执行。在许多帖子中,我发现finally会将内容覆盖catch子句的返回值。我的理解是,一旦要评估catch子句中的返回值,控制流就会转移到finally子句,而finally子句中有另一个返回值,这次返回值将被评估,而不会将控制权传回catch子句。这样,在运行时唯一调用的return将是finally的返回值。你同意这个观点吗? finally中的return不会将控制权传回程序,而是返回值并终止方法。我们可以这么说吗?

5
请缩进你的代码。 - Giovani Guizzo
参见:https://dev59.com/tnVD5IYBdhLWcg3wKoSH - Has QUIT--Anony-Mousse
你有什么问题? - thiagoh
2个回答

135

如果 try 块中的 return 被执行,它会转移控制权到 finally 块,并且函数最终正常返回(不是抛出异常)。

如果发生异常,但代码从 catch 块中的 return 执行,控制权将被转移到 finally 块,并且函数最终正常返回(不是抛出异常)。

在你的示例中,finally 块内有一个 return,因此无论发生什么,该函数都将返回 34,因为 finally 有最后的话语权。

尽管你的示例没有涵盖这一点,但即使你没有 catch 块,或者在 try 块中抛出异常并且未被捕获,这个规则仍然适用。通过从 finally 块中执行 return,你可以完全忽略异常。请考虑:

public class FinallyReturn {
  public static final void main(String[] args) {
    System.out.println(foo(args));
  }

  private static int foo(String[] args) {
    try {
      int n = Integer.parseInt(args[0]);
      return n;
    }
    finally {
      return 42;
    }
  }
}

如果您在不提供任何参数的情况下运行它:
$ java FinallyReturn
...foo中的代码会抛出一个ArrayIndexOutOfBoundsException异常。但是因为finally块执行了一个return,所以该异常被抑制了。
这就是为什么最好避免在finally中使用return的原因之一。

需要注意的是,对于这些目的,throw将可以与return互换使用。(例如,throw new Exception(4)throw new exception(34) - Marcus
我只是想补充一下user1442960所说的。JLS没有关于在catch或finally中使用return的规定。它使用了“突然”的术语,我猜这意味着异常或返回。 - Steve11235
1
@Steve11235:不,从catch块中的return语句会完成“正常”而不是“突然”的退出。这在第11节中已经很清楚了:“在抛出异常的过程中,Java虚拟机会突然完成当前线程中已经开始但尚未完成执行的任何表达式、语句、方法和构造函数调用、初始化程序以及字段初始化表达式。” - T.J. Crowder

95

这里是一些展示它如何工作的代码。

class Test
{
    public static void main(String args[]) 
    { 
        System.out.println(Test.test()); 
    }

    public static String test()
    {
        try {
            System.out.println("try");
            throw new Exception();
        } catch(Exception e) {
            System.out.println("catch");
            return "return"; 
        } finally {  
            System.out.println("finally");
            return "return in finally"; 
        }
    }
}

结果是:
try
catch
finally
return in finally

11
+1 易于理解并且让我学到了新知识 :) - ldam
1
@LoganDam,T.J. Crowder 给出了一个非常好的解释。 - andre
1
@LoganDam:这是一个很好的干净的操作顺序解释。然而,它没有回答所询问的问题。该问题明确询问了当“finally”块包含一个“return”语句时会发生什么。 - unholysampler
我又学到了新的东西...继续加油吧,伙计们! - ldam
一个很好的测试控制流程如何工作的案例。非常有趣。 - ShanJay
"return"语句会返回到try-catch-finally块之后的下一条语句,而不是退出函数。你应该在示例中加入这个说明。 - Nulik

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