Java/Exceções

Origem: Wikilivros, livros abertos por um mundo aberto.

Exceções (do inglês Exception, acrônimo de Exceptional events) são objetos que sinalizam que ocorreu algum problema no tempo de execução de um programa. Tais problemas podem ocorrer ao acessar um índice de vetor que esteja além de seu tamanho, ao tentar acessar algum membro de uma referência nula, ao dividir um número por zero, entre outros.

Ao ocorrer uma exceção, a JVM cria uma instância de um objeto da classe Exception ou de uma de suas subclasses (o que é chamado de lançamento de exceção) e encerra a execução da aplicação caso essa exceção não seja tratada (o que é chamado de captura de exceção).

A instrução catch lida com as exceções permitindo ao programa a capacidade de manter sua execução consistente ainda que exceções ocorram.


Throwable[editar | editar código-fonte]

A classe Throwable é a superclasse de Exception e Error e indiretamente de RuntimeException.

O argumento das instruções throws, throw e da cláusula de catch só pode ser do tipo Throwable.

Quando a JVM lança uma instância de Throwable ou uma de suas subclasses, nesse objeto há dos dados da pilha de chamada de métodos que foi desencadeada até esse lançamento ocorrer. É chamada de pilha de rastreamento.

Exception in thread "main" java.lang.RuntimeException
	at Teste.metodoInterno(Teste.java:8)
	at Teste.metodoExterno(Teste.java:4)
	at Teste.main(Teste.java:12)

Exceções não checadas[editar | editar código-fonte]

São exceções que estendem a classe RuntimeException. Geralmente ocorrem de problemas na construção da lógica do algoritmo e são evitáveis.

Abaixo estão algumas das mais comuns.

ArithmeticException[editar | editar código-fonte]

    int i = 5 / 0;
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at Teste.main(Teste.java:4)

ArrayIndexOutOfBoundsException[editar | editar código-fonte]

    char[] vetor = {'0', '1', '2'};
		char c = vetor[3];
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
	at Teste.main(Teste.java:5)


NullPointerException[editar | editar código-fonte]

    String s = null;
    s.isEmpty();
Exception in thread "main" java.lang.NullPointerException
	at Teste.main(Teste.java:5)

Exceções checadas[editar | editar código-fonte]

São exceções que estendem a classe Exception, com exceção da classe RuntimeException e suas subclasses.

Erros[editar | editar código-fonte]

Além das exceções, há também a possibilidade de lançamento de erros. Erros são objetos da classe Error. Embora a ocorrência de erros seja muito mais remota, no geral, são irreparáveis e causam o término abrupto da execução de um programa.

Assim como as exceções, os erros podem ser capturados pela instrução catch porém não é aconselhado (pois?). Assim sendo, os erros serão abordados nas explicações de forma secundária.

Em relação às regras de lançamento, declaração e captura de exceções, os erros tem as mesmas características de exceções não checadas.

Instrução throws[editar | editar código-fonte]

A instrução throws declara na assinatura do método que há a possibilidade de ser lançada uma exceção em seu bloco de instruções. Além de exceções,Throwable e qualquer outra de suas subclasses podem ser utilizadas.

void metodoQualquer() throws Throwable {}

Apesar de throws indicar que um método pode lançar uma exceção, o compilador não exige que efetivamente se tenha uma exceção sendo lançada.

void metodoQualquer() throws Exception {}

A instrução 'throws' é requerida para lançar, dentro do bloco de instruções, exceções checadas e opcional para exceções não checadas e erros conforme será visto na próxima seção. Na seção relativa à try-catch-finallyserá visto que as chamadas de métodos que declaram exceções checadas precisam ser realizadas dentro de um bloco try e essas exceções devem ser capturadas em uma cláusula catch.

Instrução throw[editar | editar código-fonte]

Lança uma instância de uma exceção e interrompe o fluxo normal de execução do programa. Qualquer subclasse de Throwable pode ser utilizada.

    throw new RuntimeException();
Exception in thread "main" java.lang.RuntimeException
	at Teste.main(Teste.java:3)

O lançamento de exceções não checadas (e erros) não exige que elas sejam declaradas na assinatura do método. A utilização de throws, então, é opcional.

void metodoQualquer() {
    throw new RuntimeException(); //Exceção não checada. throws opcional.
}

Enquanto que para exceções checadas a declaração é obrigatória.

void metodoQualquer1() throws Exception {
    throw new Exception(); // Exceção checada. Requer throws.
}

//NÃO COMPILA
void metodoQualquer2(){
    throw new Exception(); // Exceção checada. Requer throws.
}

Para exceções não checadas e erros o compilador não verifica se a classe da exceção declarada é covariante com a classe da exceção lançada.

void metodoQualquer() throws Error {
    throw new RuntimeException(); // Error e RuntimeException não são covariantes.
}

Já para exceções checadas é exigido pelo compilador que a exceção lançada seja covariante da exceção declarada. O contrário não é verdadeiro.

void metodoQualquer() throws Exception {
    throw new java.io.IOException(); // Covariantes. Exception é superclasse de IOException.
}

// NÃO COMPILA
void metodoQualquer2() throws java.io.IOException {
    throw new Exception(); // IOException não é superclasse de Exception, e sim subclasse.
}

Em resumo, quando throw lança uma instância de exceção checada é necessário declará-la com throws, o que é opcional para exceções não checadas e erros.

Instruções try-catch-finally[editar | editar código-fonte]

As instruções try-catch, try-finally ou try-finally são utilizadas para controlar o fluxo de um trecho do programa que pode lançar exceção, e no caso de uma exceção ser lançada executar determinados blocos de instruções.

try[editar | editar código-fonte]

A instrução try é utilizada para delimitar um bloco em que métodos chamados podem lançar exceções a serem tratadas. Essas exceções lançadas podem ser capturadas por uma cláusula catch, e em seu bloco pode ser desenvolvido código para tratamento das exceções lançadas.

Encadeado à try deve sempre haver ou catch ou finally.

try {} ///NÃO COMPILA!

try {} catch(Throwable t){}

try {} finally {}

Caso após try haja catch e finally a ordem sempre deve ser try-catch-finally

try {} catch(Throwable t){} finally {}

try {} finally{} catch(Throwable t){} //NÃO COMPILA!


catch[editar | editar código-fonte]

A instrução catch captura exceções e erros lançados dentro de try e possibilita o desenvolvimento de código para tratá-los.

Métodos que declarem exceções checadas devem sempre ser chamados ou dentro de try e ter uma cláusula catch com um tipo covariante ou dentro de métodos que relancem a exceção.

public static void metodoComExcecao() throws Exception {}

public static void main(String[] args) {
    try {
        metodoComExcecao();
    } catch(Exception e) {}
}

No caso do método chamador relançar a exceção, quando chamado deverá estar dentro de try-catch ou dentro de outro método que também relance a exceção.

public static void metodoComExcecao() throws Exception {}

public static void metodoRelancandoExcecao() throws Exception {
    metodoComExcecao();
}

public static void main(String[] args) {
    try {
        metodoRelancandoExcecao();
    } catch (Exception e) {}
}

O método chamado em main() abaixo não compila pois lança uma exceção checada e não é chamado dentro de um try-catch e nem main() relança a exceção.

Evite incluir throws na declaração de main()!

public static metodoComExcecao() throws Exception {}

public static void main(String[] args) {
        metodoComExcecao(); //NAO COMPILA! 
}

Se há mais de uma cláusula catch elas deverão ser dispostas da subclasse mais específica até a subclasse mais abrangente.

try {} 
catch (IllegalArgumentException e) {} //IllegalArgumentException é subclasse de RuntimeException.
catch (RuntimeException e) {}         //RuntimeException é subclasse de Exception.
catch (Exception e) {}

O código abaixo não compilará por não observar a regra. IllegalArgumentException é subclasse de RuntimeException porém está na cláusula acima.

try {} 
catch (RuntimeException e) {}           
catch (IllegalArgumentoException) {}    //NÃO COMPILA!
catch (Exception e) {}

finally[editar | editar código-fonte]

A instrução finally permite que um bloco seja sempre executado após a execução de um bloco de catch e/ou de try.

try {
    throw new Exception();
} catch (Exception e) {
    System.out.println("Dentro de catch.");    
} finally {
    System.out.println("Dentro de finally.");
}
Dentro de catch.
Dentro de finally.

No código abaixo, o rastreamento de pilha da exceção lançada por catch() é exibido após o println() devido à prioridade característica de finally em sempre ser executado.

try {
    throw new Exception();
} catch (Exception e) {
    throw new RuntimeException();
} finally {
    System.out.println("É exibido ainda que catch lance uma exceção.");
}

Caso o bloco de finally fosse removido, teria-se apenas a exibição do rastreamento de pilha da exceção.

É exibido ainda que catch lance uma exceção.
Exception in thread "main" java.lang.RuntimeException
	at estudando.excecoes.Teste.main(Teste.java:9)

Como finally sempre executa há a possibilidade de uma exceção lançada em catch ser engolida pela JVM.

try {
    throw new Exception();
} catch (Exception e) {
    throw new RuntimeException("dentro de catch");
} finally {
    throw new RuntimeException("dentro de finally");
}
Exception in thread "main" java.lang.RuntimeException: dentro de finally
	at estudando.excecoes.Teste.main(Teste.java:11)

Há uma exceção na regra de finally sempre executar após catch que ocorre quando em catch é executado o método System.exit(). Esse método executa o encerramento da JVM.

try {
    throw new Exception();
} catch (Exception e) {
    System.out.println("Última execução antes da JVM desligar.");
    System.exit(0);
} finally {
    System.out.println("Não será executado.");
}
Última execução antes da JVM desligar.