Exceção lançada dentro do bloco de captura - será capturada novamente?

180

Isso pode parecer uma questão de programação e eu pensei que sabia a resposta, mas agora me vejo precisando checar novamente. Neste trecho de código abaixo, a exceção lançada no primeiro bloco de captura será capturada pelo bloco de exceção geral abaixo?

try {
  // Do something
} catch(IOException e) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

Eu sempre pensei que a resposta seria não, mas agora tenho um comportamento estranho que pode ser causado por isso. A resposta é provavelmente a mesma para a maioria das linguagens, mas estou trabalhando em Java.

roryf
fonte
2
Talvez você possa descrever o "comportamento estranho"?
Jeffrey L Whitledge
Você tem certeza de que o ApplicationException não está sendo lançado em outro lugar e se propagando até esse bloco?
sblundy
Percebi isso no meu IDE Eclipse. É difícil me forçar a colocar a "nova exceção de lançamento" em um bloco de tentativa, mas não sei por quê. Eu fiz isso no passado sem fazer isso. Não vejo por que um bloco try seria necessário. Muitos exemplos no Google mostram pessoas que não precisam de um bloco de tentativa. É porque estou jogando dentro de uma declaração if?
djangofan

Respostas:

214

Não, pois o novo thrownão está no trybloco diretamente.

Chris Jester-Young
fonte
Digamos que se o código é assim, tente {} catch (Exceção e) {System.err.println ("In catch Exception:" + e.getClass ()); } catch (IOException e) {System.err.println ("Na captura IOException:" + e.getClass ()); } e o código no bloco try gera a exceção de E / S. Irá para o bloco de exceção geral imediato ou passará para o bloco de captura de IOException?
precisa saber é o seguinte
4
@ user3705478 O código não será compilado, para evitar esse tipo de situação incorreta. Em geral, você não tem permissão para capturar uma subclasse após a captura de sua superclasse no mesmo bloco try.
Chris Jester-Young
Obrigado. Então, receberei um erro de tempo de compilação, certo? Vou testá-lo quando chegar em casa.
precisa saber é o seguinte
Isso depende de @ user3705478, se um RuntimeExceptionfor lançado do catchbloco, não haverá erro de compilação.
Randy Dev
@AndrewDunn Acho que não é disso que se trata a questão de user3705478, mas o que acontece se uma catchcláusula de exceção pai for listada antes de uma catchcláusula de exceção filho . Meu entendimento é que o Java não permite isso e é capturado em tempo de compilação.
Chris Jester-Young
71

Não. É muito fácil verificar.

public class Catch {
    public static void main(String[] args) {
        try {
            throw new java.io.IOException();
        } catch (java.io.IOException exc) {
            System.err.println("In catch IOException: "+exc.getClass());
            throw new RuntimeException();
        } catch (Exception exc) {
            System.err.println("In catch Exception: "+exc.getClass());
        } finally {
            System.err.println("In finally");
        }
    }
}

Deve imprimir:

Em captura IOException: classe java.io.IOException
Finalmente
Exceção no encadeamento "main" java.lang.RuntimeException
        em Catch.main (Catch.java:8)

Tecnicamente, isso poderia ter sido um bug do compilador, dependente da implementação, comportamento não especificado ou algo assim. No entanto, o JLS é muito bem definido e os compiladores são bons o suficiente para esse tipo de coisa simples (a caixa de canto genérica pode ser uma questão diferente).

Observe também que, se você trocar os dois blocos catch, ele não será compilado. A segunda captura seria completamente inacessível.

Observe que o bloco final sempre é executado mesmo que um bloco coletor seja executado (exceto casos estúpidos, como loops infinitos, anexando-se à interface das ferramentas e eliminando o encadeamento, reescrevendo o código de código, etc.).

Tom Hawtin - linha de orientação
fonte
3
A maneira mais óbvia de evitar um finallyé, é claro, ligar System.exit. :-P
Chris Jester-Young
3
@ Chris Jester-Young for(;;);é mais curto, contido na linguagem, não apresenta muitos efeitos colaterais e, para mim, mais óbvio.
Tom Hawtin - defina
1
System.exité mais amigável para a CPU! : -O Mas sim, tudo bem, claramente esse é um critério subjetivo. Além disso, eu não sabia que você era um jogador de código. ;-)
Chris Jester-Young
28

A especificação da linguagem Java diz na seção 14.19.1:

Se a execução do bloco try for concluída abruptamente devido a um lançamento de um valor V, haverá uma opção:

  • Se o tipo de tempo de execução de V for atribuível ao Parâmetro de qualquer cláusula catch da instrução try, a primeira cláusula catch (mais à esquerda) será selecionada. O valor V é atribuído ao parâmetro da cláusula catch selecionada e o bloco dessa cláusula catch é executado. Se esse bloco for concluído normalmente, a instrução try será concluída normalmente; se esse bloco for concluído abruptamente por qualquer motivo, a instrução try será concluída abruptamente pelo mesmo motivo.

Referência: http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#24134

Em outras palavras, a primeira captura anexa que pode lidar com a exceção, e se uma exceção é lançada fora dessa captura, isso não está no escopo de nenhuma outra captura para a tentativa original, portanto, eles não tentarão lidar com isso.

Uma coisa relacionada e confusa a saber é que em uma estrutura try- [catch] -finally, um bloco finalmente pode lançar uma exceção e, nesse caso, qualquer exceção lançada pelo bloco try ou catch é perdida. Isso pode ser confuso na primeira vez que você o vê.

Alex Miller
fonte
Só queria acrescentar que, desde o Java 7, você pode evitar isso usando try-with-resources. Então, se tryAND finallylança ambos, finallyé suprimido, mas também é ADICIONADO à exceção de try. Se catchjogar também, você não terá sorte, a menos que você lide com isso por meio de 'addSuppressed' e adicionando a tryexceção - então você tem os três.
LAFK diz Restabelecer Monica
6

Se você deseja lançar uma exceção do bloco catch, deve informar seu método / classe / etc. que ele precisa lançar a exceção. Igual a:

public void doStuff() throws MyException {
    try {
        //Stuff
    } catch(StuffException e) {
        throw new MyException();
    }
}

E agora seu compilador não gritará com você :)

Mastergeek
fonte
4

Não - como Chris Jester-Young disse, ele será lançado para a próxima tentativa na hierarquia.

Ian P
fonte
2

Como dito acima ...
Eu acrescentaria que, se você tiver problemas para ver o que está acontecendo, se não conseguir reproduzir o problema no depurador, poderá adicionar um rastreio antes de lançar novamente a nova exceção (com o bom e antigo System .out.println, na pior das hipóteses, com um bom sistema de log como o log4j).

PhiLho
fonte
2

Não será capturado pelo segundo bloco de captura. Cada exceção é capturada apenas quando dentro de um bloco try. Você pode aninhar tentativas (não que seja uma boa ideia em geral):

try {
    doSomething();
} catch (IOException) {
   try {
       doSomething();
   } catch (IOException e) {
       throw new ApplicationException("Failed twice at doSomething" +
       e.toString());
   }          
} catch (Exception e) {
}
Vinko Vrsalovic
fonte
Eu escrevi um código semelhante. Mas não estou convencido. Eu quero chamar um Thread.sleep () no meu bloco de captura. Mas Thread.sleep lança uma InterruptedException. É correto (uma prática recomendada) fazê-lo como você mostrou no seu exemplo?
Riroo 3/17
1

Não, uma vez que todas as capturas se referem ao mesmo bloco de tentativa, portanto, jogar de dentro de um bloco de captura seria capturado por um bloco de tentativa anexo (provavelmente no método que chamou esse)

Uri
fonte
-4

Postagem antiga, mas a variável "e" deve ser única:

try {
  // Do something
} catch(IOException ioE) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}
Ted K
fonte
5
Isso deve ser um comentário e não uma resposta. Ela não fornece nada para abordar a questão real - é apenas uma crítica à questão dos POs.
Derek W