Exceção lançada na captura e finalmente cláusula

155

Em uma pergunta para Java na universidade, havia este trecho de código:

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

Me pediram para dar sua saída. Eu respondi 13Exception in thread main MyExc2, mas a resposta correta é 132Exception in thread main MyExc1. Por que é isso? Eu simplesmente não consigo entender para onde MyExc2vai.

Jubstuff
fonte

Respostas:

167

Com base na leitura de sua resposta e na probabilidade de sua elaboração, acredito que você acha que uma "exceção em andamento" tem "precedência". Tenha em mente:

Quando uma nova exceção é lançada em um bloco catch ou finalmente no bloco que será propagado para fora desse bloco, a exceção atual será abortada (e esquecida) à medida que a nova exceção for propagada para fora. A nova exceção começa a desenrolar a pilha como qualquer outra exceção, abortando o bloco atual (o bloco catch ou finalmente) e sujeita a qualquer catch ou bloco aplicável ao longo do caminho.

Observe que os blocos catch ou finalmente aplicáveis incluem:

Quando uma nova exceção é lançada em um bloco de captura, a nova exceção ainda está sujeita ao bloco finalmente da captura, se houver.

Agora refaça a execução lembrando que, sempre que você acertar throw, você deve interromper o rastreamento da exceção atual e começar a rastrear a nova exceção.

Bert F
fonte
7
«Com base na leitura da sua resposta e vendo como você provavelmente a encontrou, acredito que você acha que uma" exceção em andamento "tem" precedência "» Obrigado ... esse foi exatamente o meu pensamento :)
Jubstuff
39

Isto é o que a Wikipedia diz sobre finalmente a cláusula:

Mais comum é uma cláusula relacionada (finalmente, ou assegura) que é executada independentemente de uma exceção ocorrer ou não, geralmente para liberar recursos adquiridos no corpo do bloco de tratamento de exceções.

Vamos dissecar seu programa.

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

Então, 1será emitido na tela e, em seguida, q()é chamado. Em q(), uma exceção é lançada. A exceção é capturada, Exception ymas não faz nada. Uma cláusula finalmente é executada (precisa), portanto, 3será impressa na tela. Como (no método q()há uma exceção lançada na cláusula finally , também o q()método passa a exceção para a pilha pai (pela throws Exceptiondeclaração na método) new Exception()será lançada e capturada por catch ( Exception i ), a MyExc2exceção será lançada (por enquanto, adicione-a à pilha de exceções ), mas um finalmente no mainbloco será executado primeiro.

Então,

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

Uma cláusula finalmente é chamada ... (lembre-se, acabamos de capturar Exception ie lançar MyExc2) em essência, 2é impressa na tela ... e após a 2impressão na tela, uma MyExc1exceção é lançada. MyExc1é tratado pelo public static void main(...)método

Resultado:

"132Exceção no segmento principal MyExc1"

Palestrante está correto! :-)

Em essência , se você tiver uma cláusula finalmente em uma tentativa / captura, uma finalmente será executada ( após capturar a exceção antes de lançar a exceção capturada)

Buhake Sindi
fonte
O catché executado desde que q()lançou um Exceptionde seu próprio finallybloco.
Péter Török
"Em q (), uma exceção é lançada, mas antes que a exceção seja totalmente lançada, uma cláusula finalmente é executada primeiro, portanto, 3 será impressa na tela." Er ... não, a primeira exceção lançada qpassa a execução para o catchbloco vazio q(que engole essa exceção) e depois no finallybloco q. O dito finalmente bloqueia as impressões e 3, em seguida, lança uma nova exceção, que graças a q's throws Exceptioné passada para cima pela pilha para o pai.
Powerlord 23/09/10
38

As exceções no bloco final substituem as exceções no bloco de captura.

Citando a partir da edição Java Language Specification 14 :

Se o bloco catch for concluído abruptamente pelo motivo R, o bloco final será executado. Depois, há uma escolha:

  • Se o bloco final for concluído normalmente, a instrução try será concluída abruptamente pelo motivo R.

  • Se o bloco final for concluído abruptamente pelo motivo S, a instrução try será concluída abruptamente pelo motivo S (e o motivo R será descartado).

Roland
fonte
21

Finalmente, a cláusula é executada mesmo quando a exceção é lançada de qualquer lugar no bloco try / catch.

Como é o último a ser executado no maine gera uma exceção, essa é a exceção que os chamadores veem.

Daí a importância de garantir que a finallycláusula não atinja nada, pois ela pode engolir exceções do trybloco.

Alexander Pogrebnyak
fonte
5
Também será executada mesmo que não há nenhuma exceção lançada no bloco try / catch
nanda
2
+1: Direto e direto ao ponto sem diminuir a pilha inteira que o OP já parece entender.
Powerlord 23/09/10
9

A methodnão pode throwduas exceções ao mesmo tempo. Ele sempre jogará o último arremesso exception, que neste caso será sempre o do finallybloco.

Quando a primeira exceção do método q()é lançada, ela será capturada e depois engolida pela exceção finalmente lançada pelo bloco.

q () -> jogado new Exception -> main catch Exception -> throw new Exception -> finally jogue um novo exception(e aquele do catché "perdido")

Garis M Suero
fonte
3

A maneira mais fácil de pensar nisso é imaginar que haja uma variável global em todo o aplicativo que esteja mantendo a exceção atual.

Exception currentException = null;

À medida que cada exceção é lançada, "currentException" é definido para essa exceção. Quando o aplicativo termina, se currentException for! = Null, o tempo de execução relata o erro.

Além disso, os blocos finalmente sempre são executados antes da saída do método. Você pode solicitar o snippet de código para:

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
        }
    }
}

A ordem em que o aplicativo é executado é:

main()
{
  try
    q()
    {
      try
      catch
      finally
    }
  catch
  finally
}
CodingWithSpike
fonte
1

É sabido que o bloco final é executado após o try and catch e sempre é executado ... Mas como você viu, é um pouco complicado às vezes, verifique o trecho de código abaixo e você verá que as instruções return e throw não sempre faça o que eles devem fazer na ordem que esperamos que o tema faça.

Felicidades.

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

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

Ordem:

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/

Luiz Fernando
fonte
1
Embora esse snippet de código possa ser a solução, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se que você está respondendo a pergunta para os leitores no futuro, e essas pessoas podem não saber as razões para a sua sugestão de código
Rahul Gupta
1

A lógica é clara até terminar a impressão 13. Em seguida, a exceção lançada em q()é capturado por catch (Exception i)nos main()e uma new MyEx2()está pronta para ser lançada. No entanto, antes de lançar a exceção, o finallybloco deve ser executado primeiro. Em seguida, a saída se torna 132e finallypede para lançar outra exceção new MyEx1().

Como um método não pode lançar mais de um Exception , sempre lançará o mais recente Exception. Em outras palavras, se os dois catche os finallyblocos tentarem jogar Exception, a Exceptioncaptura de entrada será engolida e somente a exceção finallyserá lançada.

Assim, neste programa, a exceção MyEx2é engolida e MyEx1lançada. Essa exceção é lançada main()e não é mais capturada, portanto, a JVM para e a saída final é 132Exception in thread main MyExc1.

Em essência, se você tiver a finallyem uma try/catchcláusula, a finallyserá executada APÓS capturar a exceção , mas antes de lançar qualquer exceção capturada , e somente a exceção mais recente seria lançada no final .

yyFred
fonte
0

Eu acho que você só precisa andar pela finally quarteirões:

  1. Imprima "1".
  2. finally no q impressão "3".
  3. finallyna mainimpressão "2".
Uwe Keim
fonte
0

Para lidar com esse tipo de situação, ou seja, manipular a exceção gerada pelo bloco finalmente. Você pode colocar o bloco final usando o bloco try: Veja o exemplo abaixo em 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"

fonte
-1

Eu acho que isso resolve o problema:

boolean allOk = false;
try{
  q();
  allOk = true;
} finally {
  try {
     is.close();
  } catch (Exception e) {
     if(allOk) {
       throw new SomeException(e);
     }
  }
}
Vouze
fonte
3
Que problema você vai "resolver"? Você quer dizer a pergunta no exame? bem, já respondeu. Se você quer dizer o problema do código fornecido, uma vez que é apenas uma pergunta do exame, não há sentido em culpar.
Earth Engine