在 catch 和 finally 子句中引发的异常

关于 Java 在大学里的一个问题,有这样一段代码:

class MyExc1 extends Exception {}
class MyExc2 extends Exception {}
class MyExc3 extends MyExc2 {}


public class C1 {
public static void main(String[] args) throws Exception {
try {
System.out.print(1);
q();
}
catch (Exception i) {
throw new MyExc2();
}
finally {
System.out.print(2);
throw new MyExc1();
}
}


static void q() throws Exception {
try {
throw new MyExc1();
}
catch (Exception y) {
}
finally {
System.out.print(3);
throw new Exception();
}
}
}

我被要求提供它的输出。我回答的是 13Exception in thread main MyExc2,但正确答案是 132Exception in thread main MyExc1。为什么?我就是不明白 MyExc2去哪儿了。

178725 次浏览

我认为你只需要走过 finally街区:

  1. 打印“1”。
  2. q打印“3”。
  3. main打印“2”。

即使从 try/catch 块中的任何位置抛出异常,也会执行 Finally 子句。

因为它是在 main中执行的最后一个,并且它抛出一个异常,这就是调用者看到的异常。

因此,确保 finally子句不抛出任何东西非常重要,因为它可以吞噬来自 try块的异常。

这就是 维基百科对 finally 子句的解释:

更常见的是相关从句 (最后,或确保)执行 无论是否发生异常, 通常是为了释放资源 获得在体内的 异常处理块异常处理块。

让我们剖析一下你的程序。

try {
System.out.print(1);
q();
}

因此,1将被输出到屏幕上,然后调用 q()。在 q()中,将引发异常。然后异常被 Exception y捕获,但它什么也不做。然后执行一个 q()2子句(它必须执行) ,因此,3将被打印到屏幕上。因为(在方法 q()中,在 q()3子句中抛出了一个异常,而且 q()方法将异常传递给父堆栈(通过方法声明中的 throws Exception)) new Exception()将被 catch ( Exception i )抛出并捕获,q()0异常将被抛出(现在将它添加到异常堆栈中) ,但是 q()1块中的 q()3将首先执行。

所以,

catch ( Exception i ) {
throw( new MyExc2() );
}
finally {
System.out.print(2);
throw( new MyExc1() );
}

一个 终于子句被称为... (记住,我们刚刚捕捉到了 Exception i并抛出了 MyExc2)本质上,2被打印到屏幕上... 在 2被打印到屏幕上之后,一个 MyExc1异常被抛出。MyExc1public static void main(...)方法处理。

产出:

“132Exception in thread main MyExc1”

讲师是正确的! : -)

本质上 ,如果 try/catch 子句中有 终于,则将执行 finally (之后捕获异常 之前将捕获的异常抛出)

method不能同时 throw两个异常。它将始终抛出最后抛出的 exception,在这种情况下,它将始终是来自 finally块的那个。

当抛出来自方法 q()的第一个异常时,它将捕获异常,然后被抛出的 finally 块所吞噬。

Q () ->抛出 new Exception -> main catch Exception -> throw new Exception -> finally抛出一个新的 exception(来自 catch的那个是“丢失的”)

考虑这个问题最简单的方法是假设有一个对整个应用程序来说是全局的变量,它保存着当前的异常。

Exception currentException = null;

当引发每个异常时,“ currentException”被设置为该异常。当应用程序结束时,如果 currentException 是!= null,然后运行时报告错误。

此外,finally 块总是在方法退出之前运行:

public class C1 {


public static void main(String [] argv) throws Exception {
try {
System.out.print(1);
q();


}
catch ( Exception i ) {
// <-- currentException = Exception, as thrown by q()'s finally block
throw( new MyExc2() ); // <-- currentException = MyExc2
}
finally {
// <-- currentException = MyExc2, thrown from main()'s catch block
System.out.print(2);
throw( new MyExc1() ); // <-- currentException = MyExc1
}


}  // <-- At application exit, currentException = MyExc1, from main()'s finally block. Java now dumps that to the console.


static void q() throws Exception {
try {
throw( new MyExc1() ); // <-- currentException = MyExc1
}
catch( Exception y ) {
// <-- currentException = null, because the exception is caught and not rethrown
}
finally {
System.out.print(3);
throw( new Exception() ); // <-- currentException = Exception
}
}
}

应用程序的执行顺序是:

main()
{
try
q()
{
try
catch
finally
}
catch
finally
}

基于阅读你的答案,看看你是如何想出来的,我相信你认为“进行中的异常”具有“优先级”。记住:

当在 Catch 块或 finally 块中抛出一个新的异常并从该块中传播出去时,当前异常将随着新异常向外传播而中止(并被遗忘)。新的异常像其他异常一样开始解除堆栈,中止当前块(catch 或 finally 块) ,并在此过程中服从任何适用的 catch 或 finally 块。

请注意,适用的捕捉或最终块包括:

当在 catch 块中引发新异常时,新异常仍然受该 catch 的 finally 块(如果有的话)的约束。

现在回溯执行过程,请记住,每当您点击 throw时,您应该中止跟踪当前异常并开始跟踪新异常。

我认为这解决了问题:

boolean allOk = false;
try{
q();
allOk = true;
} finally {
try {
is.close();
} catch (Exception e) {
if(allOk) {
throw new SomeException(e);
}
}
}

众所周知,finally 块是在 try and catch 之后执行的,并且总是被执行... ..。 但是正如您所看到的,有时候查看下面的代码片段会有一点麻烦,您会看到的 Return 和 throw 语句并不总是按照我们期望的主题顺序执行它们应该执行的操作。

干杯。

/////////////Return dont always return///////


try{


return "In Try";


}


finally{


return "In Finally";


}


////////////////////////////////////////////




////////////////////////////////////////////
while(true) {


try {


return "In try";


}


finally{


break;


}
}
return "Out of try";
///////////////////////////////////////////




///////////////////////////////////////////////////


while (true) {


try {


return "In try";


}
finally {


continue;


}
}
//////////////////////////////////////////////////


/////////////////Throw dont always throw/////////


try {


throw new RuntimeException();


}
finally {


return "Ouuuups no throw!";


}
//////////////////////////////////////////////////

Finally 块中的异常取代 catch 块中的异常。

Java 语言规范

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

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

  • 如果 finally 块因为原因 S 突然完成,那么 try 语句因为原因 S 突然完成(因为 R 被放弃)。

class MyExc1 extends Exception {}
class MyExc2 extends Exception {}
class MyExc3 extends MyExc2 {}


public class C1 {
public static void main(String[] args) throws Exception {
try {
System.out.print("TryA L1\n");
q();
System.out.print("TryB L1\n");
}
catch (Exception i) {
System.out.print("Catch L1\n");
}
finally {
System.out.print("Finally L1\n");
throw new MyExc1();
}
}


static void q() throws Exception {
try {
System.out.print("TryA L2\n");
q2();
System.out.print("TryB L2\n");
}
catch (Exception y) {
System.out.print("Catch L2\n");
throw new MyExc2();
}
finally {
System.out.print("Finally L2\n");
throw new Exception();
}
}


static void q2() throws Exception {
throw new MyExc1();
}
}

订单:

TryA L1
TryA L2
Catch L2
Finally L2
Catch L1
Finally L1
Exception in thread "main" MyExc1 at C1.main(C1.java:30)

Https://www.compilejava.net/

处理这种情况,即处理 finally 块引发的异常。可以通过 try block 将 finally 块包围起来: 请看下面的 python 示例:

try:
fh = open("testfile", "w")
try:
fh.write("This is my test file for exception handling!!")
finally:
print "Going to close the file"
fh.close()
except IOError:
print "Error: can\'t find file or read data"

逻辑清晰,直到完成打印出 13。然后,q()中抛出的异常被 main()中的 catch (Exception i)捕获,并且 new MyEx2()准备好被抛出。但是,在抛出异常之前,必须首先执行 finally块。然后输出变成 132finally请求抛出另一个异常 new MyEx1()

因为一个方法不能抛出多个 Exception,所以它总是抛出最新的 Exception。换句话说,如果 catchfinally块都试图抛出 Exception,那么 catch 中的 Exception就是 吞下去,并且只抛出 finally中的异常。

因此,在这个程序中,异常 MyEx2被吞噬,MyEx1被抛出。这个异常被抛出 main(),不再被捕获,因此 JVM 停止,最终输出为 132Exception in thread main MyExc1

实际上,如果在 try/catch子句中有一个 finally,那么将执行 finally捕获异常后,而不是 在引发任何捕获的异常之前最终只会抛出最后一个异常