Exceções são situações inválidas que ocorrem durante o processamento e impedem que o programa continue seu fluxo normal até que a situação seja de algum modo resolvida.

Nas linguagens mais antigas, os programas tentavam evitar que estas situações ocorressem através de testes de validação e fazendo o processamento apenas quando todas as condições fossem válidas. Esse modo de programar funciona, mas deixa o código inchado e menos legível já que, conforme o caso, uma grande quantidade de testes pode ser necessária antes de qualquer processamento.

Na programação moderna, o código é escrito sem preocupação com as condições de erro. Quando ocorre uma situação inválida, o sistema emite (lança) uma exceção e então o programa tem a opção de tratar essa ocorrência.

Mecanismo das exceções

Como tudo no java, exceções são instâncias de classes. Quando um método ou o sistema de detecta uma situação de exceção, uma instância da classe de exceção correspondente é criada e "lançada" no programa.

Para entender melhor o mecanismo usado, é preciso entender o conceito de pilha de execução. A qualquer momento do processamento existe sempre uma "pilha" de métodos ou funções em execução.

Por exemplo, um programa Java começa executando o método main da classe principal. Dentro do método main serão chamados outros métodos de objetos criados ali ou de classes já existentes no Java. Estes métodos chamarão outros métodos, que chamarão outros, e assim por diante, constituindo uma pilha.

Sempre que o método A chama o método B, o método A fica parado esperando a execução do método B para poder continuar. O método B, por sua vez, também pode chamar outros métodos e também vai ter que ficar esperando a execução destes para continuar. Se uma exceção acontecer, vai acontecer sempre no método que está no topo da pilha, porque é sempre este método que está sendo executado, os outros estão em estado de espera.

Nesse caso, o método pode ou não “capturar” essa exceção. Se não capturar, o método é abortado e a execução vai para o método seguinte da pilha. Ali também a exceção pode ser capturada ou não e se não for capturada, o método também é encerrado e a execução vai para o método seguinte. Se nenhum método capturar a exceção ela chegará ao método main que, se também não capturar a exceção será encerrado, finalizando o programa.

Essa propagação da exceção é encerrada quando ela é capturada por algum método. Portanto, ou o método captura a exceção ou a exceção encerra o método.

Ciclo de vida da exceção


Quando a situação inválida ocorre ela é:
  • Criada: é criada uma instância da classe de exceção apropriada;
  • Lançada: É propagada para todos os métodos em execução no momento, iniciando pelo que estiver no topo da pilha de execução;
  • Capturada: Algum dos métodos da pilha pode capturar a exceção, geralmente para tratar a situação. Uma vez capturada a exceção não se propaga mais;
  • Tratada: Quando é possível resolver a situação inválida, o método que capturou a exceção também pode tratá-la.

Captura de exceções no Java


Usa a estrutura try-catch-finally. Cada uma destas palavras reservadas tem um bloco de comandos associado:

try{
  // bloco de comandos sujeitos a exceções
}
catch (ClasseExceção ObjExc){
  //Código de tratamento para a exceção especificada
}
finally {
  // Este bloco sempre será executado ocorrendo ou
  // não alguma exceção. Use-o para finalizar uma
  // operação que não pode ficar pendente.
}
 

Pode haver vários blocos catch, cada um com uma exceção diferente, caso ocorra uma exceção, a classe da exceção será comparada com as classes listadas pelos blocos catch, na ordem que aparecem no código. O primeiro que combinar será executado e os de baixo não serão comparados.

A classe Exception é a classe ancestral de todas as exceções, portanto sempre vai combinar com qualquer exceção. Se aparecer em um bloco catch, deve ser o último bloco, caso contrário os de baixo nunca serão comparados.

A estrutura try-catch-finally permite que ou o bloco catch ou o bloco finally sejam omitidos, mas nunca os dois. Portanto é possível usar try-finally ou try-catch.

Exemplo:
public double DivAtrib(double value){
    try{
        return Atrib/value;
    }
    catch(Exception e){
        System.out.println("Operação invalida");
    }
}
 

Método toString()
O método toString() é definido pela classe Object, que é ancestral de todas as classes no Java. Este método é usado para gerar uma representação do objeto na forma de string. Idealmente, todas as classes deveriam reescrever o método toString() para manter a representação válida.

As exceções também são descendentes de Object e também tem o método toString(). Nas exceções, este método costuma ser usado para retornar uma string explicativa sobre o que gerou a exceção.

Exemplo:
try{
    \\ código
}
catch(Exception e){
    System.out.println(e.toString());
}
 
 

Resumo try - catch – finally:
  • try: bloco com código sujeito a exceção. O bloco será executado até o ponto onde a exceção ocorreu, se ocorrer. Neste caso a execução passa para o bloco catch (se existir) e depois para o bloco finally.
  • catch: Podem haver vários blocos catch, cada um associado a uma exceção diferente. A exceção será comparada com as classes listadas nos blocos catch e o primeiro que combinar terá seu código executado. Apenas um bloco catch será executado. A classe Exception é ancestral de todas as exceções, portanto combina com qualquer exceção.
  • finally: Os comandos nesse bloco serão sempre executados, em qualquer situação, ocorrendo ou não alguma exceção.
  • Os blocos catch ou finally podem ser omitidos desde que um deles esteja presente.

Para criar e lançar exceções


Exceções são objetos, instâncias de uma classe. Toda classe de exceção é descendente da classe Exception. O Java já define uma grande quantidade de classes para exceções, entretanto, é possível criar novas classes sempre que necessário.

Depois de criada, a exceção precisa ser lançada na pilha de execução. O comando para lançar a exceção é o comando throw.

É comum criar e lançar a exceção na mesma linha sem utilizar uma variável de referência para a exceção:

throw new Exception();

Uma vez lançada a exceção, nenhum outro código será executado, seja no método que lançou, seja em qualquer outro método na pilha de execução até que a exceção seja capturada.

IMPORTANTE: Um método que lança uma ou mais exceções precisa informar a exceção na sua assinatura usando o comando throws.

MAIS IMPORTANTE: Não faz sentido um método tentar capturar a exceção que ele mesmo criou. O método lança a exceção quando não é capaz de resolver a situação inválida e captura a exceção quando é capaz de tratar a situação.

Exemplo:
public void UmMetodo() throws Exception{
    if (condição) throw new Exception();
    // Aqui o codigo do metodo - este código não será
    // executado se a exceção for lançada
}
 

Para criar uma nova classe de exceção


A definição de uma nova classe de exceção é extremamente simples, basta criar um descendente da classe Exception e reescrever o método toString() para retornar a string explicativa.

Exemplo:
public class NovaExcecao extends Exception{
   public String toString(){
      return “Aconteceu a nova exceção”;
   }
}
 

Links recomendados: