Sistemas de informa¸c˜ao – Univ´as
Roberto Ribeiro Rocha
Maio de 2014
Sum´
ario
1 Introdu¸c˜ao 1
2 Problematiza¸c˜ao 1
3 Exce¸c˜oes 1
4 Tratamento de Exce¸c˜oes 3
4.1 Hierarquia das exce¸c˜oes . . . 5
4.2 Delegando exce¸c˜oes . . . 6
4.3 Lan¸cando exce¸c˜oes . . . 7
1
Introdu¸
c˜
ao
Todos os programas est˜ao sujeitos a falhas que podem interromper a execu¸c˜ao e causar perdas para os usu´arios e terceiros. ´E esperado de um sistema que ele seja tolerante a falhas, ou seja, que ele consiga recuperar de poss´ıveis problemas que venha acontecer, dando continuidade `a execu¸c˜ao de suas tarefas.
O desenvolvedor deve ficar atendo `as poss´ıveis falhas no sistema para que elas se-jam tratadas adequadamente, criando estrat´egias de tolerˆancia e corre¸c˜ao destas falhas. A linguagem Java fornece este recurso importante que ´e chamado de tratamento de exce¸c˜oes, que ´e feito atrav´es da manipula¸c˜ao deexce¸c˜oes.
2
Problematiza¸
c˜
ao
Utilizando o exemplo da classeConta, o m´etodosacar(...) efetua o saque somente se o saldo for suficiente retornando true indicando sucesso e retorna false caso o saldo seja insuficiente.
Esta abordagem resolve o problema caso n˜ao haja dinheiro suficiente para um saque, por´em o m´etodo que fez esta chamada pode n˜ao verificar o valor retornado e continuar a executar de maneira inconsistente.
Exemplo 1
O c´odigo a seguir permite que o pagamento seja efetuado mesmo que a conta n˜ao tenha saldo para a opera¸c˜ao.
1 Conta conta = new Conta (" Teste ", 1234) ; 2 conta . d e p o s i t a r (100) ;
3 conta . sacar (200) ;
4 r e a l i z a r P a g a m e n t o (200) ;// esta opera c¸ ˜a o ´e i n c o n s i s t e n t e
O m´etodo sacar(...) deve possuir uma forma de avisar o m´etodo que o chamou de forma que o chamador saiba o ocorrido e tome as providˆencias necess´arias para tratar o problema.
O recurso utilizado para notificar que a chamada de um m´etodo n˜ao foi realizada com sucesso ´e chamada deexcec¸˜ao, e seu tratamento normalmente ´e feito pelo m´etodo chamador.
3
Exce¸
c˜
oes
Exce¸c˜ao´e um acontecimento de algum evento ou uma situa¸c˜ao que ocorre de ma-neira inesperada no sistema.
Exemplo 2
1 p a c k a g e br . edu . univas . lab3 . excecao ; 2
3 p u b l i c c l a s s Runner { 4
5 p u b l i c s t a t i c void main ( String [] args ) { 6 ca lcu lar () ;
7 }
8
9 p u b l i c s t a t i c void ca lcu lar () {
10 System . out . println (" I n i c i a n d o ... ") ; 11 int x = 10 , y = 0 , z ;
12 z = x / y ;
13 System . out . println (" R e s u l t a d o : " + z ) ;
14 }
15 }
Ao executar este c´odigo, ele imprimir´a no console o seguinte resultado: Iniciando...
Exception in thread "main"java.lang.ArithmeticException: / by zero at br.edu.univas.lab3.excecao.Runner.calcular(Runner.java:12)
at br.edu.univas.lab3.excecao.Runner.main(Runner.java:6)
Este conjunto de informa¸c˜oes ´e conhecido como stacktrace e s˜ao de extrema im-portˆancia para o programador, pois fornece as informa¸c˜oes sobre a falha ocorrida e sua localiza¸c˜ao.
O funcionamento do esquema de exce¸c˜oes ser´a demostrado baseado no exemplo 2: quando ocorre uma falha (linha 12), uma exce¸c˜ao ´e criada e lan¸cada (ArithmeticException), interrompendo a execu¸c˜ao do m´etodo corrente (calcular(). A interrup¸c˜ao do m´etodo faz com que a execu¸c˜ao do programa volte para o m´etodo chamador (main), fazendo com que ele seja interrompido tamb´em. Esta interrup¸c˜ao vai ocorrendo at´e que o programa termine.
As interrup¸c˜oes ocorrem porque nenhum m´etodo fez a captura (tratamento ade-quado) da exce¸c˜ao, for¸cando o Java imprimir as informa¸c˜oes de origem e tipo da exce¸c˜ao e de todos os m´etodos stack) envolvidos na execu¸c˜ao (stack). A stack armazena as in-forma¸c˜oes(vari´aveis, parˆametros, linha em execu¸c˜ao) de um m´etodo quando este chama outro m´etodo.
Se algum m´etodo da stack possui um tratamento adequado para a exce¸c˜ao, ent˜ao ele captura-a e continua sua execu¸c˜ao normal.
Exemplo 3
Incluir o m´etodo abaixo no c´odigo do Exemplo 3 e substituir a chamada do m´etodo calcular() pelo m´etodoimprimir() nomain.
1 p u b l i c s t a t i c void i m p r i m i r A r r a y () {
2 System . out . println (" I n i c i a n d o impress ˜a o ... ") ; 3 int[] e l e m e n t o s = new int[5];
4 for (int i = 0; i <= 10; i ++) { 5 e l e m e n t o s [ i ] = i ;
6 System . out . println ( i ) ;
7 }
9 }
O resultado ser´a:
Iniciando impress˜ao... 0
1 2 3 4
Exception in thread "main"java.lang.ArrayIndexOutOfBoundsException: 5 at br.edu.univas.lab3.excecao.Runner.imprimirArray(Runner.java:20) at br.edu.univas.lab3.excecao.Runner.main(Runner.java:6)
Agora, os dados do stacktrace s˜ao diferentes, e relativos ao problema ocorrido.
Exemplo 4
Outro problema muito comum ´e o acesso `a referˆencias de objeto null, conforme mostrado seguir:
1 p u b l i c s t a t i c void i m p r i m i r S a l d o () {
2 System . out . println (" I m p r i m i n d o saldo ... ") ; 3 Conta conta = null;
4 System . out . println (" Saldo : " + conta . get Sal do () ) ; 5 System . out . println (" Fim da impress ˜a o . ") ;
6 }
Se o main chamar este m´etodo, o resultado no console ser´a: Imprimindo saldo...
Exception in thread "main"java.lang.NullPointerException
at br.edu.univas.lab3.excecao.Runner.imprimirSaldo(Runner.java:35) at br.edu.univas.lab3.excecao.Runner.main(Runner.java:6)
Para que o programa n˜ao seja interrompido, ´e necess´ario fazer o tratamento de exce¸c˜oes adequadamente.
4
Tratamento de Exce¸
c˜
oes
Otratamento de exce¸c˜oespermite controlar falhas, tratando-as adequadamente, tomando as decis˜oes necess´arias para a continua¸c˜ao da execu¸c˜ao normal do sistema. Este controle ´e feito atrav´es da manipula¸c˜ao de exce¸c˜oes.
Quando ocorre uma falha, o programa cria e lan¸ca (throw) uma exce¸c˜ao. Esta exce¸c˜ao pode ser capturada (catch) por qualquer m´etodo interessado e tratada de forma que a execu¸c˜ao normal do c´odigo torne-se v´alida novamente.
O tratamento de exce¸c˜oes ´e feita utilizando uma estrutura fornecida pela linguagem, composta por trˆes comandos:
uma poss´ıvel exce¸c˜ao. Ele ´e usado quando o programador sabe que um determinado conjunto de instru¸c˜oes pode gerar algum tipo de exce¸c˜ao.
• catch: ´e o bloco de comandos que ´e executado caso a exce¸c˜ao capturada (especifi-cada pelo catch) foi lan¸cada dentro do bloco try. O bloco catch ´e opcional, mas ele normalmente ´e implementado junto a um bloco try. O bloco catch pode ser definido v´arias vezes, cada um com uma exce¸c˜ao diferente, cada um tratando um tipo diferentes de falha ocorrida.
• finally: ´e o bloco de comandos de instru¸c˜oes que sempre ser´a executado ao final de um bloco try-catch, independente do que ocorreu nos blocos anteriores. Ele nor-malmente ´e utilizado para fazer algum processamento de finaliza¸c˜ao (fechar conex˜ao, liberar recursos, salvar informa¸c˜oes, etc.). O bloco finally tamb´em ´e opcional.
A l´ogica de execu¸c˜ao destes trˆes blocos ´e a seguinte:
• Tenta executar os comandos do bloco try;
• Se algum comando do try gerar uma exce¸c˜ao, ent˜ao executa os comandos do bloco catch apropriado. O desenvolvedor deve ter o cuidado para que o blococatch n˜ao gere uma exce¸c˜ao.
• Independente do que foi executado dentro dotrye docatche das exce¸c˜oes geradas, os comandos do bloco finally sempre ser˜ao executados.
Exemplo 5
O c´odigo do Exemplo 2, poderia evitar a interrup¸c˜ao do programa tratando adequa-damente a exce¸c˜ao ArithmeticException da seguinte maneira:
1 p u b l i c s t a t i c void ca lcu lar () {
2 System . out . println (" I n i c i a n d o c ´a lculo ... ") ; 3 int x = 10 , y = 0 , z ;
4 try {
5 z = x / y ;
6 System . out . println (" R e s u l t a d o : " + z ) ; 7 } c a t c h ( A r i t h m e t i c E x c e p t i o n e ) {
8 System . out . println (" Erro no c ´a lculo : " + e ) ; 9 } f i n a l l y {
10 System . out . println (" Fim do c ´a lculo . ") ;
11 }
12 }
Neste caso, o c´odigo dentro do bloco try ´e monitorado e, se ocorrer a exce¸c˜ao do tipo ArithmeticException, o bloco catch ´e executado. O bloco finally sempre ser´a executado e o resultado da execu¸c˜ao ´e a seguinte:
Iniciando c´alculo...
A vari´aveleespecificada pelocatchcorresponde ao objeto da classeArithmeticException correspondente `a exce¸c˜ao capturada. Como qualquer outro objeto ele pode ser utilizado
pelo programador da forma que desejar.
Exemplo 6
Neste exemplo, ser´a mostrado o uso de mais de um blococatchpara tratar diferentes tipos de exce¸c˜oes.
1 p u b l i c s t a t i c void ca lcu lar () {
2 System . out . println (" I n i c i a n d o c ´a lculo ... ") ; 3 int x = 10 , y = 0 , z ;
4 try {
5 z = x / y ;
6 System . out . println (" R e s u l t a d o : " + z ) ; 7 } c a t c h ( A r i t h m e t i c E x c e p t i o n e ) {
8 System . out . println (" Erro no c ´a lculo : " + e ) ; 9 } c a t c h ( C l a s s C a s t E x c e p t i o n e ) {
10 System . out . println (" Erro no c ´a lculo : " + e ) ; 11 } f i n a l l y {
12 System . out . println (" Fim do c ´a lculo . ") ;
13 }
14 }
Em v´arios casos, as exce¸c˜oes comoArithmeticException,NullPointerException, ArrayIndexOutOfBoundsException e outras podem ser evitadas pelo programador fa-zendo valida¸c˜oes no c´odigo. Logo, nestes casos ´e melhor tratar o problema antes dele acontecer. No caso do Exemplo 6, uma solu¸c˜ao seria verificar se o denominador ´e dife-rente de zero, assim:
1 p u b l i c s t a t i c void ca lcu lar () {
2 System . out . println (" I n i c i a n d o c ´a lculo ... ") ; 3 int x = 10 , y = 0 , z ;
4 if( y != 0) { 5 z = x / y ;
6 System . out . println (" R e s u l t a d o : " + z ) ;
7 } else {
8 System . out . println (" Erro ao efetuar o c ´a lculo : divis ˜a o por zero . ") ;
9 }
10 System . out . println (" Fim do c ´a lculo . ") ;
11 }
4.1
Hierarquia das exce¸
c˜
oes
No Exemplo 6, o bloco catch faz a captura de exce¸c˜oes que s˜ao objetos da classe ArithmeticException. Todas as exce¸c˜oes do Java s˜ao objetos que pertencem `a uma hie-rarquia de classes, que est´a representada parcialmente na Figura 1, contendo as principais classes de exce¸c˜oes:
Esta hierarquia divide as exce¸c˜oes em diferentes tipos:
Figura 1: Hierarquia das Exce¸c˜oes.
Exemplo: falta de mem´oria. Normalmente n˜ao ´e previsto a recupera¸c˜ao deste tipo de falha, embora seja poss´ıvel.
• RuntimeException: ´e usado para indicar condi¸c˜oes que n˜ao foram definidas no projeto de um sistema. Por este motivo o compilador n˜ao obriga que elas sejam tratadas, pois s˜ao geradas somente em tempo de execu¸c˜ao.
• Exception: s˜ao erros previs´ıveis. Estas exce¸c˜oes obrigam o m´etodo chamador a trat´a-las. Caso elas n˜ao forem tratadas, o compilador ir´a acusar um erro de compila¸c˜ao.
O tratamento de todas as exce¸c˜oes poss´ıveis torna o sistema robusto, por´em esta ´e uma tarefa dif´ıcil de fazer e nem sempre ´e vi´avel. O desenvolvedor deve ficar atento para perceber onde o tratamento de exce¸c˜oes deve ser mais rigoroso, a fim de encontrar um equil´ıbrio entre qualidade e volume de c´odigo.
Importante: quando um bloco catch for definido para tratar uma exce¸c˜ao, este mesmo catch pode tratar todas as exce¸c˜oes que s˜ao de classes filhas a partir da classe definida. Aqui tamb´em vale a regra do polimorfismo. Onde cabe uma m˜ae cabe uma filha. Para tratar exce¸c˜oes filhas separadamente das m˜aes, ´e necess´ario colocar blocos catch distintos para cada uma delas, sendo que as filhas devem vir antes (acima) das m˜aes.
4.2
Delegando exce¸
c˜
oes
seja, o m´etodo repassa a exce¸c˜ao para o m´etodo que o chamou, deixando para o chamador a responsabilidade de tratar a exce¸c˜ao. Esta forma tamb´em ´e chamada de tratamento pendente de exce¸c˜oes.
Esta t´ecnica deve ser utilizada somente nos casos que fazem sentido repassar a exce¸c˜ao. Isto deve ser definido a n´ıvel de projeto e n˜ao h´a regras para seu uso.
Em Java, essa pendˆencia do tratamento de uma exce¸c˜ao ´e definida pela palavra reservadathrows, mostrada no pr´oximo exemplo.
Exemplo 7
O m´etodo imprimir(...) poderia delegar a exce¸c˜ao para seu chamador ao inv´es de trat´a-la dentro de seu pr´oprio corpo, da seguinte maneira:
1 p u b l i c s t a t i c void i m p r i m i r A r r a y () t h r o w s A r r a y I n d e x O u t O f B o u n d s E x c e p t i o n
{
2 System . out . println (" I n i c i a n d o impress ˜a o ... ") ; 3 int[] e l e m e n t o s = new int[10];
4 for (int i = 0; i <= 15; i ++) { 5 e l e m e n t o s [ i ] = i ;
6 System . out . println ( i ) ;
7 }
8 System . out . println (" Fim da impress ˜a o . ") ;
9 }
Neste caso, o m´etodo preocupa somente com a l´ogica de neg´ocio, deixando o c´odigo mais limpo e claro. Enquanto que o m´etodo chamador deve tratar a exce¸c˜ao lan¸cada por este m´etodo.
´
E importante lembrar que quando uma exce¸c˜ao for gerada, a execu¸c˜ao c´odigo ser´a interrompida, mesmo se houver a pendˆencia do tratamento da exce¸c˜ao.
Exce¸c˜oes filhas de RuntimeException n˜ao necessitam ser declaradas para serem delegadas.
Caso for necess´ario o m´etodo declarar que ele pode lan¸car mais de uma exce¸c˜ao, elas devem ser separadas por v´ırgula depois dothrows.
Outra estrat´egia que pode ser utilizada ´e: o m´etodo trata algumas exce¸c˜oes e lan¸ca outras.
4.3
Lan¸
cando exce¸
c˜
oes
Em v´arios casos, o programador pode optar por “for¸car” o lan¸camento de uma exce¸c˜ao. Estes lan¸camentos s˜ao previamente conhecidos e deve ser feita dentro de m´etodos definidos com tratamento de exce¸c˜oes pendentes.
Com isso, quem chamar este m´etodo j´a sabe que existe exer¸c˜oes a serem tratadas e deve fornecer os recursos para isto, fazendo com que o chamador fique sabendo o que ocorreu, sem a necessidade de validar valores de retorno.
Exemplo 8
Voltando no m´etodosacar(...) da conta, ele ficaria da seguinte maneira:
1 p u b l i c void sacar (f l o a t valor ) { 2 if (this. saldo < valor ) {
3 I l l e g a l A r g u m e n t E x c e p t i o n e ;
4 e = new I l l e g a l A r g u m e n t E x c e p t i o n () ; 5 t h r o w e ;
6 } else {
7 this. saldo -= valor ;
8 }
9 }
Na linha 5 a exce¸c˜ao ´e lan¸cada. A execu¸c˜ao deste m´etodo ´e interrompida e o m´etodo chamador recebe a exce¸c˜ao instanciada aqui.
Para melhorar o programa, pode-se instanciar a exce¸c˜ao utilizando outros constru-tores, por exemplo indicando qual ´e a descri¸c˜ao/motivo da exce¸c˜ao. Um exemplo seria assim:
1 I l l e g a l A r g u m e n t E x c e p t i o n e = new I l l e g a l A r g u m e n t E x c e p t i o n (" Saldo
i n s u f i c i e n t e ! ") ;
No m´etodo chamador, ele trata a exce¸c˜ao e pode imprimir o conte´udo da mensagem:
1 Conta conta = new Conta (" Teste ", 1234) ; 2 conta . de pos ita (100) ;
3 try {
4 conta . sacar (200) ;
5 System . out . println (" Saque com sucesso ") ; 6 } c a t c h ( I l l e g a l A r g u m e n t E x c e p t i o n e ) { 7 System . out . println ( e . g e t M e s s a g e () ) ;
8 }
Transportando informa¸
c˜
oes em uma exce¸
c˜
ao
Os construtores das exce¸c˜oes Java normalmente aceitam umaString, um objeto de outra exce¸c˜ao ou ambos. Estas informa¸c˜oes s˜ao definidas quando um objeto de exce¸c˜ao ´e criado. Como as exce¸c˜oes podem ser repassadas para os m´etodos chamadores, elas podem carregar estas informa¸c˜oes atrav´es de toda a pilha de m´etodos at´e chegar de volta ao main.
Isto permite que se saiba, por exemplo nomain, qual ´e a descri¸c˜ao do erro, utilizando o m´etodo getMessage() da exce¸c˜ao ou qual ´e a exce¸c˜ao que causou esta, atrav´es do m´etodoe.getCause().
4.4
Exce¸
c˜
oes criadas pelo programador
permite que a exce¸c˜ao contenha valores espec´ıficos necess´arios que podem ser transporta-dos sendo ´uteis em v´arios cen´arios.
Para criar sua pr´opria exce¸c˜ao, basta criar uma classe que seja filha da classe Throwable, ou seja, herde de qualquer uma das classes de exce¸c˜oes j´a existentes. As-sim pode-se criar uma classe para indicar o saldo insuficiente da conta.
1 p u b l i c c l a s s S a l d o I n s u f i c i e n t e E x c e p t i o n e x t e n d s E x c e p t i o n { 2
3 p u b l i c S a l d o I n s u f i c i e n t e E x c e p t i o n ( String men sag em ) { 4 s u p e r( me nsa gem ) ;
5 }
6 }
Para utilizar esta classe, ´e necess´ario alterar o m´etodo sacar(...) para delegar e lan¸car a exce¸c˜ao.
1 p u b l i c void sacar (f l o a t valor ) t h r o w s S a l d o I n s u f i c i e n t e E x c e p t i o n { 2 if (this. saldo < valor ) {
3 t h r o w new S a l d o I n s u f i c i e n t e E x c e p t i o n (" Saldo i n s u f i c i e n t e para sacar
" + valor ) ;
4 } else {
5 this. saldo -= valor ;
6 }
7 }
E o m´etodo que usa a conta necessita tratar a exce¸c˜aoSaldoInsuficienteException apropriadamente:
1 Conta conta = new Conta (" Teste ", 1234) ; 2 conta . de pos ita (100) ;
3 try {
4 conta . sacar (200) ;
5 System . out . println (" Saque com sucesso ") ; 6 } c a t c h ( S a l d o I n s u f i c i e n t e E x c e p t i o n e ) { 7 System . out . println ( e . g e t M e s s a g e () ) ;
8 }
Observa¸c˜ao importante: N˜ao deixar o bloco do catch vazio!!!
Exerc´ıcio 1 –
Utilizando o c´odigo da classe
Conta
fazer as seguintes
altera¸c˜oes:
1. Alterar o c´odigo de acordo com as ´ultimas 3 listagens.
2. Alterar o m´etodo transferirPara(...) para suportar as altera¸c˜oes.
3. Criar uma classe chamada ValorInvalidoException para validar os valores de saque, dep´osito e transferˆencia. O valor aceito deve ser maior que 0 e menor que 1000, caso contr´ario, os m´etodos devem lan¸car esta exce¸c˜ao.