184

最近我惊讶地发现在 Java 的 finally 块中可以有一个 return 语句。

似乎很多人认为按照“不要在 finally 子句中返回”中描述的那样做是一件坏事。再深入一点,我还发现“ Java 的返回并不总是”,它显示了 finally 块中其他类型的流控制的一些非常可怕的例子。

所以,我的问题是,谁能给我一个例子,在 finally 块中的 return 语句(或其他流控制)产生更好/更具可读性的代码?

4

6 回答 6

160

几年前,我很难找到一个由此引起的错误。代码类似于:

Object problemMethod() {
    Object rtn = null;
    try {
        rtn = somethingThatThrewAnException();
    }
    finally {
        doSomeCleanup();
        return rtn;
    }
}

发生的事情是异常在其他一些代码中被抛出。它被捕获并记录并在该somethingThatThrewAnException()方法中重新抛出。但是异常并没有被传播过去problemMethod()。经过很长时间的研究,我们终于找到了返回方法。finally 块中的 return 方法基本上阻止了 try 块中发生的异常传播,即使它没有被捕获。

正如其他人所说,虽然根据 Java 规范从 finally 块返回是合法的,但这是一件坏事,不应该这样做。

于 2008-09-07T20:17:35.007 回答
94

您提供的示例足以说明最终使用流控制。

即使有一个人为的例子表明它“更好”,也要考虑那些必须在以后维护你的代码并且可能不知道其中细微之处的开发人员。那个可怜的开发者甚至可能是你....

于 2008-09-07T03:20:29.397 回答
23

如果你使用 -Xlint:finally,javac 会在 finally 中返回警告。最初 javac 没有发出任何警告——如果代码有问题,它应该无法编译。不幸的是,向后兼容意味着无法禁止意料之外的巧妙愚蠢。

可以从 finally 块中抛出异常,但在这种情况下,所展示的行为几乎可以肯定是您想要的。

于 2008-09-07T13:50:01.473 回答
13

添加控制结构和返回到 finally{} 块只是“仅仅因为你可以”滥用的另一个例子,这种滥用几乎散布在所有开发语言中。Jason 正确地暗示它很容易成为维护的噩梦——反对函数提前返回的论点更适用于这种“延迟返回”的情况。

finally 块的存在有一个目的,无论前面的所有代码中发生了什么,都可以让您完全整理自己。主要是关闭/释放文件指针、数据库连接等,尽管我可以看到它被拉伸到说添加定制审计。

任何影响函数返回的东西都应该在 try{} 块中。即使您有一个检查外部状态的方法,执行了一项耗时的操作,然后再次检查该状态以防它变得无效,您仍然希望在 try{} 中进行第二次检查 - 如果它最终位于内部{}并且长时间操作失败,然后您将不必要地再次检查该状态。

于 2008-09-07T03:31:57.373 回答
6

一个简单的 Groovy 测试:

public class Instance {

  List<String> runningThreads = new ArrayList<String>()

  void test(boolean returnInFinally) {

    println "\ntest(returnInFinally: $returnInFinally)"
    println "--------------------------------------------------------------------------"
    println "before execute"
    String result = execute(returnInFinally, false)
    println "after execute -> result: " + result
    println "--------------------------------------------------------------------------"

    println "before execute"
    try {
      result = execute(returnInFinally, true)
      println "after execute -> result: " + result
    } catch (Exception ex) {
      println "execute threw exception: " + ex.getMessage()
    }  
    println "--------------------------------------------------------------------------\n"

  }

  String execute(boolean returnInFinally, boolean throwError) {
      String thread = Thread.currentThread().getName()
      println "...execute(returnInFinally: $returnInFinally, throwError: $throwError) - thread: $thread"
      runningThreads.add(thread)
      try {
        if (throwError) {
          println "...error in execute, throw exception"
          throw new Exception("as you liked :-)")
        }
        println "...return 'OK' from execute"
        return "OK"
      } finally {
        println "...pass finally block"
        if (returnInFinally) return "return value from FINALLY ^^"
        // runningThreads.remove(thread)
      }
  }
}

Instance instance = new Instance()
instance.test(false)
instance.test(true)

输出:

test(returnInFinally: false)
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: false, throwError: false) - thread: Thread-116
...return 'OK' from execute
...pass finally block
after execute -> result: OK
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: false, throwError: true) - thread: Thread-116
...error in execute, throw exception
...pass finally block
execute threw exception: as you liked :-)
-----------------------------------------------------------------------------


test(returnInFinally: true)
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: true, throwError: false) - thread: Thread-116
...return 'OK' from execute
...pass finally block
after execute -> result: return value from FINALLY ^^
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: true, throwError: true) - thread: Thread-116
...error in execute, throw exception
...pass finally block
after execute -> result: return value from FINALLY ^^
-----------------------------------------------------------------------------

问题:

对我来说有趣的一点是看看 Groovy 如何处理隐式返回。在 Groovy 中,可以从方法中“返回”,只需在末尾留下一个值(不返回)。如果您取消注释 finally 语句中的runningThreads.remove(..)行,您认为会发生什么 - 这会覆盖常规返回值(“OK”)并覆盖异常吗?!

于 2011-03-04T16:44:06.097 回答
2

finally块内部返回将导致exceptions丢失。

finally 块中的 return 语句将导致 try 或 catch 块中可能引发的任何异常被丢弃。

根据Java 语言规范:

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

   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).

Note: As per JLS 14.17 - a return statement always completes abruptly.

于 2020-02-08T06:59:21.213 回答