O tratamento de exceções é indispensável para tornar uma aplicação robusta, isso porque o usuário não quer ver um StackTrace na sua tela, mas sim uma mensagem bem escrita apontando que apenas ocorreu um erro.
Para nos situarmos um pouco mais na importância desse tipo de tratamento podemos citar o próprio Sistema Operacional Windows. Você sabe porque a famosa “tela azul da morte” é azul ? Porque foi comprovado cientificamente, que o azul transparece calma ao usuário, deixando o mesmo mais tranquilo. Imagine se a “tela azul da morte” fosse vermelha ? Tente pensar no pânico que causaria a apresentação desta tela.
Enfim, toda e qualquer aplicação está sujeita a falhas, e por conta disso, toda e qualquer aplicação também precisa de tratamento dessas falhas.
O objetivo deste artigo é mostrar um pouco mais sobre o tratamento de exceções em Java, mais especificamente para exemplificar a diferença entre exceções Checked e Unchecked.
Checked e Unchecked Exceptions
A principal pergunta do desenvolvedor neste ponto é: Essa exceção é checked ou unchecked ? A minha exceção é checked ou unchecked ? Como tornar uma exceção checked ou unchecked ? Enfim, todas essas perguntas serão respondidas ao longo deste artigo.
Resumidamente as exceções Checked são aquelas no qual você é obrigado a tratá-la, seja com um bloco try-catch ou mesmo com um throws (relançando a mesma para outro local). Por outro lado, quando você tem exceções do tipo Unchecked não é obrigatório o tratamento da mesma, você pode tratar apenas se quiser, se sentir que é necessário para o bom funcionamento da sua aplicação.
Checked exceptions são utilizadas para erros recuperáveis enquanto que Unchecked exceptions são utilizadas para erros irrecuperáveis. Significa dizer que quando você sabe que seu erro pode ser tratado, você utiliza Checked Exceptions, caso contrário utilize Unchecked Exceptions.
Para ilustrar isso, imagine que você criou uma Exception chamada “ValorPagamentoMenorTaxaEmbarqueException”, isso significa que quando o valor do pagamento for menor que a taxa de embarque você lançará uma Exception e tratará da forma que achar melhor, por exemplo: Pedindo para o usuário aumentar o valor do pagamento. Essas são as Checked Exceptions, você sabe que o erro poderá ocorrer e já sabe como corrigi-lo caso ocorra.
Por outro lado, imagine um NullPointerException inesperado na sua aplicação. Esse erro não pode ser tratado, o erro ocorreu e pronto, você deve apenas mostrar uma mensagem ao usuário dizendo que o erro ocorreu, mas nada poderá ser feito para corrigi-lo, se não reiniciar todo o processamento. Essas são as Unchecked Exceptions, que geralmente são filhas de RuntimeException, isso porque estas são exceções que ocorrem em tempo de execução (runtime) e não em tempo de design.
Quando você cria um bloco de código que exige que você use try-catch ou throws, estará em frente a uma típica Checked Exception, diferente das RuntimeExceptions que só ocorrem em tempo de execução e são inesperadas. As Unchecked Exceptions pelo fato de não serem obrigatoriamente tratadas, automaticamente relançam suas exceções, ou seja, possuem throws implícito no método que as criou.
Problemas
Ocorre em alguns casos, que este padrão não é bem aplicado, como é o caso da API JDBC. Veja na listagem 1 o uso errado deste padrão:
Listagem 1: JDBC utilizando exceções de forma errônia
try {
PreparedStatement stmt = con.prepareStatement(query);
// ...
} catch (SQLException e) {
throw new AcessoADadosException("Problema na criação do Statement", e);
}
Você deve estar familiarizado em sempre ter que fazer o try-catch ou throws ao usar a API JDBC, tratando uma SQLException. Mas se você parar para pensar, a SQLException é uma exceção muito genérica e que na maioria das vezes é irrecuperável. Imagine por exemplo se o seu banco de dados queimou, como você irá recuperar isso ? Não há como, apenas poderá mostrar uma mensagem ao usuário.
Na listagem 1, transformamos a “suposta” Checked SQLException em uma Unchecked Exception através do AcessoADadosException que apenas retornará uma mensagem com o erro e nada mais poderá ser feito. Acostume-se com essa falta de padronização, pois poderá encontrar muitos códigos aplicados Checked no lugar de Unchecked e vice-versa, mas conhecendo o conceito de ambos, você poderá facilmente realizar as “transformações” necessárias.
Exemplo Prático
Para finalizarmos esse assunto, vamos aplicar os conceitos do artigo a um exemplo prático, onde teremos a seguinte situação: Nosso método tentará escrever um arquivo em uma pasta A, porém caso ocorre algum erro de IOException (é um Checked Exception) nós tentaremos gravar na Pasta Alternativa. Porque isso ? Imagine que a pasta A no computador do usuário pode estar sem permissão de escrita, e para não perdemos o arquivo vamos gravar na pasta Alternativa que sabemos que sempre terá permissão de escrita em qualquer dos casos. Depois podemos pegar todos os arquivos que ficaram na pasta alternativa e colocá-los na pasta B manualmente, porém não perderemos o processamento.
Na listagem 2 você verá o código do caso descrito acima.
Listagem 2: Exemplo real de um Checked Exception
private void criaArquivo(String pathAlternativo) {
File arquivo = null;
try {
SimpleDateFormat fmt = new SimpleDateFormat("dd/MM/yyyy");
if (pathAlternativo.equals(""))
arquivo = new File("/home/admin/Documentos/arquivo_" + fmt.format(new Date()) );
else
arquivo = new File(pathAlternativo+"arquivo_" + fmt.format(new Date()) );
BufferedWriter writer = new BufferedWriter(new FileWriter(arquivo));
writer.write("Linha 001");
writer.close();
} catch (IOException e ) {
if (arquivo != null){
if (arquivo.exists()){
arquivo.delete();
criaArquivo("/home/admin/PathAlternativo/");
}
}
}
}
Poderiamos até melhorar um pouco o código acima colocando 1 Exception genérico, assim qualquer coisa que não fosse um IOException poderiamos tratar como um Unchecked Exception, pois seria um erro irrecuperável e nada poderíamos fazer.
CONCLUSÃO
É muito provável que a maioria dos profissionais não conheçam a real utilização do conceito descrito neste artigo, isso porque por muita das vezes este passa despercebido e sem uma utilidade visível. Porém com o aprendizado deste, fica muito mais fácil entender a real aplicação (baseada nas boas práticas de programação) das exceptions.