考虑finally {...}块

时间:2013-01-24 09:05:25

标签: java exception-handling

我想知道以下两个swnippest在语义上是否相同,如果没有,有什么区别(我们假设我们想要计算R类型的结果,并且想要防止可能抛出的异常X这样做的过程):

public R tcf(....) {
   try {
       R some = ...;
       ... compute the result ....
       return some; 
   }
   catch (X exception) {
       ... exception handling ....
   }
   finally {
       ... clean up ....
   }
}

以及以下内容:

public R tc(....) {
   try {
       R some = ...;
       ... compute the result ....
       return some; 
   }
   catch (X exception) {
       ... exception handling ....
   }
}

public R tf(....) {
    try {
        return tc(....);  // wrap the try-catch in tc()
    }
    finally {
        ... clean up ....
    }
 }

据我所知,它归结为如果try块中包含的try-catch块在语义上与try-catch-finally块相同,假设finally和中的代码是catch短语保持不变,外部try块只是促进内部短语的结果。

实际相关性:给定一个代码库,它不使用try-catch-finally,当它应该时,并且由于某种原因,由于某些原因,人们无法使用该代码,可能会或多或少地机械地生成一层包装器添加finally的方法。

我完全清楚这一事实,因为很多原因,我应该尽可能地使用try ... catch ...具体来说,我建议以任何方式重构第一个示例,使其看起来像第二个。

相反,我想确保示例2可以安全地重构为示例1。

5 个答案:

答案 0 :(得分:15)

从功能上讲,它们是等价的。

我认为在分配相关资源的同一方法中执行清理是一种很好的方式。在某些情况下,这是解决问题的唯一实用方法(例如,如果清理涉及tcf / tc的本地变量。)

此外,如果tc在自身之后没有清理,则清理成为函数合同的一部分,作为调用者的义务。这使得设计更容易出错,因为清理很容易忘记或出错。此外,如果清理中涉及的步骤以任何方式发生变化,则需要跟踪和更新每个呼叫者。

底线:如果在tcf中执行清理 ,则应该是。

答案 1 :(得分:3)

然而,它们是相同的。在追索(最终发布)索赔之后,try / finally应始终正确。 (请注意,这恰好在尝试之前发生)

例如:

   getLock();
   try {
       doSomething()
   }
   finally {
       releaseLock();
   }

因为忘记清理真的很容易,所以它应该始终与获取资源的位置相同。这样做的原因是你不应该给你的呼叫者带来他们总是要做的事情(你可以自己做)

答案 2 :(得分:2)

不使用try / catch在同一方法中执行finally,而是在没有最终执行的情况下调用某个点进行try / catch的方法,这可能是一个错误。
因此,我不建议你这样写,尽管在你描述的场景中,当包含try / catch的方法无法修改时,必须通过finally添加功能,这是唯一的方法。

答案 3 :(得分:2)

我认为您的描述非常准确,tf finally将在两种情况下执行,并且异常处理将在tcftf catch块中处理。

即,在示例(1)中:如果您的try发生异常,则流程将执行您的catch阻止,然后执行finally block。在示例(2)中,在tc中,如果try块中发生异常,则流将在您的catch块中继续,然后(假设不再重新抛出异常)返回tf中的主叫行。完成tf try阻止后,流程将在tf finally阻止中继续。

我想要注意的主要含义,以及你可能无法以你的建议方式纠正,是:

  1. 如果您在第二个示例中实施代码,则无法从tc访问tf资源,以便能够清除它们。
  2. 即使可以,您也会在不接近资源使用的层上清理资源,甚至可能不被框架的其他客户端代码使用。我想这是一个文档和培训问题。

答案 4 :(得分:1)

两者都是相同的。但是当你有可能在中间断开函数或想要释放函数所拥有的一些资源时,最终会被使用。在这种情况下,catch被跳过,但最终肯定会执行。

尝试既可以用于catch,也可以用于最后或两者。最后一般不能显示发生的异常但是catch可以跟踪异常。