Perguntas e Respostas sobre Exceções Java

O Java fornece uma abordagem robusta e orientada a objetos para lidar com cenários de exceção conhecidos como Tratamento de Exceção em Java. Algum tempo atrás, escrevi um longo post sobre Tratamento de Exceção em Java e hoje estou listando algumas Questões Importantes de Exceções em Java com Respostas para ajudá-lo em entrevistas.

  1. O que é uma Exceção em Java?
  2. Quais são as Palavras-chave de Tratamento de Exceção em Java?
  3. Explique a Hierarquia de Exceções em Java?
  4. Quais são os métodos importantes da Classe de Exceção em Java?
  5. Explique o Recurso ARM do Java 7 e o bloco multi-catch?
  6. Qual é a diferença entre Exceções Verificadas e Não Verificadas em Java?
  7. Qual é a diferença entre as palavras-chave throw e throws em Java?
  8. Como escrever exceções personalizadas em Java?
  9. O que é OutOfMemoryError em Java?
  10. Quais são os diferentes cenários que causam “Exceção na thread principal”?
  11. Qual é a diferença entre final, finally e finalize em Java?
  12. O que acontece quando uma exceção é lançada pelo método principal?
  13. Podemos ter um bloco catch vazio?
  14. Fornecer algumas melhores práticas de manipulação de exceção em Java?
  15. Qual é o problema com os programas abaixo e como podemos corrigi-los?

1. O que é uma exceção em Java?

Uma exceção é um evento de erro que pode ocorrer durante a execução de um programa e interrompe seu fluxo normal. A exceção pode surgir de diferentes tipos de situações, como entrada de dados incorreta pelo usuário, falha de hardware, falha na conexão de rede, etc. Sempre que ocorre algum erro durante a execução de uma instrução java, um objeto de exceção é criado, e então JRE tenta encontrar um manipulador de exceção para lidar com a exceção. Se um manipulador adequado for encontrado, então o objeto de exceção é passado para o código do manipulador para processar a exceção, conhecido como capturar a exceção. Se nenhum manipulador for encontrado, então a aplicação lança a exceção para o ambiente de tempo de execução e a JRE termina o programa. A estrutura de manipulação de exceção em Java é usada para lidar apenas com erros de tempo de execução, erros de compilação não são tratados pela estrutura de manipulação de exceção.

2. Quais são as palavras-chave de manipulação de exceção em Java?

Há quatro palavras-chave usadas na manipulação de exceções em Java.

  1. throw: Às vezes, explicitamente queremos criar um objeto de exceção e então lançá-lo para interromper o processamento normal do programa. A palavra-chave throw é usada para lançar exceções para a execução para lidar com elas.
  2. throws: Quando estamos lançando uma exceção verificada em um método e não a tratando, então precisamos usar a palavra-chave throws na assinatura do método para informar ao programa chamador as exceções que podem ser lançadas pelo método. O método chamador pode lidar com essas exceções ou propagá-las para seu método chamador usando a palavra-chave throws. Podemos fornecer várias exceções na cláusula throws e ela pode ser usada também com o método main().
  3. try-catch: Usamos o bloco try-catch para manipulação de exceções em nosso código. try é o início do bloco e catch está no final do bloco try para lidar com as exceções. Podemos ter vários blocos catch com um try e blocos try-catch também podem ser aninhados. O bloco catch requer um parâmetro que deve ser do tipo Exception.
  4. finalmente: O bloco finally é opcional e pode ser usado apenas com um bloco try-catch. Como a exceção interrompe o processo de execução, pode haver alguns recursos abertos que não serão fechados, então podemos usar o bloco finally. O bloco finally é sempre executado, independentemente de ocorrer uma exceção ou não.

3. Explique a Hierarquia de Exceções em Java?

As exceções do Java são hierárquicas e a herança é usada para categorizar diferentes tipos de exceções. Throwable é a classe pai da Hierarquia de Exceções do Java e possui dois objetos filhos – Error e Exception. As exceções são divididas ainda em exceções verificadas e exceções de tempo de execução. Erros são cenários excepcionais que estão fora do escopo da aplicação e não é possível antecipar e recuperar deles, por exemplo, falha de hardware, falha do JVM ou erro de falta de memória. Exceções verificadas são cenários excepcionais que podemos antecipar em um programa e tentar recuperar deles, por exemplo, FileNotFoundException. Devemos capturar essa exceção e fornecer uma mensagem útil ao usuário e registrá-la adequadamente para fins de depuração. Exception é a classe pai de todas as Exceções Verificadas. Exceções de Tempo de Execução são causadas por má programação, por exemplo, tentar recuperar um elemento do Array. Devemos verificar o comprimento do array primeiro antes de tentar recuperar o elemento, caso contrário, poderá lançar ArrayIndexOutOfBoundException em tempo de execução. RuntimeException é a classe pai de todas as exceções de tempo de execução.

4. Quais são os métodos importantes da classe Java Exception?

Exception e todas as suas subclasses não fornecem métodos específicos, e todos os métodos são definidos na classe base Throwable.

  1. String getMessage() – Este método retorna a mensagem String de Throwable, e a mensagem pode ser fornecida ao criar a exceção através de seu construtor.
  2. String getLocalizedMessage() – Este método é fornecido para que subclasses possam substituí-lo e fornecer mensagens específicas para o local ao programa chamador. A implementação da classe Throwable deste método simplesmente usa o método getMessage() para retornar a mensagem de exceção.
  3. synchronized Throwable getCause() – Este método retorna a causa da exceção ou null se a causa for desconhecida.
  4. String toString() – Este método retorna informações sobre Throwable no formato de String, a String retornada contém o nome da classe Throwable e a mensagem localizada.
  5. void printStackTrace() – Este método imprime as informações da pilha de rastreamento no fluxo de erro padrão. Este método é sobrecarregado e podemos passar PrintStream ou PrintWriter como argumento para escrever as informações da pilha de rastreamento no arquivo ou fluxo.

5. Explicar a funcionalidade ARM do Java 7 e o bloco multi-catch?

Se estiver capturando muitas exceções em um único bloco try, você notará que o código do bloco catch parece muito feio e consiste principalmente em código redundante para registrar o erro. Tendo isso em mente, uma das características do Java 7 foi o bloco multi-catch, onde podemos capturar várias exceções em um único bloco catch. O bloco catch com essa funcionalidade se parece com o seguinte:

catch(IOException | SQLException | Exception ex){
     logger.error(ex);
     throw new MyException(ex.getMessage());
}

Na maioria das vezes, usamos o bloco finally apenas para fechar os recursos e às vezes esquecemos de fechá-los, o que pode resultar em exceções em tempo de execução quando os recursos são esgotados. Essas exceções são difíceis de depurar e pode ser necessário examinar cada lugar onde estamos usando esse tipo de recurso para garantir que estamos fechando-o. Portanto, uma das melhorias do Java 7 foi a funcionalidade try-with-resources, onde podemos criar um recurso na própria instrução try e usá-lo dentro do bloco try-catch. Quando a execução sai do bloco try-catch, o ambiente de execução fecha automaticamente esses recursos. Um exemplo de bloco try-catch com essa melhoria é:

try (MyResource mr = new MyResource()) {
            System.out.println("MyResource created in try-with-resources");
        } catch (Exception e) {
            e.printStackTrace();
        }

Leia mais sobre isso em Java 7 ARM.

6. Qual é a diferença entre Exceções Verificadas e Não Verificadas em Java?

  1. Exceções Verificadas devem ser tratadas no código usando o bloco try-catch, caso contrário, o método deve usar a palavra-chave throws para informar ao chamador sobre as exceções verificadas que podem ser lançadas pelo método. Exceções Não Verificadas não precisam ser tratadas no programa ou mencionadas na cláusula throws do método.
  2. Exception é a superclasse de todas as exceções verificadas, enquanto RuntimeException é a superclasse de todas as exceções não verificadas. Note que RuntimeException é a subclasse de Exception.
  3. Exceções verificadas são cenários de erro que precisam ser tratados no código, caso contrário, você receberá um erro de compilação. Por exemplo, se você usar FileReader para ler um arquivo, ele lança FileNotFoundException e devemos capturá-lo no bloco try-catch ou lançá-lo novamente para o método chamador. Exceções não verificadas são principalmente causadas por programação inadequada, por exemplo, NullPointerException ao chamar um método em uma referência de objeto sem garantir que não seja nula. Por exemplo, posso escrever um método para remover todas as vogais da string. É responsabilidade do chamador garantir que não passe uma string nula. Posso alterar o método para lidar com esses cenários, mas idealmente o chamador deve cuidar disso.

7. Qual é a diferença entre a palavra-chave throw e throws em Java?

A palavra-chave throws é usada na assinatura do método para declarar as exceções que o método pode lançar, enquanto a palavra-chave throw é usada para interromper o fluxo do programa e passar o objeto de exceção para o tempo de execução para lidar com ele.

8. Como escrever exceções personalizadas em Java?

Podemos estender a classe Exception ou qualquer uma de suas subclasses para criar nossa própria classe de exceção personalizada. A classe de exceção personalizada pode ter suas próprias variáveis e métodos que podemos usar para passar códigos de erro ou outras informações relacionadas à exceção para o manipulador de exceção. Um exemplo simples de uma exceção personalizada é mostrado abaixo.

package com.journaldev.exceptions;

import java.io.IOException;

public class MyException extends IOException {

	private static final long serialVersionUID = 4664456874499611218L;
	
	private String errorCode="Unknown_Exception";
	
	public MyException(String message, String errorCode){
		super(message);
		this.errorCode=errorCode;
	}
	
	public String getErrorCode(){
		return this.errorCode;
	}
	

}

9. O que é OutOfMemoryError em Java?

OutOfMemoryError em Java é uma subclasse de java.lang.VirtualMachineError e é lançada pela JVM quando ela fica sem memória heap. Podemos corrigir esse erro fornecendo mais memória para executar a aplicação Java por meio de opções Java. $>java MeuPrograma -Xms1024m -Xmx1024m -XX:PermSize=64M -XX:MaxPermSize=256m

10. Quais são os diferentes cenários que causam “Exception in thread main”?

Alguns dos cenários comuns de exceção na thread principal são:

  • Exception in thread main java.lang.UnsupportedClassVersionError: Esta exceção ocorre quando sua classe Java é compilada a partir de outra versão do JDK e você está tentando executá-la a partir de outra versão do Java.
  • Exception in thread main java.lang.NoClassDefFoundError: Existem duas variantes dessa exceção. A primeira é quando você fornece o nome completo da classe com a extensão .class. O segundo cenário é quando a classe não é encontrada.
  • Exception in thread main java.lang.NoSuchMethodError: main: Esta exceção ocorre quando você está tentando executar uma classe que não possui o método main.
  • Exceção na linha “main” java.lang.ArithmeticException: Sempre que uma exceção é lançada do método principal, ela imprime a exceção no console. A primeira parte explica que uma exceção é lançada do método principal, a segunda parte imprime o nome da classe de exceção e depois de dois pontos, imprime a mensagem de exceção.

Leia mais sobre isso em Java Exception na linha principal.

11. Qual é a diferença entre final, finally e finalize em Java?

final e finally são palavras-chave em java, enquanto finalize é um método. A palavra-chave final pode ser usada com variáveis de classe para que elas não possam ser reatribuídas, com a classe para evitar extensões por outras classes e com métodos para evitar a substituição por subclasses, a palavra-chave finally é usada com o bloco try-catch para fornecer instruções que sempre serão executadas mesmo se alguma exceção ocorrer, geralmente finally é usado para fechar recursos. O método finalize() é executado pelo Coletor de Lixo antes que o objeto seja destruído, é uma ótima maneira de garantir que todos os recursos globais sejam fechados. Dos três, apenas finally está relacionado ao tratamento de exceções em java.

12. O que acontece quando uma exceção é lançada pelo método principal?

Quando uma exceção é lançada pelo método main(), o tempo de execução do Java termina o programa e imprime a mensagem de exceção e o rastreamento de pilha no console do sistema.

13. Podemos ter um bloco catch vazio?

Podemos ter um bloco catch vazio, mas é um exemplo de má programação. Nunca devemos ter um bloco catch vazio porque, se a exceção for capturada por esse bloco, não teremos informações sobre a exceção e será um pesadelo para depurá-la. Deve haver pelo menos uma instrução de log para registrar os detalhes da exceção no console ou nos arquivos de log.

14. Forneça algumas Melhores Práticas de Manipulação de Exceção em Java?

Algumas das melhores práticas relacionadas à Manipulação de Exceção em Java são:

  • Use Exceções Específicas para facilitar a depuração.
  • Lance exceções cedo (Falhe-Rápido) no programa.
  • Capture exceções tarde no programa, deixe o chamador lidar com a exceção.
  • Use o recurso ARM do Java 7 para garantir que os recursos sejam fechados ou use o bloco finally para fechá-los corretamente.
  • Sempre registre mensagens de exceção para fins de depuração.
  • Use o bloco multi-catch para um fechamento mais limpo.
  • Use exceções personalizadas para lançar um único tipo de exceção da API de sua aplicação.
  • Siga a convenção de nomenclatura, sempre termine com Exception.
  • Documente as exceções lançadas por um método usando @throws no javadoc.
  • As exceções são custosas, então lance-as apenas quando fizer sentido. Caso contrário, você pode capturá-las e fornecer uma resposta nula ou vazia.

Leia mais sobre elas em detalhes em Práticas Recomendadas de Tratamento de Exceção em Java.

15. Qual é o problema com os programas abaixo e como podemos corrigi-lo?

Nesta seção, vamos examinar algumas perguntas de programação relacionadas a exceções em Java.

  1. Qual é o problema com o programa abaixo?

    package com.journaldev.exceptions;
    
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    public class TestException {
    
    	public static void main(String[] args) {
    		try {
    			testExceptions();
    		} catch (FileNotFoundException | IOException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	
    	
    	public static void testExceptions() throws IOException, FileNotFoundException{
    		
    	}
    }
    

    O programa acima não irá compilar e você receberá uma mensagem de erro como “A exceção FileNotFoundException já está capturada pela IOException alternativa”. Isso ocorre porque FileNotFoundException é uma subclasse de IOException, existem duas maneiras de resolver esse problema. A primeira maneira é usar um único bloco catch para ambas as exceções.

    		try {
    			testExceptions();
    		}catch(FileNotFoundException e){
    			e.printStackTrace();
    		}catch (IOException  e) {
    			e.printStackTrace();
    		}
    

    Outra maneira é remover o FileNotFoundException do bloco de captura múltipla.

    		try {
    			testExceptions();
    		}catch (IOException  e) {
    			e.printStackTrace();
    		}
    

    Você pode escolher qualquer uma dessas abordagens com base no código do seu bloco catch.

  2. Qual é o problema com o programa abaixo?

    package com.journaldev.exceptions;
    
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    import javax.xml.bind.JAXBException;
    
    public class TestException1 {
    
    	public static void main(String[] args) {
    			try {
    				go();
    			} catch (IOException e) {
    				e.printStackTrace();
    			} catch (FileNotFoundException e) {
    				e.printStackTrace();
    			} catch (JAXBException e) {
    				e.printStackTrace();
    			}
    	}
    
    	public static void go() throws IOException, JAXBException, FileNotFoundException{
    		
    	}
    }
    

    O programa não compilará porque FileNotFoundException é uma subclasse de IOException, então o bloco de captura de FileNotFoundException é inacessível e você receberá uma mensagem de erro como “Bloco de captura inacessível para FileNotFoundException. Já é tratado pelo bloco de captura para IOException”. Você precisa corrigir a ordem do bloco de captura para resolver esse problema.

    			try {
    				go();
    			} catch (FileNotFoundException e) {
    				e.printStackTrace();
    			} catch (IOException e) {
    				e.printStackTrace();
    			} catch (JAXBException e) {
    				e.printStackTrace();
    			}
    

    Note que JAXBException não está relacionada a IOException ou FileNotFoundException e pode ser colocada em qualquer lugar na hierarquia de blocos de captura acima.

  3. Qual é o problema com o programa abaixo?

    package com.journaldev.exceptions;
    
    import java.io.IOException;
    
    import javax.xml.bind.JAXBException;
    
    public class TestException2 {
    
    	public static void main(String[] args) {
    		try {
    			foo();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}catch(JAXBException e){
    			e.printStackTrace();
    		}catch(NullPointerException e){
    			e.printStackTrace();
    		}catch(Exception e){
    			e.printStackTrace();
    		}
    	}
    
    	public static void foo() throws IOException{
    		
    	}
    }
    

    O programa não irá compilar porque JAXBException é uma exceção verificada e o método foo() deveria lançar essa exceção para ser capturada no método chamador. Você receberá uma mensagem de erro como “Bloco catch inalcançável para JAXBException. Esta exceção nunca é lançada do corpo da declaração try”. Para resolver esse problema, você terá que remover o bloco catch de JAXBException. Observe que capturar NullPointerException é válido porque é uma exceção não verificada.

  4. Qual é o problema com o programa abaixo?

    package com.journaldev.exceptions;
    
    public class TestException3 {
    
    	public static void main(String[] args) {
    		try{
    		bar();
    		}catch(NullPointerException e){
    			e.printStackTrace();
    		}catch(Exception e){
    			e.printStackTrace();
    		}
    		
    		foo();
    	}
    
    	public static void bar(){
    		
    	}
    	
    	public static void foo() throws NullPointerException{
    		
    	}
    }
    

    Esta é uma pergunta de truque, não há problema com o código e ele irá compilar com sucesso. Sempre podemos capturar uma Exception ou qualquer exceção não verificada, mesmo que não esteja na cláusula throws do método. Da mesma forma, se um método (foo) declara uma exceção não verificada na cláusula throws, não é obrigatório lidar com ela no programa.

  5. Qual é o problema com o programa abaixo?

    package com.journaldev.exceptions;
    
    import java.io.IOException;
    
    public class TestException4 {
    
    	public void start() throws IOException{		
    	}
    	
    	public void foo() throws NullPointerException{
    		
    	}
    }
    
    class TestException5 extends TestException4{
    	
    	public void start() throws Exception{
    	}
    	
    	public void foo() throws RuntimeException{
    		
    	}
    }
    

    O programa acima não vai compilar porque a assinatura do método start() não é a mesma na subclasse. Para corrigir esse problema, podemos alterar a assinatura do método na subclasse para ser exatamente a mesma da superclasse ou podemos remover a cláusula throws do método da subclasse, como mostrado abaixo.

    @Override
    	public void start(){
    	}
    
  6. Qual é o problema com o programa abaixo?

    package com.journaldev.exceptions;
    
    import java.io.IOException;
    
    import javax.xml.bind.JAXBException;
    
    public class TestException6 {
    
    	public static void main(String[] args) {
    		try {
    			foo();
    		} catch (IOException | JAXBException e) {
    			e = new Exception("");
    			e.printStackTrace();
    		}catch(Exception e){
    			e = new Exception("");
    			e.printStackTrace();
    		}
    	}
    
    	public static void foo() throws IOException, JAXBException{
    		
    	}
    }
    

    O programa acima não irá compilar porque o objeto de exceção no bloco de captura múltipla é final e não podemos alterar seu valor. Você receberá um erro de tempo de compilação como “O parâmetro e de um bloco de captura múltipla não pode ser atribuído”. Temos que remover a atribuição de “e” para um novo objeto de exceção para resolver este erro. Leia mais em Java 7 bloco de captura múltipla.

Isso é tudo para as questões de entrevista de exceção em Java, espero que você goste delas. Vou adicionar mais à lista no futuro, certifique-se de marcá-la para uso futuro.

Source:
https://www.digitalocean.com/community/tutorials/java-exception-interview-questions-and-answers