没有抓到Java异常?

时间:2013-08-07 07:44:05

标签: java exception try-catch

我对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(经过测试,确实就是这样)。

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

6 个答案:

答案 0 :(得分:196)

来自Java Language Specification 14.20.2.

  

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

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

  •   
  • 如果finally块因原因S而突然完成,则try语句突然完成,原因为S(并且原因R被丢弃)

  •   

所以,当有一个引发异常的catch块时:

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

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

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

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

答案 1 :(得分:19)

finally块中抛出的异常会抑制先前在try或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和close都抛出   异常,然后方法readFirstLineFromFileWithFinallyBlock   抛出finally块抛出的异常;例外   从try块抛出的内容被抑制。


Java 7的新try-with语法增加了另一个异常抑制步骤:try块中抛出的异常会抑制try-with part中之前抛出的异常。

来自同一个例子:

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


在问题的代码中,每个块都明显地丢弃旧的异常,甚至没有记录它,当你试图解决一些错误时不好:

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

答案 2 :(得分: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
}

答案 3 :(得分:5)

您的问题非常明显,答案很简单。 消息为“2”的异常对象被异常对象覆盖,消息为“3”。

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

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

答案 4 :(得分:2)

finally块始终运行。您可以从try块内部return或抛出异常。 finally块中抛出的异常将覆盖catch分支中抛出的异常。

此外,抛出异常不会导致任何输出。第throw new Exception("2");行不会写出任何内容。

答案 5 :(得分: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语句捕获并打印B - # 2;
  3. 阻止最终# 3在try-catch之后执行(或者只尝试,如果没有发生任何异常)语句并打印C - # 4并抛出新的异常;
  4. 这个被外部捕获声明# 5;
  5. 捕获

    结果是ABC321

    的删除方式相同