在java中有一个try ... catch ... finally块的3个排列。
执行finally块后,控制转到finally块之后的下一行。如果我删除finally块并将其所有语句移到try ... catch块之后的行中,那么它与finally块中的它们具有相同的效果吗?
答案 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 语句处理,如果没有处理,可能会有一些不匹配的异常>