没有编译器错误的多个 return 语句

Multiple return statements without compiler error

这是一道面试题:

public class Demo {

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

    static String foo() {
        try {
            return "try ...";
        } catch (Exception e) {
            return "catch ...";
        } finally {
            return "finally ..."; //got as result
        }
    }
}

我的问题是为什么没有编译时错误。当我的 finally 块中有 return 语句时,它从 finally 绑定到 return 而不是 trycatch 块。我试图用 -Xlint 选项编译这段代码,它给出了警告 as.

warning: [finally] finally clause cannot complete normally

没有编译时错误,因为 return 语句中只有 1 条且恰好 1 条实际上 return 将控制权返回给调用代码。

正如@Hoopje 所解释的,trycatch 中的 return 将首先执行,它们各自的 return 语句也会执行。但就在 return 将控件返回到调用代码之前,它将执行 finally 块。现在,这个 block 也是 return 的东西,所以这个 return 覆盖了前一个。

您的代码工作正常,因为在 try、catch 和 finally 块中只有一个 return 语句。如果您尝试在 try、catch 或 finally 块中写入两个 return 语句,表示存在无法访问的 return 语句,则会发生编译错误。

绝妙的问题.. 据我所知 return 如果您在代码中添加了 finally 块,则 try 和 catch 块的语句将转移到 finally 。这就是它的工作原理。

所以在这种情况下,所有代码行都在执行,您可以尝试调试。我在下面的代码中尝试了所有三个块。

public class Main {

    public static void main(String[] args) {
        System.out.println(foo());
    }
    static String foo() {
        try {
            throw new Exception();
        } catch (Exception e) {
            return "catch ...";
        } finally {
            return "finally ..."; //got as result
        }
    }
}

你可以从下面得到这个想法link。 Multiple returns: Which one sets the final return value?

本质上是这样的:

public boolean someMethod(){
        if(1 == 1){
            return true;
        }
        return false;
}

虽然会给出警告,但不会给出编译错误。编译器只会在有可能 no return 语句被执行时给出错误。

这在 Java 语言规范中有描述:

§14.17

Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement.

§14.20.2

If execution of the try block completes normally, then the finally block is executed, and then there is a choice:

  • If the finally block completes normally, then the try statement completes normally.
  • If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S.

If execution of the try block completes abruptly for any other reason R, then the finally block is executed, and then there is a choice:

  • If the finally block completes normally, then the try statement completes abruptly for reason R.
  • If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded).

它不会给出编译错误,因为它是 Java 语言规范所允许的。但是,它会给出警告消息,因为在 finally 块中包含 return 语句通常不是一个好主意。

您的示例中发生的情况如下。 try 块中的 return 语句被执行。但是,必须始终执行 finally 块,因此它会在 catch 块完成后执行。那里出现的 return 语句覆盖了前一个 return 语句的结果,因此方法 returns 第二个结果。

类似地,finally 块通常不应抛出异常。这就是为什么警告说 finally 块应该正常完成,也就是说,没有 return 或抛出异常。

(简短回答-阅读答案的粗体和斜体部分

根据 Java 8 文档的执行流程。它为您提供了详细信息。您可以根据以下内容推断 return 语句的执行。

带有 finally 块的 try 语句首先执行 try 块。

然后有一个选择:

• 如果 try 块的执行正常完成,则 finally 块是 执行,然后有一个选择:

– 如果 finally 块正常完成,则 try 语句完成 通常。

– 如果 finally 块由于原因 S 突然完成,则 try 语句 由于 S.

原因突然完成

• 如果 try 块的执行由于抛出值而突然完成 V,然后有一个选择:

– 如果 V 的 运行 时间类型与可捕获异常的赋值兼容 class try 语句的任何 catch 子句,然后是第一个(最左边)这样的子句 catch 子句被选中。值 V 被分配给参数 选择的 catch 子句,执行该 catch 子句的 Block。

然后有一个选择:

› 如果 catch 块正常完成,则执行 finally 块。 然后有一个选择:

» 如果 finally 块正常完成,则 try 语句 正常完成。

» 如果 finally 块由于任何原因突然完成,则 try 出于同样的原因,语句突然完成。

如果 catch 块由于原因 R 突然完成,则 finally 块 被执行。然后有一个选择:

» 如果 finally 块正常完成,则 try 语句 由于原因 R.

突然完成

» 如果 finally 块由于原因 S 突然完成,则 try 语句由于原因 S(并且原因 R 被丢弃)而突然完成。

– 如果 V 的 运行 时间类型与可捕获的赋值不兼容 try 语句的任何 catch 子句的异常 class,然后是 finally 块被执行。

然后有一个选择:

› 如果 finally 块正常完成,则 try 语句完成 突然因为抛出值 V.

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

• 如果 try 块的执行由于任何其他原因 R 突然完成,则 finally块被执行,然后有一个选择:

– 如果 finally 块正常完成,则 try 语句完成 突然因为原因 R.

– 如果 finally 块由于原因 S 突然完成,则 try 语句 由于原因 S(并且丢弃原因 R)突然完成。

这个link- javaDoc

中的解释很清楚

尝试运行这个:

它将打印:1、2、3,然后抛出除以零的异常

public class Demo {
  public static void main(String[] args) {
    System.out.println(foo());
  }
  public static String print(int a){
    System.out.println(a);
    return String.valueOf(a/0);
  }
  static String foo() {
    try {
      return print(1);
    } catch (Exception e) {
      return print(2);
    } finally {
      return  print(3);
    }
  }
}