Exceções Aula 8 (3h) Exceções em Java Exceções LPR1211 SCC361 v.1 Exceções verificadas e não verificadas Tópicos: exceções em Java, exceções verificadas e não verificadas, criação, lançamento e tratamento de exceções, cláusula throws, fluxo de controle, propagação automática de exceções Criação e lançamento de exceções Objetivos: exercitar a definição de exceções e seus respectivos tratadores e a construção de aplicações bem-estruturadas e capazes de lidar com erros. Fluxo de Controle Pré-requisitos: classes e objetos, fundamentos de programação estruturada, herança e subtipos. Cláusula throws Tratadores de exceção Propagação Automática de Exceções Cláusula finally LPR1211 SCC361 v.1 Exceções - Definição Exceções Exceções constituem uma maneira clara de representar situações excepcionais (ou condições de erros) sem poluir o código fonte; i.e, um dos objetivos de um mecanismo de tratamento de exceções é simplificar o código fonte através da separação do processo normal das rotinas de tratamento de exceções. As exceções que um método pode sinalizar são parte explícita do contrato de serviço do método. Dessa forma, a lista de exceções pode ser vista pelos clientes do método, verificada pelo compilador e preservada por classes derivadas que redefinem o método. Exceções podem sinalizar erros de um programa de maneira direta, evitando que o programador tenha que declarar variáveis do tipo “flag” de erros. LPR1211 SCC361 v.1 Uma exceção é lançada (“thrown”). A exceção é pega (“caught”) e tratada por um tratador de exceção ( “exception handler” ). LPR1211 SCC361 v.1 1 Representação de Exceções em Java - ( I ) Representação de Exceções em Java ( II ) Exceções são objetos, o que oferece uma maior flexibilidade para a inclusão de informações relativas ao contexto da ocorrência da exceção dentro do próprio objeto. Exceções que são subclasses de Error indicam situações irrecuperáveis que normalmente não podem ser tratadas pela aplicação. Ela contém dados e suas classes podem definir métodos que manipulam esses dados. Essas exceções não são geradas pelo programa do usuário. Exemplo: (1) Condição de erro na própria máquina virtual Java que ficou, por exemplo, sem memória(OutOfMemoryError). Exemplo: A classe Throwable (figura M6.1) inclui um campo String que armazena uma mensagem de erro que descreve a condição excepcional. Essa mensagem é criada quando o objeto de exceção é criado e pode ser lida com o método getMessage(). (2) Um arquivo de classe que foi corrompido e não pode ser lido. Classes de exceções são derivadas da classe Throwable ou de uma de suas subclasses. LPR1211 SCC361 v.1 OBS - Este tipo de exceção pode ser pega e tratada mas é raro isto acontecer. LPR1211 SCC361 v.1 Representação de Exceções em Java- ( III ) Exceções Verificadas X Não Verificadas - ( I ) Objetos de exceções do tipo Error (e seus subtipos), assim como os do tipo RuntimeException, representam exceções nãoverificadas (unchecked exceptions). Exceções que são subclasses de Exception indicam condições de erro menos severas que podem ser pegas e tratadas. Exceções não-verificadas – normalmente não precisam ser tratadas pelo programa. Exemplos : - erros de entrada/saída do pacote java.io (IOException); - erro de divisão por zero (ArithmeticException); - uso de referência nula (NullPointerException); - indicação de que o programa tentou ler uma posição inexistente de um array (ArrayIndexOutofBoundsException). LPR1211 SCC361 v.1 Qualquer método pode lançar uma exceção não-verificada a qualquer hora. Exemplo(1):não existe uma forma de predizer quando a exceção OutOfMemoryError irá ocorrer. LPR1211 SCC361 v.1 Exemplo(2):qualquer método que usa objetos ou arrays pode lançar a exceção NullPointerException, caso seja passado um argumento null inválido. 2 Exceções Verificadas X Não Verificadas - ( II ) Criação e Lançamento de Exceções - ( I ) Objetos de exceções do tipo Exception (e seus subtipos) representam exceções verificadas (checked exceptions), a menos que sejam subclasses de RuntimeException (RuntimeException é uma subclasse de Exception). Recomenda-se que exceções definidas pela aplicação sejam representadas por objetos da classe Exception ou de uma de suas subclasses. Exceção verificadas – precisam ser tratadas pelo programa e surgem somente em situações específicas e bem definidas. Eventualmente uma aplicação pode definir exceções derivadas da classe Error, caso sejam consideradas irrecuperáveis, mas isto deve ser evitado sempre que possível. Exemplo: se você tenta ler uma informação de um arquivo, você deve considerar a possibilidade de que a exceção FileNotFoundException seja lançada caso o arquivo não seja encontrado. LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 Criação e Lançamento de Exceções - ( II ) Criação e Lançamento de Exceções - ( III ) • Criação de uma classe de exceção derivada de Exception Código Java : public class MinhaExcecao extends Exception { private String attr; public MinhaExcecao( ) { } public MinhaExcecao(String nome) { super(nome); this.attr = nome; } } LPR1211 SCC361 v.1 Figura M6 -1 LPR1211 SCC361 v.1 3 Criação e Lançamento de Exceções - ( IV ) Criação e Lançamento de Exceções( V ) A criação de um novo tipo de exceção é justificada pela adição de nova informação útil para seu tratamento posterior. A classe MinhaExcecao estende Exception para adicionar um construtor que recebe um parâmetro String e também um atributo attr para armazenar o parâmetro recebido pelo construtor. Outra justificativa para a criação de um novo tipo de exceção é que o tipo da exceção é uma informação importante no seu novo processo de tratamento, pois exceções são pegas pelos tratadores através do seu tipo. O construtor invoca o construtor da superclasse passando uma mensagem que descreve a condição excepcional. Uma exceção é criada através do comando new como qualquer outro objeto. Exemplo: MinhaExcecao e = new MinhaExcecao (“nome”); LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 Assinatura de Métodos - Cláusula throws - ( I ) Criação e Lançamento de Exceções( VI ) Definir: exceção externa • Lançamento de um objeto de exceção Java exige que uma exceção verificada que é lançada externamente seja declarada na assinatura do método através da cláusula throws. throw e ; Exemplo public void m( ) throws MinhaExcecao{... throw new MinhaExcecao(); ... } • Outras opções: throw new MinhaExcecao( ); throw new MinhaExcecao(“nome” ); O cliente do método m( ) precisa saber quais são as possíveis exceções lançadas por m( ), no caso do contrato de serviço implementado por m( ) não poder ser cumprido devido à ocorrência de uma condição excepcional. LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 4 Assinatura de Métodos - Cláusula throws - ( II ) Cláusula throws - ( III ) • Exemplo: void public m( ) throws Exception{ (...) throw new MinhaExcecao( ); } Um método pode lançar diversas exceções externas. Exemplo void public m( ) E1, E2, E3 {...} Você pode lançar na implementação de m() exceções que são derivadas das classes de exceções declaradas na cláusula throws. Figura M6 -2 LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 Cláusula throws - (IV) Cláusula throws - (V) O contrato definido pela cláusula throws é estritamente imposto, i.e., você pode lançar apenas exceções de tipos que estejam declarados na cláusula throws. Um método pode lançar diferentes tipos de exceções e declarar apenas o supertipo na cláusula throws. O lançamento de qualquer outro tipo de exceção verificada que não esteja declarada na cláusula throws (a menos que seja uma exceção derivada) é inválido. Essa prática deve ser usada com cuidado pois informação útil para os clientes que invocam m( ) é escondida, uma vez que eles podem não saber quais possíveis subtipos podem ser lançados. O fato de um método não possuir uma cláusula throws implica que nenhuma exceção verificada pode ser lançada. Exceções derivadas de Error e RuntimeException são as únicas exceções que não precisam ser listadas na cláusula throws de um método pois elas são exceções não-verificadas. Para fins de documentação, a cláusula throws deve ser completa e específica tanto quanto possível. Qualquer método pode potencialmente lançar uma exceção não-verificada e nesses casos o compilador não verifica se elas não são tratadas. LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 5 Definição de Tratadores de Exceção - ( I ) (Cláusula try / catch) Definição de Tratadores de Exceção - ( II ) (Cláusula try / catch) O primeiro bloco, logo após a palavra try delimita a porção de código para a qual os tratadores definidos no comando serão aplicados. • Tratadores de Exceções são definidos pelo comando try/catch . • Exemplo: try{ System.out.println(“ Entrou no bloco try”); if (condicao_excepcionalX) throw new MinhaExcecao(“descrição de X”); System.out.println(“Saiu normalmente do bloco try”); } catch(MinhaExcecao e) { System.out.println(“Executando tratador”); } LPR1211 SCC361 v.1 A cláusula catch define um tratador para exceções de um tipo escolhido. Exceções lançadas são associadas a tratadores pelo tipo de exceção definida na cláusula catch. A exceção é associada ao primeiro catch imediatamente depois da cláusula try, cujo parâmetro seja da mesma classe da exceção gerada ou de alguma de suas superclasses. LPR1211 SCC361 v.1 Definição de Tratadores de Exceção - ( III ) (Cláusula try / catch) Definição de Tratadores de Exceção - ( IV) (Cláusula try / catch) Quando um tratador é encontrado, ele é executado e nenhuma outra cláusula catch é executada. • Havendo mais de uma cláusula catch para um mesmo bloco try, elas devem estar ordenadas da mais específica para a mais genérica. try { Qualquer número de cláusulas catch pode estar associado a um bloco try (incluindo zero); desde que cada cláusula trate tipos diferentes de exceções. Exception .... } catch(MinhaOutraExcecao e ) { ...} catch(MinhaExcecao e ) { ...} catch(Exception e ) { ...} Uma cláusula catch geral (um tratador genérico), isto é, que captura exceções do tipo Exception, é considerada uma escolha pobre de implementação pois o tratador captura qualquer tipo de exceção. MinhaExcecao MinhaOutraExcecao LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 6 Definição de Tratadores de Exceção (Cláusula try / catch) Definição de Tratadores de Exceção (Cláusula try / catch) Exceções podem ser tratadas e depois regeneradas dentro das cláusula catch usando-se a cláusula throw sem operando final do tratador. Exceções de tipos diferentes também podem ser lançadas dentro de um tratador. Exemplo try { ... } catch(MinhaOutraExcecao e ) { ...; throw ; } Em outras palavras, uma cláusula catch é uma espécie de método com apenas um único parâmetro (o tipo da exceção a ser pega) dentro do qual você pode tentar recuperar-se da condição excepcional ou você pode realizar operações de limpeza e relançar a exceção. Nesse caso, o controle da execução passa para o bloco try mais envolvente ou para o chamador do método. LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 O que acontece depois que o tratador é executado? Fluxo de Controle - ( I ) Uma das questões mais importantes de um mecanismo de tratamento de exceções é decidir como a seqüência de execução do programa é afetada após o lançamento de uma exceção. Existem 2 modelos básicos: modelo de continuação (resumption model) e modelo de terminação (termination model). Em ambos os modelos, o lançamento de uma exceção provoca o desvio do fluxo de execução para o tratador apropriado. A diferença entre eles reside no local de reinício do processamento normal, após o término do tratador. Figura M6 -3 LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 7 Fluxo de Controle - ( II ) Fluxo de Controle - ( III ) No modelo de terminação, o controle retorna para o comando seguinte ao final do bloco try/catch que contém o tratador que tratou a exceção. No modelo de continuação, a execução prossegue a partir do comando seguinte ao que provocou a exceção. Ou seja, o fluxo de execução normal original é mantido, como se não houvesse ocorrido a exceção. O fluxo de exceção original é interrompido no ponto em que ocorreu a exceção e reiniciado a partir do próximo bloco. Supõe-se que o tratador executado é capaz de mascarar a condição excepcional. Supõe-se que a ocorrência de uma exceção pode invalidar parte do fluxo original de execução. O modelo de terminação á adotado por Java e pala maioria das linguagens de programação modernas que oferecem suporte para tratamento de exceções. LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 Exemplo - Class A Exemplo - Class B class A { ... void fun1( ) throws E1 { ... try { comando1; throw new E1( ); comando2; throw new E2( ); comando3; } catch(E2) {...} comando4; LPR1211 SCC361 v.1 } // fim fun1( ) LPR1211 SCC361 v.1 class B { ... void fun2( ) { A a = new A( ); try { ... try { a.fun1( ); comando5; throw new E1( ); comando6; throw new E2( ); comando7; } catch(E1 e) {...} comando8; } // fim primeiro try catch(E2 e) {...} } // fim fun2( ) 8 Propagação Automática de Exceções - ( I ) Propagação Automática de Exceções - ( II ) Alguns mecanismos de tratamento de exceções obrigam o chamador a ter sempre um tratador para toda chamada de método que declare na sua assinatura algum tipo de exceção externa. Idealmente uma exceção deve ser tratada num ponto do programa o mais próximo possível daquele em que foi criada. Dessa forma, existe a tentativa inicial de produzir o resultado normal esperado pela chamada do método, evitando o retorno de uma resposta excepcional. Ou seja, o tratamento dado deve estar explícito no código fonte, mesmo que o tratador apenas pegue a exceção e a relance. Nessa situação, dizemos que a exceção foi mascarada, o que caracteriza uma exceção interna. Outros mecanismos, como o de Java, permitem que esses tratadores sejam omitidos. Se a geração de uma resposta normal não é possível; uma exceção externa é lançada para o chamador do serviço, que deve então tentar tratá-la. LPR1211 SCC361 v.1 O próprio mecanismo propaga automaticamente as exceções para as quais não sejam encontradas tratadores apropriados LPR1211 SCC361 v.1 Propagação Automática de Exceções - ( III ) Propagação Automática de Exceções - ( IV ) try { ..... bloco try Isso exige que o programador tenha uma disciplina mais rígida do uso do mecanismo para evitar que a propagação automática não ocorra de forma indiscriminada. throw E ( ) sequencia de cláusulas catch s Em Java, quando uma exceção ocorre dentro de um bloco try para o qual não existe uma cláusula catch apropriada, a execução do bloco try é interrompida, o bloco finally (se existir) é executado e a exceção é relançada agora como resultado da execução do comando try/catch. catch( ... ) { ... } ..... nenhum tratador para uma exceção do tipo E é encontrado ...... bloco finally ..... } throw // o mecanismo de Java relança a exceção para o // contexto mais envolvente proximo_comando; LPR1211 SCC361 v.1 Figura M6 -4 LPR1211 SCC361 v.1 9 Propagação Automática de Exceções - ( V ) Propagação Automática de Exceções - (VI) Se nenhum tratador for encontrado, a exceção é propagada para o chamador do método, depois dos tratadores dos comandos try envolvidos terem sido examinados. Caso o contexto mais envolvente seja um outro comando try/catch, o mesmo processo de propagação automática é repetido até que se encontre um tratador apropriado num comando try/catch ou se alcance um bloco do procedimento que não esteja encapsulado por um bloco try/catch. Se a chamada de um método está envolvida por outra cláusula try, a busca pelo tratador prosseguirá no conjunto de tratadores anexados ao comando try. Nesse momento, a fronteira do componente foi alcançada. A execução do método é interrompida e a exceção é lançada como resposta excepcional para o chamador do método. A propagação continua até que o chamador inicial seja encontrado (isto é, o programa principal). Nesse momento, a exceção torna-se externa. LPR1211 SCC361 v.1 Se nenhum tratador for encontrado, o programa é finalizado. LPR1211 SCC361 v.1 Propagação Automática de Exceções - (VII) Propagação Automática de Exceções - ( VIII ) Uma exceção externa(que não seja filha das classes Error ou RuntimeException) deve ser obrigatoriamente incluída na claúsula throws do método para que a propagação automática entre os métodos ocorra ..... try { m1( ) throws E { a.m1( ) } Um método que chama outro método que lista uma exceção verificada na sua cláusula throws tem 3 alternativas para tratar a exceção : m2 throws E { throw E ; try { b.m2( ); catch( E e) {...} } ...... (1) - pegá-la e tratá-la através de um comando try/catch; Propagação automática do E exceção externa E é lançada } (2) - pegar a exceção, executar comandos de limpeza, e então relançar a nova exceção. (3) - declarar o tipo da exceção na sua cláusula throws para habilitar a propagação automática de novo LPR1211 SCC361 v.1 } catch ( F f) { ...} Figura M6 -5 LPR1211 SCC361 v.1 10 Propagação Automática de Exceções - ( IX) Cláusula Finally ( I ) Em Java, não há tratador de execução genérico e também não é possível desativar uma exceção. A cláusula opcional finally define um trecho de código que é executado incondicionalmente na cláusula try; independente da ocorrência de alguma exceção. Durante a redefinição, se existirem tipos novos de exceções, eles devem ser derivados dos tipos declarados na cláusula throws do método original. Exemplo: fechamento de arquivo, recurso externo alocado que deve ser liberado. Caso contrário, não podem ser declaradas outros tipos de exceções além das listadas na cláusula throws do método original. No caso normal, isto é, quando nenhuma exceção é gerada, a cláusula finally é executada antes que o controle passe para o próximo comando. LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 Cláusula Finally ( II ) Cláusula Finally ( III ) try { No caso excepcional, se uma exceção é gerada dentro do bloco try e existe um tratador associado ao bloco para tratar a exceção, então o controle é primeiro transferido para a cláusula catch e depois para a cláusula finally. ... A cláusula opcional finally define um trecho de código que é executado incondicionalmente na cláusula try,independentemente da ocorrência de alguma exceção. finally {...} } catch (...) {...} catch (...) {...} • As cláusulas try e finally podem ser usadas juntas sem cláusula catch. Se não existe tratador local para a exceção; o controle é transferido para a cláusula finally e então a exceção é propagada para o contexto mais envolvente á procura de uma tratador. LPR1211 SCC361 v.1 • Nesse caso, o bloco finally é simplesmente um código de limpeza que tem garantia de ser sempre executado. LPR1211 SCC361 v.1 11 Exemplo 1 - Código - ( I ) Exemplo 1 - Código - ( II ) // arquivo ContaCor.java public ContaCor(String nome, double val, int num, int pwd){ class ContaCor { titular = nome; private int estado; // 1 = ativa ; 2 = inativa numConta = num; private int senha; senha = pwd; private int numConta; saldoAtual = val; private String titular; estado =1; //conta ativa private double saldoAtual; } // fim do construtor // pré condição : conta ativa e val>0 } // fim da classe ContaCor public boolean creditaValor (double val) {..} class Exemplo1{ static public void main (String Arg[ ]) { // pré condição : conta ativa e senha válida e val>0 e val >= saldoAtual } ContaCor c1 = new ContaCor (“Maria”, 500, 1,1); public boolean debitaValor (double val, int pwd) {..} double f = c1.getSaldo(1); public double getSaldo(int pwd) { System.out.println( “ Saldo Atual = “ +f); if (estado!=1) return (-1); // conta deve estar ativa } if (pwd !=senha) return(-1); // senha deve ser válida LPR1211 SCC361 v.1 return (saldoAtual); } } // fim da classe Exemplo 1 LPR1211 SCC361 v.1 Código Modificado da classe ContaCor Código Modificado do Prog. Principal Exemplo 1 class ContaCor{ ... class Exemplo1{ static public void main(String Arg [ ]){ double f; ContaCor c1 = new ContaCor ( “Maria”, 500, 1,1); try { f=c1.getSaldo(2); // senha inválida System.out.println (“Saldo Atual ...”); } catch(ContaCorExc e) { // tratador de exceção } } } // fim da classe Exemplo 1 modificado public double getSaldo(int pwd) throws ContaCorExc{ if (estado != 1) throw new ContaCorExc(ContaCorExc.GET_SALDO, ContaCorExc.CONTA_INATIVA, this); if (pwd != senha) throw new ContaCorExc(ContaCorExc.GET_SALDO, ContaCorExc.SENHA_INVALIDA, this); return(saldoAtual); } // fim do método getSaldo( ) modificado } // fim da classe ContaCor Modificada Figura M6 -6 LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 12 Exemplo 1 / Complementação - ( I ) Exemplo 1 / Complementação - ( II ) class ContaCorExc extends Exception { // dados da operação // nome das operacoes private ContaCor conta; public static final int CREDITA_VALOR = 1; public ContaCorExc(int op, int er, ContaCor cc) { public static final int DEBITA_VALOR = 2; operacao = op; public static final int GET_SALDO = 3; erro = er; private int operacao; // operação que criou a exceção conta = cc; // nome dos erros } // fim do construtor public static final int CONTA_INATIVA = 1; public int getOperacao ( ) { return operacao; } public static final int VALOR_INVALIDO = 2; public int getErro( ) {return erro; } public static final int SALDO_INSIFICIENTE = 3; } // fim da classe ContaCorExc public static final int SENHA_INVALIDA = 4; private int erro; //tipo erro LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 Exemplo 2 Exemplo 3 public static double fatorial(int x) { void acess (int b[ ], int m ) { if (X<0) int k = b[m]; throw new IllegalArgumentException( “ x deve ser positivo”); } double fat; for (fat=1.0; x>1; fat *= x; x - - ); / * vazio */ • Esse método pode gerar dois tipos de exceções não verificadas: (1) - Se a referência b para o array de inteiros for nula, então a exceção NullPointerException é gerada; return fat; } • A exceção do tipo IllegalArgumentException é do tipo RuntimeException. (2) - Se o índice m for menor que zero ou maior que (b.length-1),então a exceção IndexOutofBoundsException é gerada. • Por isso, esse tipo de exceção não precisa ser listado na cláusula throws do método, mesmo sendo lançado por ele (comando throw). (3) - As duas são filhas de RunTimeException. LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 13 Exemplo 4 Exemplo 5 O método calculaDist( ) recebe uma lista de valores inteiros positivos que representam notas de provas. A sequência de notas é terminada por um número inteiro negativo. As notas devem ser >=0 e =< 100. A saída é uma distribuição das notas numa das seguinte s categorias : 0-9, 10-19, 20-29, 30-399, 40-49, 50-59, 60-69, 7079, 80-89, 90-100. O método não fornece um tratador para exceção do tipo IOException, que pode ser lançada na chamada in.readLine(). Essa exceção é verificada (filha da classe Exception) e , portanto, o seu tipo deve estar listado na cláusula throws de clalculaDist( ). A exceção do tipo ArrayIndexOutofBoundsException é nãoverificada. public String readFirstLine (String filename) throws IOException { BufferedReader in = new BufferedReader (new FileReader(filename)); return in.readline ( ); } • A exceção do tipo IOException pode ser lançada pela chamada in.readline( ). • Como essa exceção é uma classe derivada de Exception (i.e., ela é verificada), então o método tem 2 alternativas de tratamento: (1) - pega a exceção e a trata; ou (2) - lista o seu tipo na sua cláusula throws. LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 Exemplo 5 - Código - ( I ) Exemplo 5 - Código- ( II ) Import java.io, *; class DistGrau { int novoGrau, indice, limite1, limite2; int [ ] freq = {0 0 0 0 0 0 0 0 0 0}; void calculaDist( ) throws IOException { DataInputStream in = newDataInputStream(System.in); novoGrau = 0; while (novoGrau>=0) { // sai ao encontrar o 10 número negativo System.out.println(“Entre com um grau”); novoGrau = Uinteger.parseInt(in.readLine( )); // pode lançar IOException if (novoGrau >= 0) { indice = novoGrau / 10; LPR1211 SCC361 v.1 LPR1211 SCC361 v.1 try { freq[indice] ++;} catch(ArrayIndexoutofBoundsException) { // exceção não verificada if (novoGrau = = 100) freq[9 ] ++; else System.out.println(“Erro” + novoGrau + “está fora da faixa”); } // fim do tratador } fim do if } fim do while System.out.println( “\n Frequência dos limites \n “); for (indice =0; indice <10; indice++) { limite1 =10 * indice; limite2 = limite1+9; if (indice = = 9) limite2=100; System.out.println(limite1+”-” limite2+”:” + freq[indice]); }// fim do for }// fim do método calculaDist } // fim da classe DistGrau 14