segunda-feira, abril 09, 2007

Exceções no Java

Dae pessoal, essa semana na empresa em que trabalho discutíamos sobre exceções em Java. Caramba, foi feia a peleia... cada um dizia uma coisa, usava o controle de exceções de uma maneira nas empresas por onde passaram (a equipe toda é de novos talentos vindos de grandes empresas do mercado de software bancário, fabricas de software , etc... com experiência em frameworks proprietários e comunitários diversos). Então resolvi postar esse material, não que eu estivesse certo na discussão ou os outros errados... mas para que sirva para mais alguém que um dia tenha tido dúvidas a respeito disto ou que desconheça o assunto. :D

Bom a definição de exceção (exception em inglês – palavra reservada no Java) pela Sun:

Uma exceção é um evento, que ocorre durante a execução de um programa, que interrompe o fluxo normal das instruções do programa.

Lindo isso. Nossa, estou contendo as lágrimas!
Quando ocorre um erro em um método em um programa desenvolvido em Java é criado um objeto (do tipo Exception) que contém as informações sobre o erro incluindo seu tipo e o estado do programa quando ocorreu o erro. Criar este objeto e passá-lo para a JVM chamasse lançar uma exceção (throwing an exception).
Depois que ocorre um erro e o objeto é criado o sistema tenta encontrar alguma coisa que trate este erro e este processo é chamado de pilha de chamada (call stack). Nada mais é do que uma lista de ordem inversa, ou seja, ocorre um erro vejo se o método o trata ou vou propagando este erro até que alguém o faça. Quem trata um erro? Os tratadores, ou apanhadores, de erros (exceptions handlers).

Imagine:

public double dividir(double dividendo,
double divisor) {
... faz e retorna o resultado da divisão
}

Se eu informar zero como divisor? FERROU! Claro que a linguagem Java retornaria um erro aqui, mas se isso não fosse tratado nosso programa pararia sem um tratamento a esta exceção.
A duas coisas a fazer com uma exceção, tratá-la ou propagá-la.
Vamos ver o primeiro, para isso temos que apanhar (catch) a exceção em um bloco tentar (try) apanhar (catch):

try {
... tenta alguma coisa
}
catch(Exception e) {
... deu erro, captura o erro e
o trata aqui
}

Ainda há a instrução por fim/finalmente (finally) que sempre é executado.

try {
... tenta alguma coisa
}
catch(Exception e) {
... deu erro, captura o erro e o trata aqui
}
finally {
... comandos que sempre devem ser executados
mesmo que haja uma execeção
}

Um exemplo de tratamento de exceções com uso do finally seria comandos a submetidos a bancos de dados que se houver um erro na transação, por exemplo, deveríamos fechar a conexão com o banco de dados para pouparmos recursos:

public void getSysdate() {
try {
... abre conexão, faz seleciona e retorna a
data do banco de dados
}
catch(SQLException e) {
System.out.println("Ocorreu um erro de
SQL: " + e);
}
catch(ClassNotFoundException e) {
System.out.println("Ocorreu um erro ao
tentar conectar ao
banco: " + e);
}
catch(Exception e) {
System.out.println("Ocorreu um erro que
eu não sei de que
tipo é: " + e);
}
finally{
... blablabla fecha a conexão, finaliza
objetos criados no processo
}
}

Beleza, mas aqui há pontos importantes a serem observados.

1. Eu estou tratando o erro no método, e não o propagando;
2. A "cascata" de tratamento das exceções vai da mais específica a mais genérica;
3. Poderia imprimir a mensagem de erro de outra maneira (mais abrangente) colocando um e.printStackTrace() no método catch.

Eu poderia não tratar minha exceção aqui dentro do método e sim propagá-la obrigando que qualquer classe que fizesse uso deste método tivesse que a tratá-la.

public void getSysdate() SQLException,
ClassNotFoundException,
Exception {
... faz o que eu queria fazer antes
}

Quando eu for chamar o método eu terei que o fazer dentro de um bloco try catch:

try {
instância_da_classe.getSysdate();
}
catch(SQLException e) {
System.out.println("Ocorreu um erro de
SQL:" + e);
}
catch(ClassNotFoundException e) {
System.out.println("Ocorreu um erro ao tentar
conectar ao banco: " + e);
}
catch(Exception e) {
System.out.println("Ocorreu um erro que eu
não sei de que tipo
é: " + e);
}
finally{
... blablabla fecha a conexão, finaliza
objetos criados no processo
}

Exceções é um capítulo bem complicado (nada de mais, só merece atenção) do Java e vai muito além desta minha explicação superficial sobre seu funcionamento. Sugiro que leiam o Java Tutorial e outros recursos que estão na internet para saber sobre os tipos de exceções que existem, sobre a classe Throwable e as suas filhas Error e Exception e as demais vantagens do uso de exceções no Java.

Para variar aqui tem dois links que deixam a disposição um exemplo de classe de exceção e uma classe que lança essa exceção. Divirtasse!

Saída obtida ao rodar o Calculadora:

shell> java Calculadora
No há divisão por zero.
at Calculadora.dividir(Calculadora.java:12)
at Calculadora.main(Calculadora.java:23)

http://geocities.yahoo.com.br/sadbuttruebr/blog/
exemplos_java/Calculadora.java

http://geocities.yahoo.com.br/sadbuttruebr/blog/
exemplos_java/DivisaoPorZeroException.java

3 comentários:

Anônimo disse...

"Se eu informar zero como divisor? FERROU! Claro que a linguagem Java retornaria um erro aqui,"

Isto é falso. Quando os argumentos são double não existe erro ao dividir por zero.

Unknown disse...

Ok, então que sejam dois inteiros.
O propósito é falar sobre exceções.

Att.

Unknown disse...

Cara, gostei do teu post.. so que eu queria saber se existe a possibilidade de voltar ao ponto do programa onde estava antes da exceção ser criada.. tipo, manda uma mensagem pro usuario e depois ele poder tentar novamente a partir dakele ponto..
obrigado.. se puder mande um email diegomoraes.a@gmail.com