您是否真的需要“最终”障碍


72

java中的try ... catch ... finally块有3种排列。

  1. 试着抓
  2. 尝试...抓住...最后
  3. 尝试...最后

一旦执行了finally块,控制权将移至finally块之后的下一行。如果我删除了finally块并将其所有语句移至try ... catch块之后的行,那么与将它们放入finally块中的效果相同吗?


3
stackoverflow.com/questions/3354823/how-to-use-finally之前已经回答了这个问题。所以它是重复的
Shervin Asgari 2010年


@Shervin,在将其标记为重复之前您是否已阅读了两个问题
Amit Kumar Gupta,2010年

Answers:


51

我认为willcode最接近在这里表达关键点的地方,可能每个人都知道,但是不清楚。

问题是您的要求确实存在一些问题:“如果我在catch块之后编写所有语句,而不是将它们写入到finally块中,那么会出错吗?”

如果将所有语句写在catch块之后,则意味着

1)您将始终捕获异常。

2)捕获异常后,您将始终继续执行下一条语句。

这意味着您将始终在异常发生后始终“正常”继续执行,这通常是您永远不会做的事情实际上实际上希望执行的事情。

例外应该就是这样-例外。如果实际上您可以处理异常,则最好编写代码先考虑这些条件,而根本不要导致异常。如果您遵循此模型,那么异常确实是例外-您无法预期或至多无法解决的情况。真的没想到您应该努力。 这通常意味着您无法处理真正的异常,这也意味着您不应该只是继续执行,而通常是结束应用程序。

通常要做的是允许错误传播回调用堆栈。有人说,这样做是偶然的,链中较高的某个人可能会处理它。我要说的是,从没有发生过,这样做有两个实际目的。一种可能是用户可以修复的东西,如果有的话。因此,您可以将错误传播回来,直到到达可以将其报告给用户的位置为止。或两个,用户无法修复它,但您想获取整个调用堆栈进行调试。然后,将其捕获到顶部即可正常失败。

现在,finally块对您应该具有更多的意义。大家都说它总是运行。对finally的最清晰使用实际上是在尝试... finally块。您现在要说的是,如果代码运行正常,那就太好了。我们仍然需要进行一些清理,最后总是执行,然后继续。但是,如果发生异常,我们现在确实需要finally块,因为我们可能仍需要进行一些清理,但是我们不再在这里捕获异常,因此我们将不再继续。对于确保清除发生,finally块是必不可少的。

异常停止执行的想法可能很难让人理解,除非他们有一定的经验,但这实际上是总是做事的方式。如果发生了错误,那么它可能很小,您应该一开始就将其考虑在内,否则将有越来越多的错误等待发生。

“吞并”错误-捕获它们并继续前进是您最糟糕的事情,因为程序变得不可预测,并且您无法找到并修复错误。

编写良好的代码将包含必要的try ... finally块,以确保无论结果如何,始终释放资源。但是编写良好的代码通常只包含少量的try ... catch块,这些块主要是为了使应用程序尽可能正常地失败或延迟用户而存在,这意味着至少始终将消息传递给用户等。但是您通常不只是发现错误并继续前进。


9
您的结论(1和2)绝对是错误的-不,这个问题并不意味着他将始终捕获异常和/或他将始终遵循后续指示。1)即使没有捕获到异常,除非从函数返回,否则仍然继续执行后续行;2)如果您不返回,则无论您尝试使用try块还是catch-始终会继续执行后续指示(如果有),这是正确的,这里没有什么错。我会回答的是,有人可以在尝试期间返回-但最终仍会执行。就是这样。
Giorgi Tsiklauri

@GiorgiTsiklauri您说执行将继续,“即使您没有捕获到异常也是如此”,但这并不总是正确的-您是说根本没有异常发生吗?如果是这样,则不会在出现异常时将其考虑在内,但您没有catch语句。最终将为此很好地工作。“尝试时返回”绝对不是最终存在的唯一原因。
Poikilos

83

我知道这是一个非常老的问题,但是我今天遇到了,我对给出的答案感到困惑。我的意思是,它们都是正确的,但是当对这个问题有一个非常直接的实际答案时,它们都在理论甚至哲学的层面上回答。

如果您使用return,break,continue或其他任何其他Java关键字来更改catch块(甚至try块)中的代码顺序执行,则finally块内的语句仍将执行。

例如:

public void myFunc() {

    double p = 1.0D;
    String str = "bla";
    try{
        p = Double.valueOf(str);
    }
    catch(Exception ex){
        System.out.println("Exception Happened");
        return;  //return statement here!!!
    }finally{
        System.out.println("Finally");
    }
    System.out.println("After finally");
}

执行此代码时将打印:

Exception Happened 
Finally

这是finally块存在的最重要原因。大多数答案都暗示或提及了这一点,但没有一个人强调它。我认为,因为这是一个新手问题,所以简单易懂的答案非常重要。


4
谢谢,这是我一直在寻找的答案。
Madhavi

43

最重要的是,finally即使引发了异常并且没有捕获到异常,也可以保证执行一个块。然后,您使用finally,块来执行必要的清理,例如关闭流。finally块之后的代码可能永远不会到达。

来自Java教程

try块退出时,finally块始终执行。这样可以确保即使发生意外异常,也可以执行finally块。但是,最后,它不仅对异常处理有用,它还使程序员避免因返回,继续或中断而意外跳过清理代码。将清理代码放在finally块中始终是一个好习惯,即使没有例外的情况也是如此。


1
@articlestack还记得在System.exit();时不会调用finally块;叫做。
Dead Programmer,2010年

1
@Suresh S:好点,但是无论如何要使用System.exit ...没关系...(资源已在操作系统级别释放)
helios 2010年

@helios您不能一概而论,特别是如果该进程正在利用另一个进程的资源。
reubenjohn

1
即使从catch块引发了异常,最终也不会执行吗?
reubenjohn

10

如果我理解这个问题,那么您是在问:

try {
    Foo f = new Foo();
    f.bar();
}
finally
{
    Foo.baz();
}

和:

// This doesn't actually compile because a try block needs a catch and/or finally block
try {
    Foo f = new Foo();
    f.bar();
}
Foo.baz();

或者,更有可能:

Foo f = new Foo();
f.bar();
Foo.baz();

不同的是,如果任一new Foo()f.bar()抛出一个异常,该finally块将在第一时间拿到情况下执行,但Foo.baz()不会在最后两种情况得到执行:而不是控制将跳过Foo.baz(),而JVM寻找一个异常处理程序。


编辑

回应您的评论,该怎么办:

Foo f = new Foo();
try {
    f.bar();
}
catch (Exception ex)
{
    // ...
}

f.baz();

没错,假设该catch块没有引发异常,或者从方法返回指示发生了故障,那么f.baz()无论是否存在异常,都将被调用。但是,即使在这种情况下,该finally块仍可作为f.baz()用于清理的文档。

更重要的是,通常会引发异常,这一点很重要,因此很难在不知道引发异常的情况下编写继续执行所做的代码。有时候,异常表明您可以忽略一些愚蠢的事情,在这种情况下,您应该吞下该异常。但是,更常见的情况是,您希望通过重新抛出异常(或引发其他异常)或从带有错误代码的方法返回信号来指示失败。

例如,如果f.bar()应该将a转换String为a Double,并且在失败时抛出a NumberFormatException,则该try块之后的代码可能需要知道Stringa实际上并未转换为a Double。而且,因此,通常您不希望在代码catch块之后继续操作。相反,您将要发出失败信号。这称为“失败中止”(与“失败后恢复”相比,可能应该称为“手指交叉失败后陷入困境”)。

除非在特殊情况下,否则您可能会感到困惑。例如,该catch块可以设置与之相关的内容DoubleDouble.NaN该内容专门用于在数学表达式中正确传播错误。即使在这种情况下,该finally块也可以用作f.baz()涉及某种清理的文档。


1
我说的是尝试抓住两个障碍。现在最后需要什么?因为如果发现错误并采取了特定的措施,则应执行下一条语句(finally {}或catch {}之后的语句)。
阿米特·库玛·古普塔

@aticlestak:我对文章进行了编辑,希望可以回答您的问题。您描述的案例开启了有关如何使用异常的理念。
Max Lybbert 2010年

3

finally块包含无论是否捕获到异常都应执行的代码行。即使您决定停止在该方法中运行代码。因此,tcf之后的代码可能不会执行,但是最终代码是“保证的”(保证非崩溃的意思是立即打破了不可处理的错误)。


如果尝试...该怎么办呢?但是我写的rest语句没有finally块还是处于finally块?有什么不同?
阿米特·库玛·古普塔

2

是的,会有一些非常严重的错误。

也就是说,您的代码仅在有error时才运行。

内部语句finally始终运行,无论是否引发异常。这就是重点。


2
例如,无论是否存在错误,您都希望运行清除代码。那属于finally块。
Michael Petrotta

您可以通过添加Throw new Exception()来确保每次都会出错。作为“尝试”块中的最后一行LOL
JumpingJezza

2
我认为您误解了“ catch块之后的所有语句”,对我来说听起来像try {...; }抓{....; }所有陈述;
Maxem 2010年

@JumpingJezza:是的,但是那条语句很混乱,因为finally中的代码不是本地在try块中的,因此需要像在其他任何地方一样捕获错误。最终代码中的代码应具有鲁棒性,以使其积极地发挥作用而不产生异常。
中午

@马克西姆:这是一种解释,但我无法想象问这个问题的目的,因为将其与之混淆会显得很奇怪……
午间丝绸

2

最后阻止,特别是在防止异常时使用。如果发生任何运行时错误,程序可能会导致终止。因此,此时,它将在终止程序之前调用finally块。通常,“最终”包含连接关闭语句,保存操作和文件输入,输出关闭操作。


1

如果您的代码从不抛出异常,或者您正在使用所有正确的异常。并非总是这样。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.