你真的需要'终极'块

时间:2010-10-05 07:13:07

标签: java exception-handling

在java中有一个try ... catch ... finally块的3个排列。

  1. 的try ... catch
  2. 的try ... catch ...最后
  3. 尝试......终于
  4. 执行finally块后,控制转到finally块之后的下一行。如果我删除finally块并将其所有语句移到try ... catch块之后的行中,那么它与finally块中的它们具有相同的效果吗?

9 个答案:

答案 0 :(得分:54)

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

如果你把一个return,break,continue或任何其他java关键字更改了catch块(或者甚至是try block)中的代码的顺序执行,那么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块的最重要原因。大多数答案暗示它或在场边提及它,但没有一个是强调它。我认为因为这是一个新手问题,所以直截了当的答案非常重要。

答案 1 :(得分:45)

我认为willcode最接近于表达关键点,并且可能每个人都意味着它但不清楚。

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

如果您在catch块之后编写所有语句,那么您所暗示的是

1)你将永远抓住异常。

2)在您发现异常后,您将始终继续接下来的语句。

这意味着您将在异常后“正常”继续执行,这通常是您 从不 实际上想做的事情。

例外情况应该就是这样 - 例外。如果您实际上可以处理异常,那么编写代码以首先考虑这些条件并且根本不会导致异常总是更好。如果你遵循这个模型,那么异常就是非常特殊的 - 你无法预料或最多无法修复的条件。真的没有预料到你应该努力的方向。 这意味着一般情况下您无法处理真正的异常,这也意味着您不应该只是继续执行,而是通常会结束应用程序。

通常做的是允许错误传播回调用堆栈。有人说这是在链中较高的人可能能够处理它的情况下完成的。我会说基本上永远不会发生,有两个真正的目的要做到这一点。一个可能是用户可以修复的东西,如果有的话。因此,您将错误传播回来,直到您到达可以向用户报告的位置。或者两个,用户无法修复它,但您希望获得整个调用堆栈以进行调试。然后你在顶部抓住它以优雅地失败。

现在,finally块应该对你有更多的意义。众所周知,它总是运行。最后一个最明确的使用是真的在尝试...最后块。你现在说的是代码运行良好,很棒。我们仍然需要做一些清理,最后总是执行然后我们继续前进。但是如果发生异常,我们现在真的需要最终阻止,因为我们可能仍然需要做一些清理,但我们不再在这里捕获异常,所以我们不再继续前进了。 finally块对于确保清理工作至关重要。

总是暂停执行的异常的想法可能很难被人掌握,直到他们有一定的经验,但这实际上是总是做事的方式。如果发生错误,要么它是如此轻微,你应该考虑到它开始,否则只有越来越多的错误等待发生。

“吞咽”错误 - 抓住它们并继续前进是最糟糕的事情,因为你的程序变得不可预测,你找不到并修复错误。

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

答案 2 :(得分:40)

重点是即使异常被提出而未被捕获,也会保证finally块被执行。然后使用finally块作为一次机会,执行必要的清理,如关闭流。可能永远无法访问finally块之后的代码。

来自java tutorial

  

finally块总是在执行时执行   try块退出。这确保了   即使是,也执行finally块   发生意外的异常。但   最后不仅仅是有用的   异常处理 - 它允许   程序员避免清理   代码被意外绕过了   返回,继续或休息。把   finally块中的清理代码是   即使没有,也总是很好的做法   预计有例外。

答案 3 :(得分: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()将无法获取在最后两种情况下执行:当JVM查找异常处理程序时,控件将跳过Foo.baz()


修改

回应你的评论,如何:

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

f.baz();

你是对的,假设catch块没有重新抛出异常,或者从指示发生故障的方法返回,那么无论是否存在异常,都会调用f.baz()。但是,即使在这种情况下,finally块也可用作f.baz()用于清理的文档。

更重要的是,抛出异常这一事实通常很重要,因此很难编写代码继续执行它正在做的事情而不知道抛出异常。有时,异常表示您可以忽略的愚蠢的事情,在这种情况下,您应该吞下异常。但是,更常见的情况是,您需要通过重新抛出异常(或抛出不同的异常)或使用错误代码从方法返回来发出失败信号。

例如,如果f.bar()应该将String转换为Double,则失败时会抛出NumberFormatException,然后代码try } block可能需要知道String实际上并没有转换为Double。因此,一般情况下,您不希望在catch阻止之后继续。相反,你会想要发出失败信号。这被称为“失败时中止”(与“失败后恢复”相比,可能应该被称为“手指交叉失败后混乱”)。

除非在特殊情况下,否则您可能会糊里糊涂。例如,catch块可以将相关Double设置为Double.NaN,专门用于在数学表达式中正确传播错误。即使在这种情况下,finally块也可以作为f.baz()参与某种清理的文档。

答案 4 :(得分:3)

finally块包含应该执行的代码行,无论是否捕获到异常。即使您决定停止在该方法中运行的代码。因此,t-c-f之后的代码可能无法执行,但最终代码是“保证”的(保证在非崩溃的情况下立即破坏不可处理的错误)。

答案 5 :(得分:2)

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

也就是说,如果出现错误,您的代码只会运行

finally内的语句始终运行,无论抛出异常。这就是重点。

答案 6 :(得分:2)

最后阻止特别用于异常预防时。如果发生任何运行时错误,程序可能会导致终止。所以在这个时候,它会在终止程序之前调用finally块。通常'finally'包含连接结束语句,保存操作和文件输入,输出关闭操作。

答案 7 :(得分:1)

如果您的代码永远不会抛出异常,或者您正在消耗所有正确的异常。这不是经常发生的事情。

答案 8 :(得分:0)

问题确实在这里得到了回答 (how to use finally)

我对答案的理解是:在某些情况下,我们会抛出一些except块无法处理的异常。在那种情况下你会怎么做?你想继续吗?不是个好主意!你最好打印一些东西,表明你通过了一个可能发生或可能没有发生异常的阶段,如果发生了异常,它应该由 catch 语句处理,如果没有处理,可能会有一些不匹配的异常