在catch和finally子句中抛出异常。

时间:2021-01-25 20:29:26

On a question for Java at the university, there was this snippet of code:

在大学的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();
        }
    }
}

I was asked to give its output. I answered 13Exception in thread main MyExc2, but the correct answer is 132Exception in thread main MyExc1. Why is it that? I just can't understand where does MyExc2 go.

我被要求给出它的输出。在线程main MyExc2中,我回答了13个异常,但是正确的答案是线程main MyExc1中的132Exception。为什么?我只是不明白我的理由去哪儿了。

10 个解决方案

#1


116  

Based on reading your answer and seeing how you likely came up with it, I believe you think an "exception-in-progress" has "precedence". Keep in mind:

基于阅读你的答案,看看你是如何想出答案的,我相信你认为“进步”是有“优先”的。请记住:

When an new exception is thrown in a catch block or finally block that will propagate out of that block, then the current exception will be aborted (and forgotten) as the new exception is propagated outward. The new exception starts unwinding up the stack just like any other exception, aborting out of the current block (the catch or finally block) and subject to any applicable catch or finally blocks along the way.

当在catch块中抛出一个新的异常,或者最终在该块中传播时,当新的异常向外传播时,当前异常将被终止(并被遗忘)。新的异常开始像任何其他异常一样将堆栈展开,从当前块(catch或finally块)中退出,并以任何适用的捕获或最后的阻塞为主题。

Note that applicable catch or finally blocks includes:

注意,适用的catch或finally块包括:

When a new exception is thrown in a catch block, the new exception is still subject to that catch's finally block, if any.

当一个新的异常被抛出一个catch块时,这个新的异常仍然会受到那个catch的finally块(如果有的话)的约束。

Now retrace the execution remembering that, whenever you hit throw, you should abort tracing the current exception and start tracing the new exception.

现在重新跟踪执行过程,记住,无论何时单击抛出,都应该中止跟踪当前异常,并开始跟踪新的异常。

#2


34  

This is what Wikipedia says about finally clause:

这就是*最后的条款:

More common is a related clause (finally, or ensure) that is executed whether an exception occurred or not, typically to release resources acquired within the body of the exception-handling block.

更常见的是一个相关的子句(最后,或确保),不管是否发生异常,通常是释放在异常处理块的主体内获得的资源。

Let's dissect your program.

让我们仔细分析程序。

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

So, 1 will be output into the screen, then q() is called. In q(), an exception is thrown. The exception is then caught by Exception y but it does nothing. A finally clause is then executed (it has to), so, 3 will be printed to screen. Because (in method q() there's an exception thrown in the finally clause, also q() method passes the exception to the parent stack (by the throws Exception in the method declaration) new Exception() will be thrown and caught by catch ( Exception i ), MyExc2 exception will be thrown (for now add it to the exception stack), but a finally in the main block will be executed first.

所以,1将输出到屏幕,然后q()被调用。在q()中抛出异常。异常会被异常的y捕获,但是它什么也不做。最后一个子句被执行(它必须),所以,3将被打印到屏幕上。因为(q()方法抛出一个异常的最终条款,还问()方法将异常传递给父堆栈(在方法声明抛出异常)()方法将抛出新的异常,被抓(除了我),将抛出MyExc2异常(现在将它添加到异常堆栈),但最后在主块将首先执行。

So in,

所以,

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

A finally clause is called...(remember, we've just caught Exception i and thrown MyExc2) in essence, 2 is printed on screen...and after the 2 is printed on screen, a MyExc1 exception is thrown. MyExc1 is handled by the public static void main(...) method.

最后一个条款叫做……(记住,我们刚刚发现了一个例外,我把MyExc2扔在了屏幕上。)在屏幕上打印2后,将抛出MyExc1异常。MyExc1由公共静态void main(…)方法处理。

Output:

输出:

"132Exception in thread main MyExc1"

"132Exception在线程主要MyExc1"

Lecturer is correct! :-)

讲师是正确的!:-)

In essence, if you have a finally in a try/catch clause, a finally will be executed (after catching the exception before throwing the caught exception out)

在本质上,如果您最终有一个try/catch子句,那么最终将被执行(在捕获异常之后,将捕获的异常抛出)

#3


26  

Quoting from the JLS 9: 14.20.2. Execution of try-finally and try-catch-finally

引用JLS 9: 14.20.2。最后,尝试和尝试的执行。

If the catch block completes abruptly for reason R, then the finally block is executed. Then there is a choice:

如果catch块突然结束,因为原因R,那么最后一个块将被执行。还有一个选择:

  • If the finally block completes normally, then the try statement completes abruptly for reason R.

    如果最后一个块正常完成,那么try语句就会突然结束,原因是R。

  • If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded).

    如果最后一个块突然完成了原因,那么try语句就会突然结束,原因是S(和原因R被丢弃)。

#4


17  

Finally clause is executed even when exception is thrown from anywhere in try/catch block.

最后,即使在try/catch块中抛出异常时,也会执行该子句。

Because it's the last to be executed in the main and it throws an exception, that's the exception that the callers see.

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

Hence the importance of making sure that the finally clause does not throw anything, because it can swallow exceptions from the try block.

因此,确保finally子句不抛出任何东西的重要性,因为它可以从try块中捕获异常。

#5


6  

A method can't throw two exceptions at the same time. It will always throw the last thrown exception, which in this case it will be always the one from the finally block.

一个方法不能同时抛出两个异常。它总是抛出最后一个抛出的异常,在本例中,它始终是来自finally块的那个。

When the first exception from method q() is thrown, it will catch'ed and then swallowed by the finally block thrown exception.

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

q() -> thrown new Exception -> main catch Exception -> throw new Exception -> finally throw a new exception (and the one from the catch is "lost")

q() ->抛出一个新的异常->主要捕获异常->抛出新的异常->最终抛出一个新的异常(并且从catch中得到的一个是“丢失的”)

#6


2  

The easiest way to think of this is imagine that there is a variable global to the entire application that is holding the current exception.

考虑这一点的最简单的方法是,假设在整个应用程序中有一个全局变量,它持有当前异常。

Exception currentException = null;

As each exception is thrown, "currentException" is set to that exception. When the application ends, if currentException is != null, then the runtime reports the error.

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

Also, the finally blocks always run before the method exits. You could then requite the code snippet to:

另外,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
        }
    }
}

The order in which the application executes is:

应用程序执行的顺序为:

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

#7


1  

It is well known that the finally block is executed after the the try and catch and is always executed.... But as you saw it's a little bit tricky sometimes check out those code snippet below and you will that the return and throw statements don't always do what they should do in the order that we expect theme to.

众所周知,执行finally块后试图抓住,总是执行....但是,当你看到它有点棘手时,有时候你可以看看下面的代码片段,你会发现return和throw语句并不总是按照我们期望的顺序来做它们应该做的事情。

Cheers.

欢呼。

/////////////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!";

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

#8


0  

I think you just have to walk the finally blocks:

我想你只需要走最后一条路:

  1. Print "1".
  2. 打印“1”。
  3. finally in q print "3".
  4. 最后在q打印“3”。
  5. finally in main print "2".
  6. 最后在主打印“2”。

#9


0  

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();
    }
}

Order:

顺序:

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/

https://www.compilejava.net/

#10


-1  

I think this solve the problem :

我认为这解决了问题:

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

#1


116  

Based on reading your answer and seeing how you likely came up with it, I believe you think an "exception-in-progress" has "precedence". Keep in mind:

基于阅读你的答案,看看你是如何想出答案的,我相信你认为“进步”是有“优先”的。请记住:

When an new exception is thrown in a catch block or finally block that will propagate out of that block, then the current exception will be aborted (and forgotten) as the new exception is propagated outward. The new exception starts unwinding up the stack just like any other exception, aborting out of the current block (the catch or finally block) and subject to any applicable catch or finally blocks along the way.

当在catch块中抛出一个新的异常,或者最终在该块中传播时,当新的异常向外传播时,当前异常将被终止(并被遗忘)。新的异常开始像任何其他异常一样将堆栈展开,从当前块(catch或finally块)中退出,并以任何适用的捕获或最后的阻塞为主题。

Note that applicable catch or finally blocks includes:

注意,适用的catch或finally块包括:

When a new exception is thrown in a catch block, the new exception is still subject to that catch's finally block, if any.

当一个新的异常被抛出一个catch块时,这个新的异常仍然会受到那个catch的finally块(如果有的话)的约束。

Now retrace the execution remembering that, whenever you hit throw, you should abort tracing the current exception and start tracing the new exception.

现在重新跟踪执行过程,记住,无论何时单击抛出,都应该中止跟踪当前异常,并开始跟踪新的异常。

#2


34  

This is what Wikipedia says about finally clause:

这就是*最后的条款:

More common is a related clause (finally, or ensure) that is executed whether an exception occurred or not, typically to release resources acquired within the body of the exception-handling block.

更常见的是一个相关的子句(最后,或确保),不管是否发生异常,通常是释放在异常处理块的主体内获得的资源。

Let's dissect your program.

让我们仔细分析程序。

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

So, 1 will be output into the screen, then q() is called. In q(), an exception is thrown. The exception is then caught by Exception y but it does nothing. A finally clause is then executed (it has to), so, 3 will be printed to screen. Because (in method q() there's an exception thrown in the finally clause, also q() method passes the exception to the parent stack (by the throws Exception in the method declaration) new Exception() will be thrown and caught by catch ( Exception i ), MyExc2 exception will be thrown (for now add it to the exception stack), but a finally in the main block will be executed first.

所以,1将输出到屏幕,然后q()被调用。在q()中抛出异常。异常会被异常的y捕获,但是它什么也不做。最后一个子句被执行(它必须),所以,3将被打印到屏幕上。因为(q()方法抛出一个异常的最终条款,还问()方法将异常传递给父堆栈(在方法声明抛出异常)()方法将抛出新的异常,被抓(除了我),将抛出MyExc2异常(现在将它添加到异常堆栈),但最后在主块将首先执行。

So in,

所以,

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

A finally clause is called...(remember, we've just caught Exception i and thrown MyExc2) in essence, 2 is printed on screen...and after the 2 is printed on screen, a MyExc1 exception is thrown. MyExc1 is handled by the public static void main(...) method.

最后一个条款叫做……(记住,我们刚刚发现了一个例外,我把MyExc2扔在了屏幕上。)在屏幕上打印2后,将抛出MyExc1异常。MyExc1由公共静态void main(…)方法处理。

Output:

输出:

"132Exception in thread main MyExc1"

"132Exception在线程主要MyExc1"

Lecturer is correct! :-)

讲师是正确的!:-)

In essence, if you have a finally in a try/catch clause, a finally will be executed (after catching the exception before throwing the caught exception out)

在本质上,如果您最终有一个try/catch子句,那么最终将被执行(在捕获异常之后,将捕获的异常抛出)

#3


26  

Quoting from the JLS 9: 14.20.2. Execution of try-finally and try-catch-finally

引用JLS 9: 14.20.2。最后,尝试和尝试的执行。

If the catch block completes abruptly for reason R, then the finally block is executed. Then there is a choice:

如果catch块突然结束,因为原因R,那么最后一个块将被执行。还有一个选择:

  • If the finally block completes normally, then the try statement completes abruptly for reason R.

    如果最后一个块正常完成,那么try语句就会突然结束,原因是R。

  • If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded).

    如果最后一个块突然完成了原因,那么try语句就会突然结束,原因是S(和原因R被丢弃)。

#4


17  

Finally clause is executed even when exception is thrown from anywhere in try/catch block.

最后,即使在try/catch块中抛出异常时,也会执行该子句。

Because it's the last to be executed in the main and it throws an exception, that's the exception that the callers see.

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

Hence the importance of making sure that the finally clause does not throw anything, because it can swallow exceptions from the try block.

因此,确保finally子句不抛出任何东西的重要性,因为它可以从try块中捕获异常。

#5


6  

A method can't throw two exceptions at the same time. It will always throw the last thrown exception, which in this case it will be always the one from the finally block.

一个方法不能同时抛出两个异常。它总是抛出最后一个抛出的异常,在本例中,它始终是来自finally块的那个。

When the first exception from method q() is thrown, it will catch'ed and then swallowed by the finally block thrown exception.

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

q() -> thrown new Exception -> main catch Exception -> throw new Exception -> finally throw a new exception (and the one from the catch is "lost")

q() ->抛出一个新的异常->主要捕获异常->抛出新的异常->最终抛出一个新的异常(并且从catch中得到的一个是“丢失的”)

#6


2  

The easiest way to think of this is imagine that there is a variable global to the entire application that is holding the current exception.

考虑这一点的最简单的方法是,假设在整个应用程序中有一个全局变量,它持有当前异常。

Exception currentException = null;

As each exception is thrown, "currentException" is set to that exception. When the application ends, if currentException is != null, then the runtime reports the error.

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

Also, the finally blocks always run before the method exits. You could then requite the code snippet to:

另外,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
        }
    }
}

The order in which the application executes is:

应用程序执行的顺序为:

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

#7


1  

It is well known that the finally block is executed after the the try and catch and is always executed.... But as you saw it's a little bit tricky sometimes check out those code snippet below and you will that the return and throw statements don't always do what they should do in the order that we expect theme to.

众所周知,执行finally块后试图抓住,总是执行....但是,当你看到它有点棘手时,有时候你可以看看下面的代码片段,你会发现return和throw语句并不总是按照我们期望的顺序来做它们应该做的事情。

Cheers.

欢呼。

/////////////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!";

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

#8


0  

I think you just have to walk the finally blocks:

我想你只需要走最后一条路:

  1. Print "1".
  2. 打印“1”。
  3. finally in q print "3".
  4. 最后在q打印“3”。
  5. finally in main print "2".
  6. 最后在主打印“2”。

#9


0  

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();
    }
}

Order:

顺序:

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/

https://www.compilejava.net/

#10


-1  

I think this solve the problem :

我认为这解决了问题:

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