2013-08-07 137 views
166

我有一个小的理论 try-catch结构的问题。Java异常未捕获?

我昨天花了实际考试有关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(测试,真的是这样)。

我的问题是,异常(“2”)去了哪里?

+8

+1 Ahh man,我知道这个答案。我在接受采访时被问到了这一点。理解try/catch/finally如何在堆栈上工作是一个非常好的问题。 –

+10

只有一个打印语句可以打印一个数字(最后一个:'print(e.getMessage())')。你认为输出是'AB2C3':你认为最外层的catch块会被执行两次吗? –

+0

在java中,在执行将控制传出catch块的指令之前,只要finally块存在,就执行finally块。如果只有finally块中的代码没有将控制权转移给外部,则执行catch块的延迟指令。 – Thomas

回答

192

Java Language Specification 14.20.2.

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

  • 如果如果finally块的原因小号突然完成了finally块正常完成,则try语句的原因突然完成R.

  • ,然后try语句由于原因S突然完成(并且原因R被丢弃)

所以,当有catch块抛出异常:

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

,但也有一个finally块也抛出一个异常:

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

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

+71

这甚至适用于'return'语句。如果你的finally块有一个返回值,它将覆盖'try'或'catch'块中的任何返回值。由于这些“功能”,一个好的做法是,最终阻止应该从不**抛出异常或返回语句。 – Augusto

+0

这也是Java 7中的资源尝试的继承优势。如果在关闭资源时生成次要异常(通常会使调试更容易),它将保留初始异常。 – w25r

19

例外在finally块抛出抑制尝试或catch块早些时候抛出的异常。

Java 7的例子:http://ideone.com/0YdeZo

Javadoc's例如:


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和关闭都扔 异常,则该方法readFirstLineFromFileWithFinallyBlock 抛出finally块抛出的异常;从try块抛出的异常 被抑制。


try-with语法的Java 7中添加例外抑制的另一步骤:在try块抛出的异常抑制这些早期扔在试穿用一部分。

来自同一个例子:

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块抛出异常 ,并且在试图关闭 ZipFile和BufferedWriter对象时,最多可以从try-with-resources语句中抛出最多两个异常 。如果从 尝试块抛出异常,并且会从 try-with-resources语句抛出一个或多个异常,那么会禁止从 try-with-resources语句抛出的异常被异常抛出,并且由异常抛出 块是由writeToFileZipFileContents方法抛出的块。您可以通过调用try块抛出的 异常中的Throwable.getSuppressed方法来检索这些被抑制的异常。


在从问题的代码,每个块明明白白丢弃旧例外,甚至没有记录它,当你试图解决一些bug并不好:

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

9

由于throw new Exception("2");是从catch块中抛出而不是try,所以不会再被捕获。
请参阅14.20.2. Execution of try-finally and 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 
} 
+0

是的这是正确的,我看到这种情况正在发生,但我正在寻找解释 - 为什么它是这样的行为 – Kousalik

2

finally块始终运行。从try块内部输入return或引发异常。 finally块中抛出的异常将覆盖catch分支中抛出的异常。

此外,引发异常不会导致任何输出。行throw new Exception("2");不会写任何东西。

+1

是的,我知道抛出异常输出本身没有,但我没有看到理由,为什么异常2应该被删除。我再次变得更聪明一些:-) – Kousalik

+0

总是很长时间,在很长一段时间内会发生任何事情(检查拼图http://wouter.coekaerts.be/2012/puzzle-dreams) – Dainius

4

您的问题非常明显,答案很简单,范围相同。 消息为“2”的Exception对象由消息为“3”的Exception对象覆盖。

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

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

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. 打印并抛出异常# 1;
  2. 这个例外已被catch语句和print B - # 2捕获;
  3. 终止块# 3执行try-catch(或者只尝试,如果没有发生任何异常)声明并打印C - # 4并抛出新的异常;
  4. 这一个已经被外部捕获语句# 5捕获;

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

+0

对不起,异常(“ 1“)不会被忽略,但会被成功捕获 –

+0

@Black Maggie它被缓存并抛出新的异常=>这不会被缓存并且程序被终止。在此块最终执行之前。 –