Seja bem-vindo a série de postagens sobre a certificação Java. Como funciona, o que fazer para comprar, marcar o dia da prova e o principal, o que estudar.
Parte 7 – Exceptions
Controle de fluxo de exceções é tão importante quanto a própria orientação a objetos. Veremos como está o seu conhecimento sobre exceptions para que seja possível obter sucesso na prova.
Objetivos do exame
- Checked, runtime e errors
- Bloco try catch
- Classes comuns de exceção
- Utilizando throw e throws Exception
Exceptions
Uma exceção é um evento que ocorre durante a execução de um programa que interrompe o fluxo normal de instruções.
Classe Throwable e suas subclasses
Os objetos que herdam da classe Throwable incluem descendentes diretos (objetos que herdam diretamente da classe Throwable) e descendentes indiretos (objetos que herdam de filhos ou netos da classe Throwable). A figura abaixo ilustra a hierarquia de classes da classe Throwable e suas subclasses mais significativas. Como você pode ver, Throwable tem dois descendentes diretos: Error e Exception.
Fonte: https://docs.oracle.com/javase/tutorial/essential/exceptions/throwing.html
Classe Error
Quando ocorre uma falha de vinculação dinâmica ou outra falha grave na máquina virtual Java, a máquina virtual lança um erro. Os programas simples geralmente não capturam nem lançam erros.
Classe Exception
A maioria dos programas lança e captura objetos derivados da classe Exception. Uma exceção indica que ocorreu um problema, mas não é um problema grave do sistema. A maioria dos programas que você escreve lançará e capturará exceções em vez de erros.
Checked exceptions
Uma checked exception inclui a classe Exception e todas as subclasses que não estendem de RuntimeException. Exceções verificadas tendem a ser antecipadas forçando que seja verificado em tempo de compilação.
void showMessage(String msg) throws Exception { if (msg == null){ throw new Exception("Message is null"); } System.out.println("Message: " + msg); }Quem chamar showMessage deve tratar a exceção ou lançar para a chamada anterior.
public static void main(String[] args) throws Exception { new TestExceptions().showMessage("hello!"); }
Unchecked exceptions
Exceções não verificadas (runtime exceptions) estendem da classe RuntimeException e não é obrigatório o seu tratamento pelo programador. Vejamos o mesmo exemplo do método showMessage.
void showMessage(String msg) { if (msg == null) { throw new NullPointerException("Message is null"); } System.out.println("Message: " + msg); }
Agora não precisamos declarar o throws Exception na assinatura do método, e quem chamar o método também não precisará verificar a exceção em tempo de compilação.
Obs: Não estamos discutindo qual seria a melhor estratégia para o controle de exceções. O que está sendo exposto é o que precisamos saber para a prova (a diferença entre Checked e Unchecked exceptions)
Lançando exceções
No exame, podemos ver duas situações onde devemos saber que uma exceção será lançada. Uma RuntimeException ou uma Exception lançada explicitamente.
public static void main(String[] args) { System.out.println(args[0]); }O código acima se executado sem passar nenhum parâmetro, fará com que uma RuntimeException seja lançada pela JVM.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
throw new NullPointerException("Message is null");
Este outro trecho de código fará com que o fluxo seja interrompido e fará com que o programa retorne para a chamada anterior ou para algum bloco que capture esta exceção.
Utilizando o try
Try serve basicamente para dizer ao seu código que alguma situação pode ocorrer e que você precisa captura-la para tomar alguma ação. Isso pode ocorrer em uma situação em que você chamou um método que lança exceções verificadas ou é necessário fazer alguma conversão que possa causar uma exceção dentro do bloco try. É obrigatório o uso das chaves após a palavra chave try e também um bloco catch ou finally para que seja considerado válido. A estrutura mínima de um bloco try:
try {
} catch (Exception e) {
}
try {} catch (Exception e) {
}
} finally {
}
Utilizar apenas o bloco try é um erro de compilação:
try {
}// não compila
Utilizando o finally
O bloco finally pode ser executado em duas situações:
- Se uma exceção é lançada, o bloco finally é executado após o bloco catch.
- Se nenhuma exceção for lançada, o bloco finally será executado após a conclusão do bloco try.
public static void main(String[] args) { try { showMessage(null); } finally { System.out.println("finally"); } } static void showMessage(String msg) { if (msg == null) { throw new NullPointerException("Message is null"); } System.out.println("Message: " + msg); }
O código acima irá executar, imprimir finally no console e lançar a exceção para a JVM, pois como é uma RuntimeException, não fomos obrigados a capturar a exceção.
Lembre-se: O bloco finally sempre deve ser o último bloco do try. Existe uma forma onde o bloco finally não é executado. Quando um comando System.exit(0); é invocado, a JVM para de executar naquele momento.
try { showMessage(null); } catch (Exception e) { System.out.println("catch"); System.exit(0); } finally { System.out.println("finally"); }No código acima, o bloco finally nunca será executado.
Capturando exceções
Podemos ter vários blocos catch entre os blocos try e finally, porém devemos obedecer algumas regras:
- Você deve ser capaz de reconhecer se a exceção é uma checked exception ou uma unchecked exception.
- Você precisa determinar se alguma das exceções são subclasses das outras. Neste caso, as exceções mais especialistas (subclasses) devem vir antes.
Lembre-se que no máximo um bloco catch será executado e será o primeiro bloco de captura que pode manipulá-lo.
try { showMessage(null); } catch (Exception e) { System.out.println("catch"); } catch (NullPointerException n) { //Erro
System.out.println("null");
} finally { System.out.println("finally"); }
O código acima não compila pois um NullPointerException estende de Exception que será capturado primeiro. Para que o código compile, é necessário colocar a exceção mais genérica após.
try { showMessage(null); } catch (NullPointerException n) { //OK
System.out.println("null"); } catch (Exception e) { System.out.println("catch"); } finally { System.out.println("finally"); }
O que acontece se lançarmos uma exceção dentro de um bloco try? Perceba que após a chamada do método showMessage, lançamos uma RuntimeException que será capturada pelo nosso bloco catch (Exception e).
public static void main(String[] args) { try { showMessage("test"); throw new RuntimeException("catch it!"); } catch (NullPointerException n) { System.out.println("null"); } catch (Exception e) { System.out.println("catched"); } finally { System.out.println("finally"); } } static void showMessage(String msg) { if (msg == null) { throw new NullPointerException("Message is null"); } System.out.println("Message: " + msg); }
A execução do código acima resulta em:
Message: test
catched
finally
Message: test
catched
finally
Tipos de exceção
Para o exame, você deverá ser capaz de reconhecer os três tipos de exceção. Exceções de tempo de execução (runtime exceptions ou unchecked exceptions), exceções verificadas (checked exceptions) e erros.
Runtime (Unchecked) exceptions
Não precisam ser capturadas ou declaradas. Elas podem ser lançadas pelo programador ou pela própria JVM:
Checked Exceptions
Devem ser manipuladas ou declaradas. São filhas de Exception com exceção da RuntimeException. As exceções a seguir aparecem no exame: FileNotFoundException e IOException. Você deve saber que FileNotFoundException estende de IOException.
Errors
Erros estendem a classe Error. Eles são lançados pela JVM e não devem ser manipulados ou declarados. Erros que devemos conhecer: ExceptionInInitializerError, StackOverflowError e NoClassDefFoundError.
Métodos que lançam exceções
Se você chamar um método que lança exceções, você deve verificar se a exceção é do tipo checked ou uncheked. Isso faz a diferença entre o código compilar ou não.
public class MyException extends Exception { }
public class MyClass { public static void main(String[] args) { MyClass myClass = new MyClass(); myClass.showCode(200); //Não compila } private void showCode(int code) throws MyException { System.out.println("The code is: " + code); } }
O código acima não compila, pois o método showCode lança uma checked exception. E por mais que não seja dado um throw dentro do método, o que importa para quem esta chamando, é a assinatura do método. Para que o código compile, temos duas alternativas:
1 - Lançar a exceção para cima (throws):
public static void main(String[] args) throws MyException {
2 - Capturar a exceção:
try { myClass.showCode(200); } catch (MyException e) { e.printStackTrace(); }
E quando um método não lança uma exceção, eu posso capturar?
private void showCode(int code) { System.out.println("The code is: " + code); }
try { myClass.showCode(200); } catch (MyException e) { //Não compila
e.printStackTrace();
}
O código acima não compila pois nunca será lançada uma checked exception como esperado. Alterando o código acima para uma Exception ou RuntimeExeption, o código compila normalmente.
Exceções e Herança
Quando uma classe sobrescreve um método de uma superclasse ou implementa um método de uma interface, não é permitido adicionar novas checked exceptions à assinatura do método.
public class MyClass { public void showCode(int code) { System.out.println("The code is: " + code); } } class SubClass extends MyClass { public void showCode(int code) throws MyException { super.showCode(code); } }
O método showCode da classe SubClass não compila pois esta declarando que lança uma exceção verificada não compatível com o método da classe pai.
Uma subclasse tem permissão para declarar menos exceções do que a superclasse ou interface. Isso é legal porque os chamadores já estão lidando com isso.
public class MyClass { public void showCode(int code) throws Exception { System.out.println("The code is: " + code); } } class SubClass extends MyClass { public void showCode(int code) throws MyException { System.out.println("code"); } }
A subclasse está sobrescrevendo um método que declara uma exceção mais genérica do que esta declarando na versão sobrescrita. Isto é perfeitamente legal. Assim como é legal também a subclasse declarar uma uncheked exception e a classe pai não declarar nada.
public class MyClass { public void showCode(int code) { System.out.println("The code is: " + code); } } class SubClass extends MyClass { public void showCode(int code) throws RuntimeException { System.out.println("code"); } }
O código acima compila sem problemas, pois a subclasse lança uma RuntimeException.
Resumo
E com isso fechamos mais um capítulo dos nossos estudos. Lembre-se que para o exame você precisa saber:
- Diferenciar entre uma uncheked exception e uma checked exception
- Entender como funciona o bloco try
- Identificar se uma exceção é lançada pelo programador ou pela JVM
- Declarar métodos que declaram exceções
- Reconhecer quando usar throw e throws
Bons estudos e até a próxima!
Comentários
Postar um comentário