DCC / ICEx / UFMG Tratamento de Exceção Uma exceção é uma indicação de problema na execução do programa Tratamento de Exceções 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 Eduardo Figueiredo http://www.dcc.ufmg.br/~figueiredo Ao invés de encerrar abruptamente Aumenta a robustez Exemplos de Exceções Vantagens de TE ArrayIndexOutOfBoundsException Tentar barrar todas as possibilidades de erros usando condições (if) Ocorre quando se tenta acessar um elemento fora dos limites de um array Prejudica a legibilidade do código É pouco eficiente ClassCastException Ocorre quando se tenta fazer coerção equivocada de um objeto O tratamento de exceção deixa o código mais legível e eficiente Permite programas mais robustos e tolerantes a falhas NullPointerException Ocorre quando uma referência null é usada, quando se espera um objeto Exemplo: Divide1 (sem TE) public class Divide1 { Exemplos com e sem Tratamento de Exceções public static int quotient(int numerator, int denominator) { return numerator / denominator; } Pode ocorrer divisão por zero. public static void main(String[] args) { Scanner scanner = new Scanner( System.in ); System.out.print( "Digite o numerador: " ); Pode ser digitado um int numerator = scanner.nextInt(); valor não numérico. System.out.print( "Digite o denominador: " ); int denominator = scanner.nextInt(); int result = quotient( numerator, denominator ); System.out.println("Resultado: " + numerator + " / " + denominator + " = " + result); } } Saídas do Programa Nenhum problema ocorre Resultado: 10 / 2 = 5 Divisão por zero Tipo de erro. Exception in thread “main” java.lang.ArithmeticException / by zero at Divide1.quotient(Divide1.java:10) at Divide1. main(Divide1.java:22) Pilha de execução do erro. Exemplo: Divide2 (com TE) public class Divide2 { public static int quotient(int numerator, int denominator) throws ArithmeticException { Sinalização da exceção return numerator / denominator; } ArithmeticException. Indica public static void main(String[] args) { que o método quotient Scanner scanner = new Scanner(System.in); pode lançar tal exceção. 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."); } }} 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 { Tratadores catch. System.out.print("Digite o numerador: "); Indica como cada int numerator = scanner.nextInt(); System.out.print("Digite o denominador: "); exceção deve ser tratada. 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."); } }} 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; Região protegida try. } Indica que exceções deste public static void main(String[] args) { bloco serão tratados. 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."); } }} 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. Instrução de Tratamento Try - Catch - Finally 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 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 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(); Podem lançar System.out.print("Digite o denominador: "); InputMismatchException. int denominator = scanner.nextInt(); int result = quotient( numerator, denominator ); Pode lançar System.out.println("Resultado: " + result); ArithmeticException. } catch (InputMismatchException inputMismatchException) { System.out.println("Devem ser digitados números inteiros."); } catch (ArithmeticException arithmeticException) { System.out.println("Zero é um denominador inválido."); } }} 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 Pelo menos um tratador catch (ou finalizador) deve seguir imediatamente após uma região protegida try 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 A hierarquia de exceções de Java possui centenas de classes Hierarquia de Exceções Vide API Toda exceção deve estender direta ou indiretamente a classe Throwable Throwable estende Object Hierarquia de Exceções Subclasses de Throwable Exception Object Throwable Error ... Exception ClassCastException RuntimeException NullPointerException IndexOutOfBoundsException NoSuchElementException ArrayIndexOutOfBoundsException InputMismatchException 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 Sinalização de Exceção Pode-se tratar as subclasses como se fossem do tipo da superclasse Opcionalmente, pode-se capturar individualmente cada exceção específica throw e throws Por exemplo, se o tratamento for diferente 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 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 Comando throw Usado para lançar uma exceção Indica que algo errado ocorreu no fluxo de execução do programa 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) Comando throw public class Divide2 { public static int quotient(int numerator, int denominator) throws ArithmeticException { Sinalização da exceção return numerator / denominator; } ArithmeticException. Indica public static void main(String[] args) { que o método quotient pode Scanner scanner = new Scanner(System.in); lançar tal exceção. 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."); } }} É 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(); Exemplo: UsingExceptions public class UsingExceptions { public static void main( String[] args ) { try { throwException(); } } catch (Exception exception) { System.err.println("Exception handled in method main"); O bloco finally é executado independente da exceção ter sido lançada. 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(); public static void throwException() throws Exception { try { System.out.println( "Method throwException" ); throw new Exception(); } Cria e lança uma nova exceção. } catch (Exception exception) { System.err.println("Exception handled in method throwException" ); throw exception; Re-lança a exceção catch (Exception exception) { System.err.println("Exception handled in method throwException" ); throw exception; } } capturada. finally { System.out.println( "Finally executed in throwException" ); finally { System.out.println( "Finally executed in throwException" ); } } } } } } Exemplo: UsingExceptions public class UsingExceptions { public static void main( String[] args ) { try { 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 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(); Exception handled in method throwException Finally executed in throwException } catch (Exception exception) { System.err.println("Exception handled in method throwException" ); throw exception; } finally { System.out.println( "Finally executed in throwException" ); } } } Exception handled in method main Modelo de Execução Término Excepcional 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 Novas Exceções 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 Bibliografia da Aula Novos tipos de exceção podem ser criadas Throwable Error DEITEL, H. M.; DEITEL P. J. Java: Como Programar, 8a. Edição. Pearson, 2010. Capítulo 11 Tratamento de Exceções Exception RuntimeException MinhaExcecaoChecada MinhaExcecaoNaoChecada