【问题标题】:Java's strange behavior while returning from finally block从 finally 块返回时 Java 的奇怪行为
【发布时间】:2012-08-12 05:45:00
【问题描述】:

试试这段代码。为什么 getValueB() 返回 1 而不是 2?毕竟, increment() 函数被调用了两次。

    public class ReturningFromFinally
    {
      public static int getValueA() // This returns 2 as expected
      {
         try     { return 1; }
         finally { return 2; }
      }

      public static int getValueB() // I expect this to return 2, but it returns 1
      {
        try     { return increment(); }
        finally { increment(); }
      }

      static int counter = 0;

      static int increment()
       {
          counter ++;
          return counter;
       }

      public static void main(String[] args)
      {
          System.out.println(getValueA()); // prints 2 as expected
          System.out.println(getValueB()); // why does it print 1?
      }
}

【问题讨论】:

  • 如果你有finally { return increment(); },它将返回2。第一个return 语句的表达式在finally 块之前计算。见Section §14.20.2 of the JLS
  • 或者如果他有 ++counter,它会在第二种方法中返回 2 getValue2
  • 也许最好调用不同的 incrementxxx() 函数(一个在return,另一个在finally),以不同的量递增,只是为了更好地了解问题
  • @ant 不,它不会。 counter++;++counter; 是表达式语句,效果相同。
  • 是的,事实并非如此。在这种情况下,它们具有相同的效果,但并非总是如此 stackoverflow.com/a/24858/169277

标签: java return return-value finally


【解决方案1】:

毕竟,increment() 函数被调用了两次。

是的,但返回值是在第二次调用之前确定的。

返回的值由返回语句中的表达式的评估确定在那个时间点 - 而不是“就在执行离开方法之前”。

来自section 14.17 of the JLS

带有 Expression 的 return 语句试图将控制权转移给包含它的方法的调用者;表达式的值成为方法调用的值。更准确地说,执行此类返回语句首先评估表达式。如果 Expression 的计算由于某种原因突然完成,那么 return 语句会因为这个原因而突然完成。如果 Expression 的求值正常完成,产生一个值 V,那么 return 语句会突然完成,原因是返回值 V。

执行然后转移到finally块,根据section 14.20.2 of the JLS。不过,这不会重新评估 return 语句中的表达式。

如果你的 finally 块是:

finally { return increment(); }

那么新的返回值将是该方法的最终结果(根据第 14.20.2 节) - 但您没有这样做。

【讨论】:

  • 附带说明,您不应该在 finally 块中返回任何内容或抛出任何异常。如果这样做,它会掩盖先前发出的返回值(在 try 内),或作为 try/catch 逻辑的一部分抛出的任何异常。
【解决方案2】:

my comment

如果你有finally { return increment(); },它将返回2。 第一个return 语句的表达式在finally 块之前计算。见Section §14.20.2 of the JLS

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

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

调用getValue2(就像你现在一样)两次将导致1,然后是3

【讨论】:

    【解决方案3】:

    GetValue2 方法中的 finally 块不返回任何内容。它只是调用方法来增加counter

    【讨论】:

      【解决方案4】:

      因为在你的 getValue2() 方法中,你最终被阻塞只是调用了 increment(),它不会返回它。所以你的代码正在做的是递增并返回计数器 (1),然后将计数器递增到 2,但不返回它。

      【讨论】:

        【解决方案5】:

        您在第二个示例中没有显式返回。在这种情况下,它将返回 try 块中的值。这很直观,因为Java 已经执行了try 块内的代码。执行finally 块后不会再执行该块。

        【讨论】:

          【解决方案6】:

          finally 方法的目的是确保在任何情况下都关闭资源。 考虑这个例子:

          public List<Person> getPersons() {
              Connection conn = openConnection();
              try {
                  return selectPersons(conn);
              } finally {
                  conn.close()
              }
          }
          

          conn.close() 语句在 selectPersons(conn) 执行后执行。 否则 selectPersons(conn) 会引发连接关闭错误。

          【讨论】:

            猜你喜欢
            • 2010-09-08
            • 1970-01-01
            • 1970-01-01
            • 2011-09-21
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多