【问题标题】:Multiple return statements without compiler error没有编译器错误的多个返回语句
【发布时间】:2015-09-07 16:17:15
【问题描述】:

这是一个面试问题:

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 而不是 trycatch 块返回。我试图用-Xlint 选项编译这段代码,它给出了一个警告。

warning: [finally] finally clause cannot complete normally

【问题讨论】:

  • 您认为这会破坏 Java 规范中的哪种语言,这会导致错误?

标签: java compiler-errors return try-catch try-catch-finally


【解决方案1】:

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

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

同样,finally 块通常不应该抛出异常。这就是警告说finally 块应该正常完成的原因,也就是说,没有return 或抛出异常。

【讨论】:

    【解决方案2】:

    Java 语言规范中对此进行了描述:

    §14.17

    突然完成finally 子句可能会中断 由return 语句启动的控制。

    §14.20.2

    如果try 块的执行正常完成,那么finally 块被执行,然后有一个选择:

    • 如果finally 块正常完成,则try 语句正常完成。
    • 如果 finally 块由于原因 S 突然完成,那么 try 语句由于原因 S 突然完成。

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

    • 如果finally 块正常完成,则try 语句由于原因R 突然完成。
    • 如果 finally 块由于原因 S 突然完成,那么 try 语句由于原因 S 突然完成(并且原因 R 被丢弃)。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 这其实不是真的。将执行两个返回语句,但第二个将覆盖第一个。
      • 哦。我不知道那件事。请给我一些参考,以便我了解这一点。
      • 好吧,你可以自己尝试一下,不是使用常量,而是使用打印一些东西作为参数的函数。您将看到两个 return 语句的参数都被评估了。
      • 嗯..试过了。你是对的。它评估 return 语句,但由于 finally 必须在完成 try 块后执行,并且它还返回一些值,它覆盖了来自 try 块的返回值。谢谢你纠正我。
      【解决方案4】:

      本质上和这个是一样的:

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

      它不会给出编译错误,尽管它会给出警告。只有当有可能执行 no return 语句时,编译器才会报错。

      【讨论】:

        【解决方案5】:

        绝妙的问题..根据我的知识,如果您在代码中添加了 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
                }
            }
        }
        

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

        【讨论】:

          【解决方案6】:

          您的代码运行良好,因为在 try、catch 和 finally 块中只有一个 return 语句。如果您尝试在 try、catch 或 finally 块之一中编写两个 return 语句,说明存在无法访问的 return 语句,则会发生编译错误。

          【讨论】:

            【解决方案7】:

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

            按照 Java 8 文档的执行流程。它为您提供详细信息。您可以根据以下情况推断返回语句的执行。

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

            然后有一个选择:

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

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

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

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

            – 如果 V 的运行时类型是与可捕获异常兼容的赋值 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 子句的异常类,然后是 finally 块被执行。

            然后有一个选择:

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

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

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

            – 如果finally块正常完成,则try语句完成 突然出于 R 的原因。

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

            此链接中的解释很清楚-javaDoc

            【讨论】:

              【解决方案8】:

              尝试运行这个:

              它将打印: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);
                  }
                }
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2016-07-31
                • 2015-06-01
                • 1970-01-01
                • 2014-08-20
                • 2013-10-27
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多