java中的try ... catch ... finally块有3种排列。
- 试着抓
- 尝试...抓住...最后
- 尝试...最后
一旦执行了finally块,控制权将移至finally块之后的下一行。如果我删除了finally块并将其所有语句移至try ... catch块之后的行,那么与将它们放入finally块中的效果相同吗?
java中的try ... catch ... finally块有3种排列。
一旦执行了finally块,控制权将移至finally块之后的下一行。如果我删除了finally块并将其所有语句移至try ... catch块之后的行,那么与将它们放入finally块中的效果相同吗?
Answers:
我认为willcode最接近在这里表达关键点的地方,可能每个人都知道,但是不清楚。
问题是您的要求确实存在一些问题:“如果我在catch块之后编写所有语句,而不是将它们写入到finally块中,那么会出错吗?”
如果将所有语句写在catch块之后,则意味着
1)您将始终捕获异常。
2)捕获异常后,您将始终继续执行下一条语句。
这意味着您将始终在异常发生后始终“正常”继续执行,这通常是您永远不会做的事情实际上实际上希望执行的事情。
例外应该就是这样-例外。如果实际上您可以处理异常,则最好编写代码先考虑这些条件,而根本不要导致异常。如果您遵循此模型,那么异常确实是例外-您无法预期或至多无法解决的情况。真的没想到您应该努力。 这通常意味着您无法处理真正的异常,这也意味着您不应该只是继续执行,而通常是结束应用程序。
通常要做的是允许错误传播回调用堆栈。有人说,这样做是偶然的,链中较高的某个人可能会处理它。我要说的是,从没有发生过,这样做有两个实际目的。一种可能是用户可以修复的东西,如果有的话。因此,您可以将错误传播回来,直到到达可以将其报告给用户的位置为止。或两个,用户无法修复它,但您想获取整个调用堆栈进行调试。然后,将其捕获到顶部即可正常失败。
现在,finally块对您应该具有更多的意义。大家都说它总是运行。对finally的最清晰使用实际上是在尝试... finally块。您现在要说的是,如果代码运行正常,那就太好了。我们仍然需要进行一些清理,最后总是执行,然后继续。但是,如果发生异常,我们现在确实需要finally块,因为我们可能仍需要进行一些清理,但是我们不再在这里捕获异常,因此我们将不再继续。对于确保清除发生,finally块是必不可少的。
异常停止执行的想法可能很难让人理解,除非他们有一定的经验,但这实际上是总是做事的方式。如果发生了错误,那么它可能很小,您应该一开始就将其考虑在内,否则将有越来越多的错误等待发生。
“吞并”错误-捕获它们并继续前进是您最糟糕的事情,因为程序变得不可预测,并且您无法找到并修复错误。
编写良好的代码将包含必要的try ... finally块,以确保无论结果如何,始终释放资源。但是编写良好的代码通常只包含少量的try ... catch块,这些块主要是为了使应用程序尽可能正常地失败或延迟用户而存在,这意味着至少始终将消息传递给用户等。但是您通常不只是发现错误并继续前进。
我知道这是一个非常老的问题,但是我今天遇到了,我对给出的答案感到困惑。我的意思是,它们都是正确的,但是当对这个问题有一个非常直接的实际答案时,它们都在理论甚至哲学的层面上回答。
如果您使用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块存在的最重要原因。大多数答案都暗示或提及了这一点,但没有一个人强调它。我认为,因为这是一个新手问题,所以简单易懂的答案非常重要。
最重要的是,finally
即使引发了异常并且没有捕获到异常,也可以保证执行一个块。然后,您使用finally
,块来执行必要的清理,例如关闭流。finally块之后的代码可能永远不会到达。
来自Java教程
try块退出时,finally块始终执行。这样可以确保即使发生意外异常,也可以执行finally块。但是,最后,它不仅对异常处理有用,它还使程序员避免因返回,继续或中断而意外跳过清理代码。将清理代码放在finally块中始终是一个好习惯,即使没有例外的情况也是如此。
如果我理解这个问题,那么您是在问:
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
块之后的代码可能需要知道String
a实际上并未转换为a Double
。而且,因此,通常您不希望在代码catch
块之后继续操作。相反,您将要发出失败信号。这称为“失败中止”(与“失败后恢复”相比,可能应该称为“手指交叉失败后陷入困境”)。
除非在特殊情况下,否则您可能会感到困惑。例如,该catch
块可以设置与之相关的内容Double
,Double.NaN
该内容专门用于在数学表达式中正确传播错误。即使在这种情况下,该finally
块也可以用作f.baz()
涉及某种清理的文档。
finally块包含无论是否捕获到异常都应执行的代码行。即使您决定停止在该方法中运行代码。因此,tcf之后的代码可能不会执行,但是最终代码是“保证的”(保证非崩溃的意思是立即打破了不可处理的错误)。
是的,会有一些非常严重的错误。
也就是说,您的代码仅在有error时才运行。
内部语句finally
始终运行,无论是否引发异常。这就是重点。