异常被finally吞噬

8
static int retIntExc() throws Exception{
    int result = 1;
    try {
        result = 2;
        throw new IOException("Exception rised.");
    } catch (ArrayIndexOutOfBoundsException e) {
        System.out.println(e.getMessage());
        result = 3;
    } finally {
        return result;
    }
}

我的一位朋友是.NET开发人员,目前正在迁移到Java,他向我提出了关于此源代码的问题。理论上这必须会抛出IOException("Exception rised.")并且整个方法retIntExc()必须抛出Exception。但是没有任何反应,该方法返回2。
我还没有测试过他的示例,但我认为这不是期望的行为。
编辑:感谢所有答案。你们中的一些人忽略了方法名为retIntExc的事实,这意味着这只是一些测试/实验性的示例,展示了抛出/捕获机制中的问题。我不需要“修复”,我需要解释为什么会发生这种情况。

你为什么不声明它为”throws IOException“呢? - BoltClock
https://dev59.com/tnVD5IYBdhLWcg3wKoSH - jk.
@BoltClock,这与什么相关吗? - Pacerier
7个回答

18

这就是为什么你无法从C#的finally块中返回的原因 :)

虽然这是Java语言规范中明确规定的行为,它在第14.20.2节中进行了规定。

如果finally块因原因S而突然完成,则try语句也因原因S而突然完成(并且值V的抛出被丢弃和遗忘)。

返回是突然完成的一个例子;如果finally块引发了异常,那么它也会突然完成,丢失原始异常。

上面的引用来自这个嵌套的项目符号集合,省略了这里不适用的选项:

  • 如果由于值V的抛出而使try块的执行突然完成,则有选择:
    • 如果V的运行时类型不能分配给try语句的任何catch子句的参数,则执行finally块。然后有选择:
      • 如果finally块因原因S而突然完成,则try语句也因原因S而突然完成(并且值V的抛出被丢弃和遗忘)。

2
谢谢。这就是我需要的 - JLS 参考资料。 - Kiril Kirilov
Java 17 LTS中还未修复? - Pacerier
@Pacerier:我不确定你所说的“fixed”是什么意思——它不是已经按照规定行为了吗? - Jon Skeet

3

这段代码将会返回2,因为

finally语句始终执行


-1 finally并不总是执行,而且它不会打印任何东西。 - Ishtar
请尝试这个。System.out.println(retIntExc()); 在投票之前请三思而后行! - user467871
最终块并不总是被执行。 - Woot4Moo

2
finally 块会在任何异常被抛出时执行。它不仅在你声明的 catch 块捕获异常后执行,而且在 try 块以及所有已捕获的异常之后执行。如果你的方法抛出异常,除非你在方法中捕获并返回 result,否则它不能返回任何内容。但你不能同时实现两者。
此外,除非你的方法有其他代码,否则也永远不会遇到 ArrayIndexOutOfBoundsException 异常。

2
这是因为在异常被传递之前,您发出了一个返回语句,因此返回了一个有效值。您不能同时返回一个值和抛出一个异常。
删除围绕返回的 finally 块将给出您想要的行为。

0

IOException类不是ArrayIndexOutOfBoundsException类的子类,因此catch部分将永远不会执行。

如果你将它改为这样,它将返回3。

static int retIntExc() throws Exception{
        int result = 1;
        try {
            result = 2;
            throw new ArrayIndexOutOfBoundsException ("Exception rised.");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e.getMessage());
            result = 3;
        } finally {
            return result;
        }
    }

0

我不明白为什么这不是预期的行为。在代码块结束时,result等于2。

static int retIntExc() throws Exception{
        int result = 1;
        try {
            result = 2;

然后您抛出了一个异常,但是您的catch块捕获的是不同类型的异常,因此没有执行任何操作。

        throw new IOException("Exception rised.");
    } catch (ArrayIndexOutOfBoundsException e) {
          ...
    }

finally块保证会被执行,因此最后一步是返回2。

这个成功的返回会覆盖冒泡异常。如果你想让异常继续冒泡,那么你不能在finally块中返回。


0

在 finally 方法中加入 return 语句,可以覆盖抛出的异常并返回结果。你的代码应该像这样:

static int retIntExc() throws Exception{
        int result = 1;
        try {
            result = 2;
            throw new IOException("Exception rised.");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e.getMessage());
            result = 3;
        } finally {
            // do something
        }
        // it gets here only when exception is not thrown
        return result;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接