你真的需要'终极'块

Ami*_*pta 67 java exception-handling

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

  1. 试着抓
  2. 尝试...赶上...终于
  3. 尝试......终于

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

小智 68

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

如果你把一个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");
}
Run Code Online (Sandbox Code Playgroud)

执行时,此代码将打印:

Exception Happened 
Finally
Run Code Online (Sandbox Code Playgroud)

这是存在最终块的最重要原因.大多数答案暗示它或在场边提及它,但没有一个是强调它.我认为因为这是一个新手问题,所以直截了当的答案非常重要.

  • 谢谢,这就是我一直在寻找的答案。 (4认同)
  • 这个世界上我们需要更多像您这样的工程师。 (2认同)

Sis*_*hus 48

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

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

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

1)你将永远抓住异常.

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

这意味着您将始终在异常后"正常"继续执行,这通常是您实际上从未想过的事情.

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

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

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

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

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

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

  • 你的结论(1和2)是完全错误的 - 不,这个问题并不意味着他总是会遇到异常和/或他会一直继续接受相应的指示.1)即使您没有捕获异常,除非您从函数返回,否则仍然会继续执行后续行; 2)如果你没有回来,无论你继续尝试阻止还是抓住 - 你总是会继续进行相应的指示(如果有的话),这是正确的,这里没有错.我会回答,一个人可以在尝试期间返回 - 但最终仍然会被执行.这就是区别. (8认同)

wil*_*ood 42

重点是finally即使引发异常并且没有捕获,也可以保证执行块.然后finally,您可以使用该块作为一次机会,执行必要的清理,例如关闭流.可能永远无法访问finally块之后的代码.

来自java 教程

当try块退出时,finally块总是执行.这确保即使发生意外异常也会执行finally块.但最终不仅仅是异常处理有用 - 它允许程序员避免因返回,继续或中断而意外绕过清理代码.将清理代码放在finally块中始终是一种很好的做法,即使没有预期的例外情况也是如此.


Max*_*ert 10

如果我理解这个问题,你问的是什么区别:

try {
    Foo f = new Foo();
    f.bar();
}
finally
{
    Foo.baz();
}
Run Code Online (Sandbox Code Playgroud)

和:

// 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();
Run Code Online (Sandbox Code Playgroud)

或者,更有可能:

Foo f = new Foo();
f.bar();
Foo.baz();
Run Code Online (Sandbox Code Playgroud)

区别在于,无论是new Foo()或者f.bar()抛出异常,finally块都将在第一种情况下执行,但Foo.baz()在最后两种情况下不会执行:相反,Foo.baz()当JVM查找异常处理程序时,控制将跳过.


编辑

回应你的评论,那么:

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

f.baz();
Run Code Online (Sandbox Code Playgroud)

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

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

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

除此之外,在特殊情况下,您可能会混淆.例如,catch块可以设置专门设计的相关值Double,Double.NaN以便在数学表达式中正确传播错误.即使在这种情况下,该finally块也可用作f.baz()某种清理所涉及的文档.