Prévia do material em texto
Tratamento de Exceções DCC / ICEx / UFMG Eduardo Figueiredo http://www.dcc.ufmg.br/~figueiredo Tratamento de Exceção � Uma exceção é uma indicação de problema na execução do programa � Exceção foge ao fluxo normal. Ou seja, não ocorre com frequência � O objetivo do Tratamento de Exceção (TE) é manter o programa funcionando � Ao invés de encerrar abruptamente � Aumenta a robustez Exemplos de Exceções � ArrayIndexOutOfBoundsException � Ocorre quando se tenta acessar um elemento fora dos limites de um array � ClassCastException � Ocorre quando se tenta fazer coerção equivocada de um objeto � NullPointerException � Ocorre quando uma referência null é usada, quando se espera um objeto Vantagens de TE � Tentar barrar todas as possibilidades de erros usando condições (if) � Prejudica a legibilidade do código � É pouco eficiente � O tratamento de exceção deixa o código mais legível e eficiente � Permite programas mais robustos e tolerantes a falhas Exemplos com e sem Tratamento de Exceções Exemplo: Divide1 (sem TE) public class Divide1 { public static int quotient(int numerator, int denominator) { return numerator / denominator; } public static void main(String[] args) { Scanner scanner = new Scanner( System.in ); System.out.print( "Digite o numerador: " ); int numerator = scanner.nextInt(); System.out.print( "Digite o denominador: " ); int denominator = scanner.nextInt(); int result = quotient( numerator, denominator ); System.out.println("Resultado: " + numerator + " / " + denominator + " = " + result); } } Pode ocorrer divisão por zero. Pode ser digitado um valor não numérico. Saídas do Programa � Nenhum problema ocorre � Resultado: 10 / 2 = 5 � Divisão por zero Exception in thread “main” java.lang.ArithmeticException / by zero at Divide1.quotient(Divide1.java:10) at Divide1. main(Divide1.java:22) Tipo de erro. Pilha de execução do erro. Saída do Programa � Tipo digitado não numérico Exception in thread “main” java.util.InputMismatchException at java.util.Scanner.throwFor(Scanner.java:819) at java.util.Scanner.next(Scanner.java:1431) at java.util.Scanner.nextInt(Scanner.java:2040) at java.util.Scanner.nextInt(Scanner.java:2000) at Divide1.main(Divide1.java:20 ) Identificação do provável local do erro. Classe.método(Arquivo:linha) Exemplo: Divide2 (com TE) public class Divide2 { public static int quotient(int numerator, int denominator) throws ArithmeticException { return numerator / denominator; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); try { System.out.print("Digite o numerador: "); int numerator = scanner.nextInt(); System.out.print("Digite o denominador: "); int denominator = scanner.nextInt(); int result = quotient( numerator, denominator ); System.out.println("Resultado: " + result); } catch (InputMismatchException inputMismatchException) { System.out.println("Devem ser digitados números inteiros."); } catch (ArithmeticException arithmeticException) { System.out.println("Zero é um denominador inválido."); } } } Sinalização da exceção ArithmeticException. Indica que o método quotient pode lançar tal exceção. Exemplo: Divide2 (com TE) public class Divide2 { public static int quotient(int numerator, int denominator) throws ArithmeticException { return numerator / denominator; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); try { System.out.print("Digite o numerador: "); int numerator = scanner.nextInt(); System.out.print("Digite o denominador: "); int denominator = scanner.nextInt(); int result = quotient( numerator, denominator ); System.out.println("Resultado: " + result); } catch (InputMismatchException inputMismatchException) { System.out.println("Devem ser digitados números inteiros."); } catch (ArithmeticException arithmeticException) { System.out.println("Zero é um denominador inválido."); } } } Região protegida try. Indica que exceções deste bloco serão tratados. Exemplo: Divide2 (com TE) public class Divide2 { public static int quotient(int numerator, int denominator) throws ArithmeticException { return numerator / denominator; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); try { System.out.print("Digite o numerador: "); int numerator = scanner.nextInt(); System.out.print("Digite o denominador: "); int denominator = scanner.nextInt(); int result = quotient( numerator, denominator ); System.out.println("Resultado: " + result); } catch (InputMismatchException inputMismatchException) { System.out.println("Devem ser digitados números inteiro s."); } catch (ArithmeticException arithmeticException) { System.out.println("Zero é um denominador inválido."); } } } Tratadores catch. Indica como cada exceção deve ser tratada. Saídas do Programa � Nenhum problema ocorre � Resultado: 10 / 2 = 5 � Divisão por zero (ArithmeticException) � Zero é um denominador inválido. � Tipo digitado não numérico (InputMismatchException) � Devem ser digitados números inteiros. Try - Catch - Finally Instrução de Tratamento � O tratamento de exceção em Java é feito pela instrução try-catch-finally � Região protegida (try) � Tratadores (catch) � Finalizador (finally) � As mesmas regras de escopo de Java são aplicadas � Variáveis locais a um bloco não são visíveis fora do bloco Região Protegida (try) � A região protegida (bloco try) inclui o código que pode lançar uma exceção � Exceções lançadas são tratadas pelos tratadores (catch) � Nem todos os comandos da região protegida lançam exceções � Os comandos da região protegida representam a execução normal do programa Exemplo: Divide2 (com TE) public class Divide2 { public static int quotient(int numerator, int denominator) throws ArithmeticException { return numerator / denominator; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); try { System.out.print("Digite o numerador: "); int numerator = scanner.nextInt(); System.out.print("Digite o denominador: "); int denominator = scanner.nextInt(); int result = quotient( numerator, denominator ); System.out.println("Resultado: " + result); } catch (InputMismatchException inputMismatchException) { System.out.println("Devem ser digitados números inteiros."); } catch (ArithmeticException arithmeticException) { System.out.println("Zero é um denominador inválido."); } } } Podem lançar InputMismatchException. Pode lançar ArithmeticException. Tratadores (catch) � Um tratador captura e trata uma exceção � Inicia com a palavra reservada catch � Indica a exceção tratada entre parênteses � Inclui um bloco de comandos para tratar a exceção � Pelo menos um tratador catch (ou finalizador) deve seguir imediatamente após uma região protegida try Exceção Não Capturada � Nem toda exceção lançada pelo programa necessita ser capturada e tratada � Algumas exceções podem ser ignoradas � Se lançadas e não tratadas, exceções causam interrupção do programa � Exceções lançadas e engolidas � Uma má prática de programação é engolir exceções. � Tratador vazio (bloco de comandos vazio) Bloco Finalizador (finally) � São principalmente utilizados para evitar vazamento de recursos � Devolver conexão com banco de dados � Fechar arquivos abertos � Devolver os recursos de rede, etc. � O bloco finalizador sempre é executado, independente do lançamento de uma exceção Sintaxe de TE try { // comandos que lançam exceções } catch ( /* Exceção Tratada 1 */ ) { // comandos que tratam a exceção 1 } catch ( /* Exceção Tratada 2 */ ) { // comandos que tratam a exceção 2 } … // outros tratadores finally { // comandos que liberam recursos } Hierarquia de Exceções Hierarquia de Exceções � A hierarquia de exceções de Java possui centenas de classes � Vide API � Toda exceção deve estender direta ou indiretamente a classe Throwable � Throwable estende Object Hierarquia de Exceções Object RuntimeException IndexOutOfBoundsException ArrayIndexOutOfBoundsExceptionException Throwable ClassCastException NoSuchElementException ... InputMismatchException NullPointerException Error Subclasses de Throwable � Exception � Representa situações excepcionais que podem ocorrer nos programas � Podem ser capturadas e tratadas � Error � Representa situações anormais da máquina virtual Java (JVM) � Não são capturados em aplicativos � Raramente ocorrem Exceções não Verificadas � Exceções que não precisam ser sinalizadas, capturadas ou tratadas � O compilador Java não verifica se tais exceções podem ser lançadas � Exceções não verificadas devem herdar direta ou indiretamente de RuntimeException � Error são consideradas exceções não verificadas Exceções Verificadas � O compilador impõe a restrição capturar ou sinalizar para exceções verificadas � Toda exceção que estende Exception e não estende RuntimeException � Portanto, exceções verificadas devem ser capturadas (catch) ou sinalizadas (throws) Comportamento Polimórfico � Tratadores escritos para capturar uma superclasse, também tratam as subclasses � Pode-se tratar as subclasses como se fossem do tipo da superclasse � Opcionalmente, pode-se capturar individualmente cada exceção específica � Por exemplo, se o tratamento for diferente Sinalização de Exceção throw e throws Sinalização de Exceção � Cláusula throws � Indica exceções que um método lança � Tais exceções não são tratadas no corpo do método � Comando throw � Usado para lançar uma exceção � Indica que algo errado ocorreu no fluxo de execução do programa Cláusula throws � A cláusula throws sinaliza quais exceções o método lança � Aparece na assinatura do método depois da lista de parâmetros � O método pode lançar várias exceções � A lista de exceções deve ser separada por vírgula � O método também lança exceções de métodos que o sobrescrevem Exemplo: Divide2 (com TE) public class Divide2 { public static int quotient(int numerator, int denominator) throws ArithmeticException { return numerator / denominator; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); try { System.out.print("Digite o numerador: "); int numerator = scanner.nextInt(); System.out.print("Digite o denominador: "); int denominator = scanner.nextInt(); int result = quotient( numerator, denominator ); System.out.println("Resultado: " + result); } catch (InputMismatchException inputMismatchException) { System.out.println("Devem ser digitados números inteiros."); } catch (ArithmeticException arithmeticException) { System.out.println("Zero é um denominador inválido."); } } } Sinalização da exceção ArithmeticException. Indica que o método quotient pode lançar tal exceção. Comando throw � É usado para indicar que uma exceção ocorreu no programa � Lança a exceção (nova ou existente) � Uma nova exceção pode ser criada pelo comando new � Throw especifica qual exceção será lançada � Pode ser qualquer objeto de classes que herdam de Throwable Exemplo: UsingExceptions public class UsingExceptions { public static void main( String[] args ) { try { throwException(); } catch (Exception exception) { System.err.println("Exception handled in method main"); } } public static void throwException() throws Exception { try { System.out.println( "Method throwException" ); throw new Exception(); } catch (Exception exception) { System.err.println("Exception handled in method throwException" ); throw exception; } finally { System.out.println( "Finally executed in throwException" ); } } } Cria e lança uma nova exceção. Re-lança a exceção capturada. Exemplo: UsingExceptions public class UsingExceptions { public static void main( String[] args ) { try { throwException(); } catch (Exception exception) { System.err.println("Exception handled in method main"); } } public static void throwException() throws Exception { try { System.out.println( "Method throwException" ); throw new Exception(); } catch (Exception exception) { System.err.println("Exception handled in method throwException" ); throw exception; } finally { System.out.println( "Finally executed in throwExcepti on" ); } } } O bloco finally é executado independente da exceção ter sido lançada. Exemplo: UsingExceptions public class UsingExceptions { public static void main( String[] args ) { try { throwException(); } catch (Exception exception) { System.err.println("Exception handled in method main") ; } } public static void throwException() throws Exception { try { System.out.println( "Method throwException" ); throw new Exception(); } catch (Exception exception) { System.err.println("Exception handled in method throwEx ception" ); throw exception; } finally { System.out.println( "Finally executed in throwException" ); } } } Pode-se usar “System.err” (ao invés de “System.out”) para direcionar as mensagens, por exemplo, para um arquivo de log. Saída de UsingExceptions Method throwException Exception handled in method throwException Finally executed in throwException Exception handled in method main Modelo de Execução � Quando uma exceção é lançada, o fluxo de execução é desviado para o tratador � Os comandos do bloco try não terminam � Mesmo depois de executar o tratador, o fluxo não retorna ao bloco try � A execução prossegue para o bloco finalizador (finally) ou para o comando imediatamente depois do último tratador Término Excepcional � Quando um método termina com uma exceção, nenhum valor é retornado � O método apenas lança a exceção que deve ser tratada em algum método da cadeia de chamadas � Se ninguém tratar a exceção, o programa termina abruptamente Novas Exceções � Novos tipos de exceção podem ser criadas RuntimeException Exception Throwable Error MinhaExcecaoChecada MinhaExcecaoNaoChecada Bibliografia da Aula � DEITEL, H. M.; DEITEL P. J. Java: Como Programar , 8a. Edição. Pearson, 2010. � Capítulo 11 Tratamento de Exceções