You are on page 1of 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 b) <nome

da classe de Exceo> (String <mensagem de erro>) 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"); } }

You might also like