Você está na página 1de 8

Excees em Java

Miguel Jonathan DCC/IM/UFRJ (rev. abril de 2011)


Resumo dos conceitos e regras gerais do uso de excees em Java
O uso de excees permite separar a deteco da ocorrncia de uma situao excepcional do seu tratamento, ao se
programar um mtodo em Java.
Na forma antiga de se programar, era comum embutir as aes a tomar em cada teste de erro.
Por exemplo, uma funo hipottica f() ficava assim:
void f() {
if (<teste da condio de erro 1>) {
<comandos que determinam o que fazer se o erro 1 ocorreu>
}
else if (<teste da condio de erro 2>) {
<comandos que determinam o que fazer se o erro 2 ocorreu>
}
else if....<testando e tratando outros possveis erros>
else {
<comandos para processar a funo em condies normais>
}
}
Onde essa forma de programar prejudica o bom design das aplicaes?
a) Se um mtodo for usado em aplicaes diferentes, o mesmo erro sempre ser tratado da mesma maneira. Isso
limita a flexibilidade de lidar com situaes de exceo.
b) Se for necessrio alterar o procedimento a seguir no caso de um determinado erro, o mtodo na forma acima
ter que ser alterado. Isso introduz riscos e obrigar a re-testar todo o mtodo, inclusive as partes que j
estavam funcionando corretamente.
Como funciona o mecanismo de excees:
Uma exceo em Java um objeto da classe java.lang.Exception, ou de uma de suas subclasses. Como todo
objeto, a exceo capaz de armazenar dados nas suas variveis de instncia. Quando um erro ou situao anormal
encontrado durante a execuo de um mtodo, um objeto exceo construdo, e diversos dados sobre essa ocorrncia
so registrados nos campos desse objeto.
Nesse momento, o mtodo onde ocorreu a exceo aborta, e o controle da execuo retorna ao mtodo que o chamou.
Alm disso, por um mecanismo especial, o objeto exceo que foi construdo tambm enviado ao mtodo chamador.
Diz-se que o mtodo onde ocorreu o erro "lana" a exceo para o mtodo que o chamou.
Suponha um mtodo qualquer (por exemplo, main()) que chama um mtodo g():
public static void main (String[] args) {
......
......
g();
......
......
}
Suponha tambm que, de dentro de g(), o mtodo f() seja chamado:
public void g() {
......
......
f();
......
......
}
Vamos admitir que no mtodo f() podem ocorrer dois tipos de erros ou situaes excepcionais, a exceo A e a
exceo B. Usando excees, o mtodo f() poderia ser escrito da forma abaixo.
Obs.: O comando throw que se encarrega de lanar a exceo para o mtodo chamador:
public void f() {
if (<teste da condio de erro A>) {
//constri uma exceo da classe ExcecaoA e lana para quem chamou f()
throw new ExcecaoA(....lista de argumentos...);
else if (<teste da condio de erro B>) {
//constri uma exceo da classe ExcecaoB e lana para quem chamou f()
throw new ExcecaoB(....lista de argumentos...);
}
else ....<testando outros possveis erros e procedendo de forma similar>
else {
<comandos para processar f() em condies normais sem erro>
}
}
Agora o mtodo f() no precisa mais determinar o que fazer quando cada caso de erro ocorrer. Ele precisa apenas
detectar qual o caso de erro que ocorreu. A partir da, ele constri, e "lana" para que o chamou, um objeto especial da
classe Exception (ou de alguma subclasse). Ao construir esse objeto, o mtodo f() insere nele as informaes que
permitiro entender qual erro ocorreu, e qual era o estado da aplicao no momento do erro. Esse objeto poder ser
"capturado" pelo mtodo g(), e "tratado" l, ou mesmo ser novamente lanado por g() para ser capturado e tratado
por quem o chamou, no caso o main().
Vamos supor que as excees do tipo ExcecaoA que ocorrerem em f() devam ser capturadas e tratadas apenas pelo
mtodo main(). E que as excees do tipo ExcecaoB devam ser capturadas e tratadas no mtodo g().
Nesse caso, os mtodos main() e g() devem ser escritos assim:
===========================================
public static void main (String[] args) {
......
......
// g() lanar excees tipo ExcecaoA, caso ocorram dentro de f()
// mas vai capturar e tratar as excees tipo ExcecaoB, que nunca chegaro a main
try{
g();
}
catch(ExcecaoA exa){
....comandos para examinar a exceo referenciada por exa...
....comandos para tratar o erro A...
....................................
}
......
......
}
=============================================
// O cabealho informa o compilador (e os usurios) que g() pode lanar ExcecaoA
public void g() throws ExcecaoA {
......
......
// O bloco try permite capturar excees e trat-las nos blocos catch associados:
try{
f();
}
catch(ExcecaoB exb){
....comandos para examinar a exceo referenciada por exb...
....comandos para tratar ExcecaoB...
....................................
}
......
......
}
===========================================
Note que excees do tipo B que ocorram em f() jamais chegam a main(), pois so sempre capturadas em g().
Mas as excees do tipo A lanadas por f() no so capturadas em g(), e so por ele re-lanadas para main(), onde so
finalmente capturadas e tratadas.
O programador tem agora mais flexibilidade para escolher em que ponto da aplicao os erros sero tratados, e de que
forma. Apenas no caso de o prprio main() no capturar a exceo que ocorrer o encerramento anormal do
programa (que ir "abortar"), sendo seguido da impresso na console de um relatrio mostrando a seqncia de
chamadas que originou o erro (essa seqncia chamada de stack trace). Nos demais casos, o programa nunca aborta,
mas os erros so capturados e tratados adequadamente.
Informando o compilador que o mtodo poder lanar uma ou mais excees:
No final do cabealho de um mtodo que poder lanar excees, coloca-se a informao:
throws <lista das classes de exceo que o mtodo poder lanar>
Por exemplo:
public void f() throws NumberFormatException, IOException{
.....
}
Veremos mais adiante que para certas classes de exceo essa declarao obrigatria, enquanto que para outras
opcional.
Capturando e tratando excees: os blocos try { }, catch(){ }, e finally { }
Quando programamos um mtodo em Java, e dentro desse mtodo existirem comandos ou chamadas de mtodos onde
podem ocorrer uma ou mais excees, temos a opo de envolver esses comandos em um bloco try para capturar e
tratar essas excees dentro do mtodo.
try {
<comandos>
}
Um bloco try normalmente seguido de um ou mais blocos catch, que possuem o seguinte formato:
catch (T e){
<comandos para tratar a exceo apontada por e>
}
onde T um tipo de exceo, ou seja, o nome da classe Exception ou uma de suas subclasses.
No caso de algum comando dentro do bloco try lanar uma exceo, a execuo do bloco ser interrompida, e o
controle passar para o primeiro bloco catch que tenha um parmetro de tipo compatvel com a exceo lanada.
Podem haver zero, um ou mais blocos catch aps um bloco try. Caso no haja nenhum bloco catch compatvel com
o tipo da exceo, ele ser lanada para o mtodo que chamou o mtodo atual.
O bloco finally
Tipicamente, um bloco finally contm comandos de liberao de recursos alocados no bloco try (tais como
abertura de arquivos, de banco de dados, etc). Se esses comandos ficassem no final do bloco try, poderiam nunca ser
executados em caso de lanamento de exceo.
O bloco finally ser sempre executado aps o bloco try terminar normalmente, ou aps algum bloco catch
executar, mesmo que a sada desses blocos seja causada pelo lanamento de outra exceo no tratada, ou por comando
return. O bloco finally somente no ser executado se ocorrer antes uma chamada para terminar a JVM (mquina
vitual Java), com System.exit(0).
No caso do bloco try terminar sem lanamento de exceo, ou se houver exceo, mas ela for capturada por um bloco
catch e este terminar normalmente, os demais comandos do mtodo aps o bloco finally continuaro sendo
executados.
Os blocos catch e finally so opcionais, mas no permitido haver apenas o bloco try sem pelo menos um bloco
catch ou um bloco finally associado.
Por exemplo:
public void g() {
...comandos...
try{
f();
}
catch (NumberFormatException nfe){
<comandos para tratar essa exceo>
}
catch (Exception e){
<comandos para tratar qualquer outra exceo>
}
}
Suponha que ao executar, o mtodo f() lance uma exceo do tipo NumberFormatException. Ela ser capturada
pelo primeiro bloco catch acima. Se lanar outro tipo de exceo, ela ser capturada pelo segundo bloco catch. Isso
porque o tipo Exception pode apontar para qualquer exceo, por ser a superclasse de todas.
Veja um exemplo completo abaixo. O mtodo f(int x) da classe Classe1 lanar uma exceo do tipo
NumberFormatException, caso o valor do argumento x seja negativo, ou caso seja menor ou igual ao campo valor.
Se o argumento for maior que 1000, o mtodo lanar um exceo do tipo Exception.
O mtodo lanar uma ArithmeticException se houver tentativa de diviso por zero. Essa exceo lanada
automaticamente.
public class Classe1 {
public int valor;
public Classe1 (int n){
valor = n;
}

public void f(int x) throws Exception, NumberFormatException,ArithmeticException{
if (x< 0)throw new NumberFormatException("Erro-Argumento negativo: "+ x);
if (x<= valor)
throw new NumberFormatException("Erro-Argumento deve ser maior que " + valor);
if (x > 10000)throw new Exception("Erro-Argumento muito grande: "+ x);
System.out.println (x/(valor-100));//
}
}
public class TesteExcecoes {
public static void main(String[] args) {
Classe1 c1 = new Classe1(100);
try{
// c1.f(200);
// c1.f(-20);
// c1.f(20000);
c1.f(700);
}
catch(NumberFormatException nf){
System.out.println(nf);
}
catch(ArithmeticException ar){
System.out.println(ar);
}
catch(Exception e){
System.out.println(e);
}
finally{
System.out.println("Terminou o mtodo f()");
}
}
}
Excees verificadas e no-verificadas:
A linguagem Java admite dois tipos de exceo: As no verificadas (unchecked, em ingls) so instncias de subclasses
de RuntimeException. O compilador no verifica se existe possibilidade de serem lanadas, e no exige que os
mtodos onde possam ocorrer faam qualquer tratamento. Elas representam erros ou defeitos na lgica do programa que
podem causar problemas irrecuperveis em tempo de execuo (run time).
Por outro lado, instncias de Exception, ou de qualquer outra de suas subclasses, so verificadas (checked) como,
p.ex, IOException, ClassNotFoundException e CloneNotSupportedException. Elas representam erros que
podem ocorrer em tempo de execuo, mas que no dependem da lgica do programa, em geral defeitos nos
dispositivos de entrada ou sada (arquivos, rede, etc). O compilador exige que um mtodo onde possam ocorrer
excees verificadas faa uma de duas coisas: ou utilize blocos try-catch-finally para capturar e tratar essas excees,
ou declare que pode lanar essas excees, colocando uma clusula "throws" no seu cabealho, como por exemplo:
public void M() throws IOException, CloneNotSupportedException {
..............
}
Essa clusula facultativa para o caso de excees no-verificadas.

Construtores:
A classe java.lang.Exception, e todas as suas subclasses, tm pelo menos dois construtores da forma:
a) <nome da classe de Exceo> (String <mensagem de erro>)
b) <nome da classe de Exceo> ()
A mensagem de erro sempre retornada pelo mtodo toString().
Toda exceo tambm aceita a mensagem printStackTrace(), que imprime na stream apontada por System.err um
stack trace. O stack trace um relatrio detalhado da seqncia de chamadas a mtodos que antecederam o lanamento
da exceo.
Exemplos:
Em cada um dos exemplos seguintes, uma classe teste tenta executar o mtodo f() de uma de duas classes, A ou B. Na
classe A, o mtodo f() lana uma exceo do tipo "no verificada", no caso uma NumberFormatException. Na
classe B, o mtodo f() lana uma exceo do tipo "verificada", no caso uma IOException. Os diversos exemplos
mostram o uso de blocos try{}, catch{} e finally {} de vrias formas, e os comentrios esclarecem sobre os
efeitos decorrentes.
public class A {
/**
Neste exemplo, a classe A tem um metodo f() que pode lanar uma exceo do tipo
NumberFormatException, que e' nao verificada. Por esse motivo, o mtodo f() no precisa
incluir a terminao "throws NumberFormatException".
**/
public void f(int a){
if (a<20) throw new NumberFormatException();
System.out.println("a = "+ a);
}
}
import java.io.IOException;
public class B{
/**
Nesse exemplo, como IOException uma exceo verificada, o compilador exige
que o mtodo f() declare explicitamente que pode lanar a exceo, colocando a frase
"throws IOException" no seu cabealho.
**/
public void f(int a) throws IOException {
if (a<20) throw
new IOException ("valor do argumento de f() e' " + a + " (menor que 20)");
System.out.println("a = "+ a);
}
}
public class TesteExc1{
/**
Neste exemplo, a exceo ser capturada, e as trs mensagens sero exibidas.
Ou seja, mesmo depois de finally executar, o restante do mtodo main continua.
**/
public static void main(String[] args){
try{
A x = new A();
int a = 4;
x.f(a);
}
catch(Exception e){
System.out.println("valor ilegal de a");
}
finally{
System.out.println("fim do bloco try em TesteExc");
}
System.out.println("fim do metodo main em TesteExc");
}
public class TesteExc2 {
/**
Neste exemplo, o bloco catch no existe. Portanto, a exceo no ser capturada, gerando
um stack trace. O bloco finally e' executado, mas no o que segue depois.
**/
public static void main(String[] args){
try{
A x = new A();
int a = 4;
x.f(a);
}
finally{
System.out.println("fim do bloco try em TesteExc");
}
System.out.println("fim do metodo main em TesteExc");
}
}
public class TesteExc3 {
/**
Neste exemplo, o bloco catch no existe, apenas o try e o finally.
Com esse valor de a, a exceo no ser lanada.
Nesse caso, o cdigo depois do bloco finally tambm ser executado.
**/
public static void main(String[] args){
try{
A x = new A();
int a = 34;
x.f(a);
}
finally{
System.out.println("fim do bloco try em TesteExc");
}
System.out.println("fim do metodo main em TesteExc");
}
}
public class TesteExc4 {
/**
Neste exemplo, como a exceo que pode ser lanada por f() e' no verificada,
o compilador no reclama por no haver a clusula throws no cabealho de main.
Mas a exceo ser lanada, originando um stack trace, e o mtodo main()
no continuar aps o ponto da chamada de f().
**/
public static void main(String[] args){
A x = new A();
int a = 4;
x.f(a); // com esse valor, f() lancara' excecao
System.out.println("fim do metodo main em TesteExc");
}
}
public class TesteExc5 {
/**
Neste exemplo, como a exceo que pode ser lanada por f() e' do tipo "no verificada", o
compilador no reclama do fato de main() no informar que pode lanar uma exceo, com
"throws NumberFormatException" ou "throws Exception".
Como nesse exemplo a exceo no ser lanada, o mtodo main ir at o final.
**/
public static void main(String[] args){
A x = new A();
int a = 34;
// com esse valor, f() nao lancar exceo
x.f(a);
System.out.println("fim do metodo main em TesteExc");
}
}
import java.io.IOException;
public class TesteExc6 {
/**
Neste exemplo, usa-se a informao contida no objeto exceo para gerar a mensagem de
erro, pois o mtodo f() da classe B cria excees com uma mensagem informativa.
**/
public static void main(String[] args){
try{
B x = new B();
int a = 4;
x.f(a);
}
catch(IOException e){
System.out.println(e); // imprime toString(e)
}
finally {
System.out.println("fim do bloco try em TesteExc");
}
System.out.println("fim do metodo main em TesteExc");
}
}
import java.io.IOException;
public class TesteExc7 {
/**
Neste exemplo, o compilador reclama porque a exceo que pode ser lanada por f()
do tipo "verificada" (IOException), e o mtodo main() no tem a clausula "throws
IOException"
**/
public static void main(String[] args){
B x = new B();
int a = 34;
x.f(a);
System.out.println("fim do metodo main em TesteExc");
}
}
import java.io.IOException;
public class TesteExc8{
/**
Neste exemplo, a exceo que pode ser lanada por f() e' verificada (IOException),
e o mtodo main() tem a clausula "throws IOException", compilando OK.
**/
public static void main(String[] args) throws IOException{
B x = new B();
int a = 4;
x.f(a);
System.out.println("fim do metodo main em TesteExc");
}
}

Você também pode gostar