Quando se cria programas de computador em Java, há possibilidade de ocorrer erros imprevistos durante sua execução, esses erros são conhecidos como exceções e podem ser provenientes de erros de lógica ou acesso a dispositivos ou arquivos externos.

Entendendo as exceções

As exceções ocorrem quando algo imprevisto acontece, elas podem ser provenientes de erros de lógica ou acesso a recursos que talvez não estejam disponíveis.

Alguns possíveis motivos externos para ocorrer uma exceção são:

  • Tentar abrir um arquivo que não existe.
  • Tentar fazer consulta a um banco de dados que não está disponível.
  • Tentar escrever algo em um arquivo sobre o qual não se tem permissão de escrita.
  • Tentar conectar em servidor inexistente.
Ocorrência de exceção apontada no console
Figura 1. Ocorrência de exceção apontada no console

Alguns possíveis erros de lógica para ocorrer uma exceção são:

  • Tentar manipular um objeto que está com o valor nulo.
  • Dividir um número por zero.
  • Tentar manipular um tipo de dado como se fosse outro.
  • Tentar utilizar um método ou classe não existentes.
Exemplo de exceção por divisão por zero
Figura 2. Exemplo de exceção por divisão por zero

Tratando Exceções

Uma maneira de tentar contornar esses imprevistos é realizar o tratamento dos locais no código que podem vir a lançar possíveis exceções, como por exemplo, campo de consulta a banco de dados, locais em que há divisões, consulta a arquivos de propriedades ou arquivos dentro do próprio computador.

Para tratar as exceções em Java são utilizados os comandos try e catch.

Sintaxe:


try
{
  //trecho de código que pode vir a lançar uma exceção
}
catch(tipo_exceçao_1 e)
{
  //ação a ser tomada
}
catch(tipo_exceçao_2 e)
{
  //ação a ser tomada
}
catch(tipo_exceçao_n e)
{
  //ação a ser tomada
}
Listagem 1. Sintaxe de uso d try-catch

Onde:

  • try{ … } - Neste bloco são introduzidas todas as linhas de código que podem vir a lançar uma exceção.
  • catch(tipo_excessao e) { … } - Neste bloco é descrita a ação que ocorrerá quando a exceção for capturada.

Exemplificando uma exceção

Imagine uma classe que tem um método principal main que tem como seu único objetivo alterar todas as letras de um frase para maiúsculas utilizando o método toUpperCase() da classe String, caso a frase esteja nula e se tente usar o método toUpperCase() na mesma será lançada uma exceção de NullPointerException.

Primeiro vamos ver como ficaria a tal classe sem a utilização do try/catch.


public class aumentaFrase {
  public static void main(String args[])
  {
    String frase = null;
    String novaFrase = null;
    novaFrase = frase.toUpperCase();
    System.out.println("Frase antiga: "+frase);
    System.out.println("Frase nova: "+novaFrase);
  }
}
Listagem 2. Exemplo de código sem try-catch

Quando este código for executado, o mesmo lançará uma NullPointerException, como poder ser visto na saída do console quando executamos tal programa.


Exception in thread "main" java.lang.NullPointerException
at aumentaFrase.main(aumentaFrase.java:15)
Listagem 3. Saída gerada pelo programa sem try-cach

Ou seja, o mesmo tentou acessar um atributo de um objeto que estava nulo. Para ajudar a melhorar a situação, deve-se usar o try/catch.


public static void main(String args[])
{
  String frase = null;
  String novaFrase = null;
  try
  {
    novaFrase = frase.toUpperCase();
  }
  catch(NullPointerException e) //CAPTURA DA POSSÍVEL exceção.
  {
    //TRATAMENTO DA exceção
    System.out.println("O frase inicial está nula, 
    para solucional tal o problema, foi lhe atribuito um valor default.");
    frase = "Frase vazia";
    novaFrase = frase.toUpperCase();
  }
  System.out.println("Frase antiga: "+frase);
  System.out.println("Frase nova: "+novaFrase);
}
Listagem 4. Reformulação do código com try-catck

Quando este código for executado, o mesmo lançará uma NullPointerException, porém esta exceção será tratada desta vez, sendo a mesma capturada pelo catch{} e dentro deste bloco as devidas providências são tomadas. Neste caso é atribuído um valor default à variável frase. A saída deste programa seria a seguinte:


array4
Listagem 5. Saída do programa reformulado

Comando finally

Imagine a seguinte situação: foi aberta uma conexão com o banco de dados para realizar determinada ação, e no meio deste processo seja lançada alguma exceção, como por exemplo, NullPointerException ao tentar manipular um determinado atributo de um objeto. Neste caso seria necessário que mesmo sendo lançada uma exceção no meio do processo a conexão fosse fechada. Um outro exemplo bom seria a abertura de determinado arquivo para escrita no mesmo, e no meio deste processo é lançada uma exceção por algum motivo, o arquivo não seria fechado, o que resultaria em deixar o arquivo aberto.

Quando uma exceção é lançada e é necessário que determinada ação seja tomada mesmo após a sua captura, utilizamos a palavra reservada finally.

Sintaxe:


try
{
  //trecho de código que pode vir a lançar uma exceção
} 
catch(tipo_exceçao_1 e)
{
  //ação a ser tomada
}
catch(tipo_exceçao_2 e)
{
  //ação a ser tomada
}
catch(tipo_exceçao _n e)
{
  //ação a ser tomada
}
finally
{
  //ação a ser tomada
}
Listagem 6. Sintaxe de uso do bloco finally

Exemplo:


public class aumentaFrase {
  public static void main(String args[])
  {
    String frase = null;
    String novaFrase = null;
    try
    {
      novaFrase = frase.toUpperCase();
    }
    catch(NullPointerException e)
    {
      System.out.println("O frase inicial está nula, para 
      solucional tal o problema, foi lhe atribuito um valor default.");
      frase = "Frase vazia";
    }
    finally
    {
      novaFrase = frase.toUpperCase();
    }
    System.out.println("Frase antiga: "+frase);
    System.out.println("Frase nova: "+novaFrase);
  }
}
Listagem 7. Programa aumetaFrase com bloco finally

Quando este código fosse executado, o mesmo lançaria uma NullPointerException, porém esta exceção será tratada desta vez, sendo a mesma capturada pelo catch{} e dentro deste bloco as devidas providências são tomadas. Neste caso é atribuído um valor default à variável frase. Neste exemplo, mesmo o código lançando uma exceção durante a sua execução e a mesma sendo capturada pelo catch, uma determinada ação será tomada no bloco finally, neste caso tanto com a exceção ou não será executada a linha “ novaFrase = frase.toUpperCase();, tornando todas letras da frase maiúsculas. A saída deste programa seria a seguinte:


array4
Listagem 8. Saída do programa com bloco finally

Comandos throw e throws

Imagine uma situação em que não é desejado que uma exceção seja tratada na própria classe ou método, mas sim em outro que venha lhe chamar. Para solucionar tal situação utilizamos o comando throws na assinatura do método com a possível exceção que o mesmo poderá a vir lançar.

Sintaxe:


tipo_retorno nome_metodo() throws tipo_exceção_1, tipo_exceção_2, tipo_exceção_n
{

…

}
Listagem 9. Sintaxe de declaração de método com definição de exceções

Onde:

  • tipo_retorno – Tipo de retorno do método.
  • nome_metodo() - Nome do método que será utilizado.
  • tipo_exceção_1 a tipo_exceção_n – Tipo de exceções separadas por virgula que o seu método pode vir a lançar.

Exemplo:


public class TesteString {
  private static void aumentarLetras() throws NullPointerException //lançando excessão
  {
    String frase = null;
    String novaFrase = null;
    novaFrase = frase.toUpperCase();
    System.out.println("Frase antiga: "+frase);
    System.out.println("Frase nova: "+novaFrase);
  }

  public static void main(String args[])
  {
    try
    {
      aumentarLetras();
    }
    catch(NullPointerException e)
    {
      System.out.println("Ocorreu um 
      NullPointerException ao executar o método aumentarLetras() "+e);
    }
  }
}
Listagem 10. Definição de exceções que um método pode gerar

Neste exemplo será lançada uma exceção no método aumetarLetras():


private static void aumentarLetras() throws NullPointerException
Listagem 11. Definição da exceção gerada pelo método aumentarLetras

E o mesmo será tratado no método main().


...
try
{
  aumentarLetras();
}
catch(NullPointerException e)
{
  System.out.println("Ocorreu um NullPointerException ao 
  executar o método aumentarLetras() "+e);
}
…
Listagem 12. Aplicação da exceção definida

Saída:


Ocorreu um NullPointerException ao executar o método 
aumentarLetras() java.lang.NullPointerException.
Listagem 13. Saída do programa atualizado

Agora imagine o caso em que seja necessário lançar uma exceção padrão ao invés de uma especifica. Para resolver este problema, utilizamos o comando throw dentro do bloco catch que desejamos converter a exceção.

Sintaxe:


try
{
  //…
}
catch(tipoExcessão_1 e)
{
  throw new novoTipoExceçao(e);
}
Listagem 14. Sintaxe de uso do comando throw

Onde:

  • tipoExcessão_1 e – Tipo de exceção que pode ser capturada pelo bloco catch.
  • NovoTipoExceçao – Tipo de exceção que será lançada.

Exemplo:


public class TesteString {
  private static void aumentarLetras() throws Exception //lançando exceção
  {
    String frase = null;
    String novaFrase = null;
    try
    {
      novaFrase = frase.toUpperCase();
    }
    catch(NullPointerException e)
    {
      throw new Exception(e);
    }
    System.out.println("Frase antiga: "+frase);
    System.out.println("Frase nova: "+novaFrase);
  }
  public static void main(String args[])
  {
    try
    {
      aumentarLetras();
    }
    catch(Exception e)
    {
      System.out.println("Ocorreu uma exceão ao 
      executar o método aumentarLetras() "+e);
    }
  }
}
Listagem 15. Exemplo de uso do comando throw

Neste exemplo será lançada uma NullPointerException e a mesma será convertida para Exception e relançada como Exception no método aumentarLetras() e, por fim, a mesma é tratada no método main().

Saida:


Ocorreu uma exceão ao executar o método aumentarLetras() 
java.lang.Exception: java.lang.NullPointerException
Listgem 16. Saída do programa atualizada

Criando exceções

Assim como qualquer objeto, em Java também é possível criar suas próprias exceções. Imagine um cenário em que nenhuma exceção existente faça sentido para ser lançada por você.

Por exemplo, imagine que por algum motivo você precisa que uma exceção seja lançada quando a letra B ou b não existe e determinada frase, como não existe nenhuma exceção específica para este caso será necessário criar uma exceção.

Criando uma exceção para ser lançada toda vez que uma letra B ou B não é encontrada em uma determinada frase.


public class SemLetraBException extends Exception {
  @Override
  public String getMessage(){
    return "Não existe letra B em sua frase";
  }
}
Listagem 17. Exemplo de exceção customizada

Toda exceção criada deve estender Exception, neste exemplo foi sobrescrito o método getMessage(), que é exibida no prompt toda vez que a exceção é lançada.

Utilizando a exceção

Abaixo segue um exemplo que é utilizada a exceção criada acima.


public class TesteExcecao {
  public static void main(String args[]) throws SemLetraBException 
  {
    String frase = "Sou um teste!";
    if(!frase.contains("b") || !frase.contains("B"))
    throw new SemLetraBException();
  }
}
Listagem 18. Utilizando a exceção customizada

Quando o programa acima fosse executado, uma exceção do tipo SemLetraBException() seria lançada. Abaixo está a saída exibida no prompt:


Exception in thread "main" SemLetraBException: Não existe letra B ou b em 
sua frase at TesteExcecao.main(TesteExcecao.java:8)
Listagem 19. Saída do método com a mensagem da exceção customizada

Conclusão

Como foi possível ver ao longo do artigo, o tratamento de exceções em Java é bem simples e sua manipulação pode ser feita de acordo com o que o programador deseja, desde tratá-la no próprio método ou lança-la para ser tratada em um método que venha chamar o método que lança a mesma.

Confira também