关于异常:从Java中的finally块返回

关于异常:从Java中的finally块返回

Returning from a finally block in Java

最近我感到惊讶的是,在Java的最后一个块中有一个返回语句是可能的。

似乎很多人都认为这是一件糟糕的事情,如"不要在最后一句中返回"所述。抓取更深一点,我还发现"Java的返回并不总是",这显示了一些非常可怕的例子,其他类型的流控制在最后块。

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


几年前,我很难找到由这个原因引起的错误。代码是这样的:

1
2
3
4
5
6
7
8
9
10
Object problemMethod() {
    Object rtn = null;
    try {
        rtn = somethingThatThrewAnException();
    }
    finally {
        doSomeCleanup();
        return rtn;
    }
}

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

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


您提供的示例是不使用finally中的流控制的充分理由。

即使有一个"更好"的人为例子,也要考虑开发人员,他们必须在以后维护您的代码,并且可能不知道这些细微之处。那个可怜的开发者可能就是你……


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

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


向finally块添加控制结构和返回只是"仅仅因为您可以"滥用的另一个例子,这些滥用几乎散布在所有开发语言中。Jason认为这很容易成为维护的噩梦,这是正确的——反对函数早期返回的论点更适用于"延迟返回"的情况。

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

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


一个简单的groovy测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class Instance {

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

  void test(boolean returnInFinally) {

    println"
test(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"--------------------------------------------------------------------------
"


  }

  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)

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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条linux命令语句?

    写5条linux命令语句?,工作,地址,系统,信息,目录,命令,管理,标准,功能,文件,li

    linux编译时显示命令?

    linux编译时显示命令?,系统,基础,工具,代码,百度,下来,网上,命令,内核,文件,L

    查看linux类型命令?

    查看linux类型命令?,系统,信息,命令,状态,数据,数字,情况,地址,类型,文件,lin

    linux命令语句规律?

    linux命令语句规律?,系统,管理,基础,网络,信息,命令,服务,简介,工具,标准,lin

    linux删除类型命令?

    linux删除类型命令?,系统,档案,命令,文件,名称,环境,数据,不了,目录,文件夹,

    显示linux网卡命令行?

    显示linux网卡命令行?,系统,信息,工具,网络,服务,电脑,网卡,技术指标,地址,

    linux命令显示内容?

    linux命令显示内容?,标准,系统,数据,命令,百度,实时,时间,信息,文件,内容,lin

    linux常用显示命令?

    linux常用显示命令?,工作,地址,系统,信息,管理,命令,目录,标准,功能,常用命

    linux命令行同步显示?

    linux命令行同步显示?,地址,工具,系统,数据,工作,时间,命令,综合,网址,信息,L

    linux发现新网卡命令?

    linux发现新网卡命令?,系统,信息,网络,电脑,工具,服务,状态,命令,网卡,名称,

    linux分页显示命令?

    linux分页显示命令?,工具,通信,命令,数据,信息,管道,标准,位置,一致,系统,lin

    linux中ps命令显示?

    linux中ps命令显示?,系统,信息,状态,进程,命令,多地,软件,工作,基础,报告,lin

    linux命令逐页显示?

    linux命令逐页显示?,系统,工作,地址,命令,网上,信息,百度,基础,标准,内容,在l

    linux命令发现不了?

    linux命令发现不了?,软件,系统,发行,工具,名称,不了,命令,地方,代理,信息,lin

    linux显示内核命令?

    linux显示内核命令?,地址,发行,信息,工具,电脑,系统,名称,内核,版本,状态,如

    显示等号linux命令?

    显示等号linux命令?,工作,地址,信息,系统,命令,目录,标准,管理,基础,常用命

    查看linux库类型命令?

    查看linux库类型命令?,系统,工作,信息,状态,电脑,命令,工具,代码,地址,发行,

    linux中显示路径命令?

    linux中显示路径命令?,系统,地址,工作,信息,时间,命令,数据,网络,路径,名字,l

    linux显示的命令行?

    linux显示的命令行?,信息,标准,数据,命令,实时,系统,时间,名称,文件,文件名,l

    linux命令多显示几行?

    linux命令多显示几行?,实时,系统,标准,数据,档案,命令,文件,最新,信息,状态,l