在catch和finally子句中引发异常


155

在大学里一个关于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去哪里。

Answers:


167

基于阅读您的答案并了解您可能的想法,我相信您认为“进行中的例外”具有“优先权”。记住:

当一个新的异常被抛出到一个catch块或将要传播到该块之外的finally块中时,当新的异常向外传播时,当前异常将被中止(并被遗忘)。与其他任何异常一样,新异常开始释放堆栈,退出当前块(catch或finally块),并在此过程中受到任何适用的catch或finally块的约束。

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

当在catch块中引发新异常时,新异常仍受该catch的finally块(如果有)的影响。

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


7
«基于阅读您的答案并了解您可能的想法,我相信您认为“进行中的例外”具有“优先权”»谢谢...这正是我的想法:)
Jubstuff

39

这是维基百科关于finally子句的内容:

更常见的是相关子句(最终或确保),无论是否发生异常,该子句都会执行,通常是释放在异常处理块的主体内获取的资源。

让我们剖析您的程序。

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

这样,1将输出到屏幕中,然后q()被调用。在中q(),将引发异常。然后捕获到异常,Exception y但是它什么也不做。甲最后然后子句被执行(它有),所以,3将被打印到屏幕。因为(在方法q()中,finally子句中引发q()了异常,所以throws Exception该方法new Exception()还将异常传递给父堆栈(通过方法声明中的),并被捕获catch ( Exception i )MyExc2因此将引发异常(现在将其添加到异常堆栈中),但最后main块将被首先执行。

所以在

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

一个最后条款被称为...(请记住,我们只是抓住了Exception i,扔MyExc2)在本质上,2是印在屏幕上......而之后2被显示在屏幕上,一个MyExc1异常被抛出。MyExc1由该public static void main(...)方法处理。

输出:

“线程主MyExc1中的132Exception”

讲师是正确的!:-)

从本质上说,如果你有一个最终在一个try / catch子句,终于将(执行捕获异常之前抛出捕获的异常出)


catch因为从其自身的块中q()抛出,所以执行。Exceptionfinally
彼得Török

“在q()中,引发了一个异常,但是在完全引发该异常之前,将首先执行finally子句,因此,将在屏幕上显示3。”呃……不,第一个引发的异常q将执行传递给空catchq(吞下此异常),然后移到中的finallyq。所述最后块打印3,然后引发一个新的异常,这多亏qthrows Exception向上传递堆栈到母体。
Powerlord 2010年

38

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

引用Java语言规范14版

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

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

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


21

即使从try / catch块中的任何位置抛出异常,都将执行finally子句。

由于这是最后一个要执行的操作,main并且会引发异常,因此调用者会看到该异常。

因此,确保该finally子句不抛出任何内容的重要性,因为它会吞噬该try块中的异常。


5
它也将执行即使在try / catch块抛出也不例外
南大

2
+1:指向并指向要点,而不要曲折OP已经看起来可以理解的整个堆栈。
Powerlord 2010年

9

A method不能同时出现throw两个异常。它将始终抛出最后一个抛出的东西exception,在这种情况下,它将始终是该finally块中的那个。

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

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


3

想到这一点的最简单方法是,想象整个应用程序中有一个全局变量来保存当前异常。

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
}

1

众所周知,finally块是在try和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!";

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

1
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/


1
尽管此代码段可能是解决方案,但包括说明确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而那些人可能不知道您提出代码建议的原因
Rahul Gupta

1

逻辑清楚直到完成打印13。然后抛出的异常q()被捕获catch (Exception i)main()和一个new MyEx2()准备被抛出。但是,在引发异常之前,finally必须先执行该块。然后输出变为132finally要求引发另一个异常new MyEx1()

由于一种方法不能抛出一个以上的异常Exception,因此它总是抛出最新的异常Exception。换句话说,如果catchfinally块都尝试抛出Exception,则Exceptionin catch会被吞下,并且只会抛出in finally中的异常。

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

本质上,如果finallytry/catch子句中有一个a ,finally则在捕获到异常之后将执行a ,但是在抛出任何捕获到的异常之前将只在最后抛出最后一个异常


0

我认为您只需要走finally个障碍:

  1. 打印“ 1”。
  2. finallyq打印“3”。
  3. finallymain打印“2”。

0

为了处理这种情况,即处理由finally块引发的异常。您可以通过try块来包围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"

-1

我认为这可以解决问题:

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

3
您要“解决”什么问题?你是说考试中的问题吗?好吧,它已经回答了。如果您是指给定代码的问题,则由于这只是一个考试问题,因此没有任何责备之嫌。
Earth Engine
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.