Java异常未捕获?


170

我对try-catch结构有一个小的理论问题。

我昨天参加了有关Java的实践考试,但我不理解以下示例:

try {
    try {
        System.out.print("A");
        throw new Exception("1");
    } catch (Exception e) {
        System.out.print("B");
        throw new Exception("2");
    } finally {
        System.out.print("C");
        throw new Exception("3");
    }
} catch (Exception e) {
    System.out.print(e.getMessage());
}

问题是“输出将是什么样?”

我很确定这将是AB2C3,但请注意,这不是事实。

正确的答案是ABC3(经过测试,实际上就是这样)。

我的问题是,Exception(“ 2”)放在哪里?


8
+1啊,我知道这个答案。采访中有人问我。对于理解try / catch / finally在堆栈上的工作方式,这是一个非常好的问题。
但是我不是包装工班

10
只有一个打印语句可以打印数字(最后一个:)print(e.getMessage())。您认为输出将是AB2C3:您是否认为最外面的catch块将执行两次?
Adrian Pronk

在Java中,在执行将控制权转移到catch块之外的指令之前,如果存在finally块,则将执行该块。如果只有finally块中的代码没有将控制转移到外部,则catch块中的延迟指令将被执行。
托马斯

Answers:


198

来自Java语言规范14.20.2。

如果catch块由于原因R突然完成,则执行finally块。然后有一个选择:

  • 如果finally块正常完成,则try语句由于原因R突然完成。

  • 如果final块由于原因S突然完成,则try语句由于原因S突然完成(并且R的原因被丢弃)

因此,当有一个catch块引发异常时:

try {
    // ...
} catch (Exception e) {
    throw new Exception("2");
}

但是还有一个finally块也会引发异常:

} finally {
    throw new Exception("3");
}

Exception("2")将被丢弃,只会Exception("3")被传播。


72
这甚至适用于return语句。如果您的finally块具有返回值,它将覆盖tryor或catch块中的任何返回值。由于这些“功能”,一个好的做法是,finally块永远不应引发异常或具有return语句。
2013年

这也是try-with-resources在Java 7中具有的继承优势。如果在关闭资源时生成了次要异常,它将保留初始异常,这通常会使调试更容易。
w25r

19

在finally块中引发的异常将抑制在try或catch块中较早引发的异常。

Java 7示例:http//ideone.com/0YdeZo

Javadoc的示例:


static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}

但是,在此示例中,如果方法readLine和close都抛出异常,则方法readFirstLineFromFileWithFinallyBlock引发从finally块引发的异常;从try块引发的异常被抑制。


try-withJava 7 的新语法增加了异常抑制的另一步骤:try块中引发的异常会抑制try-with部分中较早引发的异常。

来自同一示例:

try (
        java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);
        java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
    ) {
        for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) {
            String newLine = System.getProperty("line.separator");
            String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine;
            writer.write(zipEntryName, 0, zipEntryName.length());
        }
    }

可以从与try-with-resources语句关联的代码块中引发异常。在上面的示例中,尝试关闭ZipFile和BufferedWriter对象时,可以从try块引发一个异常,并且try-with-resources语句可以引发两个异常。如果从try块引发异常,而从try-with-resources语句引发一个或多个异常,则将抑制从try-with-resources语句引发的那些异常,并且由该块引发的异常是一个是由writeToFileZipFileContents方法抛出的。您可以通过从try块引发的异常中调用Throwable.getSuppressed方法来检索这些受抑制的异常。


在有问题的代码中,每个块显然都在丢弃旧的异常,甚至没有记录下来,在您尝试解决一些错误时效果不佳:

http://en.wikipedia.org/wiki/Error_hiding


9

由于throw new Exception("2");是从catch块而不是从块抛出的try,因此不会再次被捕获。
14.20.2。try-finally和try-catch-finally的执行

这是发生了什么:

try {
    try {
        System.out.print("A");         //Prints A
        throw new Exception("1");   
    } catch (Exception e) { 
        System.out.print("B");         //Caught from inner try, prints B
        throw new Exception("2");   
    } finally {
        System.out.print("C");         //Prints C (finally is always executed)
        throw new Exception("3");  
    }
} catch (Exception e) {
    System.out.print(e.getMessage());  //Prints 3 since see (very detailed) link
}

是的,没错,我看到了这种情况的发生,但是我一直在寻找解释-为什么它采用这种方式
Kousalik

5

您的问题非常明显,并且答案在相同程度上也很简单。 消息为“ 2”的Exception对象被消息为“ 3”的Exception对象覆盖。

说明: 发生异常时,抛出它的对象以捕获块以进行处理。但是,当catch块本身发生异常时,其对象将转移到OUTER CATCH Block(如果有)以进行异常处理。同样发生在这里。带有消息“ 2”的异常对象将传输到OUTER catch Block。但是,请等待 ..在离开内部try-catch块之前,它必须最终执行。这里发生了我们关注的变化。抛出了一个新的EXCEPTION对象(带有消息“ 3”),或者此finally块替换了已经抛出的Exception对象(带有消息“ 2”)。结果,当打印Exception对象的消息时,我们得到覆盖值,即“ 3”而不是“ 2”。

记住:CATCH块只能处理一个异常对象。


2

finally块始终运行。您是return从try块内部还是引发了异常。finally块中引发的异常将覆盖catch分支中引发的异常。

此外,引发异常不会单独导致任何输出。该行throw new Exception("2");不会写任何内容。


1
是的,我知道抛出Exception本身不会输出任何结果,但是我没有看到原因,为什么应该删除Exception 2。我再聪明一点:-)
Kousalik

总是很长时间,在很长的时间,一切皆有可能(支票拼图wouter.coekaerts.be/2012/puzzle-dreams
纽斯

0

根据您的代码:

try {
    try {
        System.out.print("A");
        throw new Exception("1");   // 1
    } catch (Exception e) {
        System.out.print("B");      // 2
        throw new Exception("2");
    } finally {                     // 3
        System.out.print("C");      // 4 
        throw new Exception("3");
    }
} catch (Exception e) {             // 5
    System.out.print(e.getMessage());
}

如您所见:

  1. 打印A并引发异常# 1;
  2. 这个异常被catch语句和print捕获B - # 2;
  3. 块最终# 3在try-catch(或只有在没有发生任何异常的情况下才尝试)语句之后执行,并打印C - # 4并抛出新异常;
  4. 这个已经被外部catch语句捕获了# 5;

结果是ABC3。和2的省略方式与1


抱歉,没有忽略Exception(“ 1”),但是成功捕获了它
Black Maggie

@Black Maggie缓存并抛出新异常=>缓存和程序终止。并在此块最终执行之前。
nazar_art 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.