从Java中的finally块返回

时间:2008-09-07 03:15:59

标签: java exception return try-catch-finally

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

似乎很多人认为按照“Don't return in a finally clause”的描述做这件事是件坏事。更深入地抓了一下,我还发现了'Java's return doesn't always',它显示了最终块中其他类型流控制的一些非常可怕的例子。

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

6 个答案:

答案 0 :(得分:133)

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

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

发生的事情是异常在其他一些代码中被抛弃了。它被捕获并记录并在somethingThatThrewAnException()方法中重新抛出。但是这个例外并没有在problemMethod()之后传播。经过长时间的观察,我们终于将其追溯到了返回方法。 finally块中的返回方法基本上停止了try块中发生的异常,即使它没有被捕获也会向上传播。

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

答案 1 :(得分:84)

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

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

答案 2 :(得分:19)

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

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

答案 3 :(得分:13)

添加控制结构并返回finally {}块只是“仅仅因为你可以”滥用的另一个例子,这些滥用分散在几乎所有的开发语言中。杰森是正确的,暗示它可能很容易成为维护的噩梦 - 反对函数早期回报的论据更多地适用于“迟到的回报”。

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

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

答案 4 :(得分: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”)并覆盖异常吗?!

答案 5 :(得分:1)

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

finally块中的return语句将导致所有在try或catch块中引发的异常都将被丢弃。

根据Java Language Specification:

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

注意:根据JLS 14.17-return语句总是突然完成。