Preguntas y respuestas de entrevista sobre excepciones en Java

Java proporciona un enfoque sólido y orientado a objetos para manejar escenarios de excepción conocido como Manejo de Excepciones en Java. Hace algún tiempo, escribí una publicación larga sobre Manejo de Excepciones en Java y hoy estoy enumerando algunas Preguntas Importantes sobre Excepciones en Java con Respuestas para ayudarte en entrevistas.

  1. ¿Qué es una Excepción en Java?
  2. ¿Cuáles son las Palabras Clave de Manejo de Excepciones en Java?
  3. Explique la Jerarquía de Excepciones en Java
  4. ¿Cuáles son los métodos importantes de la Clase de Excepción en Java?
  5. Explique la Característica ARM de Java 7 y el bloque multi-catch
  6. ¿Cuál es la diferencia entre Excepciones Verificadas y No Verificadas en Java?
  7. ¿Cuál es la diferencia entre las palabras clave throw y throws en Java?
  8. ¿Cómo escribir excepciones personalizadas en Java?
  9. ¿Qué es OutOfMemoryError en Java?
  10. ¿Cuáles son los diferentes escenarios que causan “Excepción en el hilo principal”?
  11. ¿Cuál es la diferencia entre final, finally y finalize en Java?
  12. ¿Qué sucede cuando se lanza una excepción desde el método main?
  13. ¿Podemos tener un bloque catch vacío?
  14. ¿Podrías proporcionar algunas mejores prácticas de manejo de excepciones en Java?
  15. ¿Cuál es el problema con los programas a continuación y cómo lo solucionamos?

1. ¿Qué es una excepción en Java?

Una excepción es un evento de error que puede ocurrir durante la ejecución de un programa y altera su flujo normal. La excepción puede surgir de diferentes tipos de situaciones, como datos incorrectos ingresados por el usuario, fallo de hardware, fallo de conexión de red, etc. Cada vez que ocurre un error al ejecutar una instrucción en Java, se crea un objeto de excepción y luego el JRE intenta encontrar un controlador de excepciones para manejar la excepción. Si se encuentra un controlador de excepciones adecuado, entonces el objeto de excepción se pasa al código del controlador para procesar la excepción, conocido como captura de la excepción. Si no se encuentra ningún controlador, entonces la aplicación arroja la excepción al entorno de ejecución y el JRE termina el programa. El marco de manejo de excepciones en Java se utiliza para manejar solo errores en tiempo de ejecución, los errores en tiempo de compilación no son manejados por el marco de manejo de excepciones.

2. ¿Cuáles son las palabras clave de manejo de excepciones en Java?

Hay cuatro palabras clave utilizadas en el manejo de excepciones en Java.

  1. throw: A veces queremos crear explícitamente un objeto de excepción y luego lanzarlo para detener el procesamiento normal del programa. La palabra clave throw se utiliza para lanzar excepciones al tiempo de ejecución para manejarlas.
  2. throws: Cuando estamos lanzando alguna excepción verificada en un método y no la estamos manejando, entonces necesitamos usar la palabra clave throws en la firma del método para hacer que el programa llamador sepa las excepciones que podrían ser lanzadas por el método. El método llamador podría manejar estas excepciones o propagarlas a su método llamador utilizando la palabra clave throws. Podemos proporcionar múltiples excepciones en la cláusula throws y también se puede usar con el método main().
  3. try-catch: Usamos el bloque try-catch para el manejo de excepciones en nuestro código. try es el inicio del bloque y catch está al final del bloque try para manejar las excepciones. Podemos tener múltiples bloques catch con un try y los bloques try-catch también pueden estar anidados. El bloque catch requiere un parámetro que debe ser de tipo Exception.
  4. finalmente: El bloque finally es opcional y solo se puede usar con un bloque try-catch. Dado que la excepción detiene el proceso de ejecución, es posible que tengamos algunos recursos abiertos que no se cierren, por lo que podemos usar el bloque finally. El bloque finally siempre se ejecuta, ya sea que ocurra una excepción o no.

3. ¿Puede explicar la Jerarquía de Excepciones en Java?

Las excepciones de Java son jerárquicas y la herencia se utiliza para categorizar diferentes tipos de excepciones. Throwable es la clase padre de la jerarquía de excepciones de Java y tiene dos objetos hijos: Error y Exception. Las excepciones se dividen además en excepciones verificadas y excepciones de tiempo de ejecución. Los errores son escenarios excepcionales que están fuera del alcance de la aplicación y no es posible anticipar ni recuperarse de ellos, por ejemplo, fallo de hardware, colapso del JVM o error de falta de memoria. Las Excepciones Verificadas son escenarios excepcionales que podemos anticipar en un programa e intentar recuperarnos de ellos, por ejemplo, FileNotFoundException. Deberíamos capturar esta excepción y proporcionar un mensaje útil al usuario y registrarla adecuadamente para fines de depuración. Exception es la clase padre de todas las Excepciones Verificadas. Las Excepciones de Tiempo de Ejecución son causadas por una programación deficiente, por ejemplo, intentar recuperar un elemento del Array. Deberíamos verificar la longitud del array primero antes de intentar recuperar el elemento, de lo contrario podría lanzar ArrayIndexOutOfBoundsException en tiempo de ejecución. RuntimeException es la clase padre de todas las excepciones de tiempo de ejecución.

4. ¿Cuáles son los métodos importantes de la clase Java Exception?

Exception y todas sus subclases no proporcionan ningún método específico y todos los métodos están definidos en la clase base Throwable.

  1. String getMessage() – Este método devuelve la cadena de mensaje de Throwable y el mensaje puede ser proporcionado al crear la excepción a través de su constructor.
  2. String getLocalizedMessage() – Este método se proporciona para que las subclases puedan anularlo y proporcionar mensajes específicos de la localidad al programa que llama. La implementación de esta clase Throwable simplemente utiliza el método getMessage() para devolver el mensaje de excepción.
  3. synchronized Throwable getCause() – Este método devuelve la causa de la excepción o null si la causa es desconocida.
  4. String toString() – Este método devuelve la información sobre Throwable en formato de cadena, la cadena devuelta contiene el nombre de la clase Throwable y el mensaje localizado.
  5. void printStackTrace() – Este método imprime la información de la pila de rastreo en el flujo de error estándar, este método está sobrecargado y podemos pasar PrintStream o PrintWriter como argumento para escribir la información de la pila de rastreo en el archivo o flujo.

Explica la característica ARM de Java 7 y el bloque de multi-captura.

Si estás capturando muchas excepciones en un solo bloque try, notarás que el código del bloque catch se ve muy feo y en su mayoría consiste en código redundante para registrar el error. Teniendo esto en cuenta, una de las características de Java 7 fue el bloque de multi-captura, donde podemos capturar múltiples excepciones en un solo bloque catch. El bloque catch con esta característica se ve así:

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

La mayoría de las veces, usamos el bloque finally solo para cerrar los recursos y a veces olvidamos cerrarlos, lo que resulta en excepciones en tiempo de ejecución cuando los recursos se agotan. Estas excepciones son difíciles de depurar y podríamos necesitar revisar cada lugar donde estamos usando ese tipo de recurso para asegurarnos de cerrarlo. Entonces, una de las mejoras de Java 7 fue try-with-resources, donde podemos crear un recurso en la misma declaración try y usarlo dentro del bloque try-catch. Cuando la ejecución sale del bloque try-catch, el entorno de ejecución cierra automáticamente estos recursos. Un ejemplo de bloque try-catch con esta mejora es:

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

Lee más sobre esto en Java 7 ARM.

6. ¿Cuál es la diferencia entre Excepciones Comprobadas y No Comprobadas en Java?

  1. Las Excepciones Comprobadas deben ser manejadas en el código utilizando un bloque try-catch o, de lo contrario, el método debe utilizar la palabra clave throws para informar al llamador sobre las excepciones comprobadas que podrían ser lanzadas desde el método. Las Excepciones No Comprobadas no necesitan ser manejadas en el programa ni mencionarlas en la cláusula throws del método.
  2. Exception es la superclase de todas las excepciones comprobadas, mientras que RuntimeException es la superclase de todas las excepciones no comprobadas. Tenga en cuenta que RuntimeException es la clase secundaria de Exception.
  3. Las excepciones comprobadas son escenarios de error que requieren ser manejados en el código, de lo contrario, se obtendrá un error en tiempo de compilación. Por ejemplo, si se usa FileReader para leer un archivo, este arroja FileNotFoundException y debemos capturarlo en el bloque try-catch o lanzarlo nuevamente al método del llamador. Las excepciones no comprobadas son causadas principalmente por una programación deficiente, por ejemplo, NullPointerException al invocar un método en una referencia de objeto sin asegurarse de que no sea nulo. Por ejemplo, puedo escribir un método para eliminar todas las vocales de la cadena. Es responsabilidad del llamador asegurarse de no pasar una cadena nula. Puedo cambiar el método para manejar estos escenarios, pero idealmente, el llamador debería encargarse de esto.

7. ¿Cuál es la diferencia entre las palabras clave throw y throws en Java?

La palabra clave throws se utiliza con la firma del método para declarar las excepciones que el método podría lanzar, mientras que la palabra clave throw se utiliza para interrumpir el flujo del programa y pasar el objeto de excepción a tiempo de ejecución para que lo maneje.

8. ¿Cómo escribir excepciones personalizadas en Java?

Podemos extender la clase Exception o cualquiera de sus subclases para crear nuestra propia clase de excepción personalizada. La clase de excepción personalizada puede tener sus propias variables y métodos que podemos usar para pasar códigos de error u otra información relacionada con la excepción al controlador de excepciones. Un ejemplo simple de una excepción personalizada se muestra a continuación.

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. ¿Qué es OutOfMemoryError en Java?

OutOfMemoryError en Java es una subclase de java.lang.VirtualMachineError y se lanza por la JVM cuando se queda sin memoria de heap. Podemos solucionar este error proporcionando más memoria para ejecutar la aplicación Java a través de las opciones de Java. $>java MyProgram -Xms1024m -Xmx1024m -XX:PermSize=64M -XX:MaxPermSize=256m

10. ¿Cuáles son los diferentes escenarios que causan “Excepción en el hilo principal”?

Algunos de los escenarios comunes de excepción en el hilo principal son:

  • Excepción en el hilo principal java.lang.UnsupportedClassVersionError: Esta excepción ocurre cuando su clase Java está compilada desde otra versión de JDK y está intentando ejecutarla desde otra versión de Java.
  • Excepción en el hilo principal java.lang.NoClassDefFoundError: Hay dos variantes de esta excepción. La primera es cuando proporciona el nombre completo de la clase con la extensión .class. El segundo escenario es cuando no se encuentra la clase.
  • Excepción en el hilo principal java.lang.NoSuchMethodError: main: Esta excepción ocurre cuando intenta ejecutar una clase que no tiene el método main.
  • Excepción en el hilo “main” java.lang.ArithmeticException: Cada vez que se produce una excepción en el método principal, imprime la excepción en la consola. La primera parte explica que se produce una excepción en el método principal, la segunda parte imprime el nombre de la clase de la excepción y luego, después de dos puntos, imprime el mensaje de la excepción.

Lee más al respecto en Java Exception in thread main.

11. ¿Cuál es la diferencia entre final, finally y finalize en Java?

final y finally son palabras clave en Java, mientras que finalize es un método. La palabra clave final puede usarse con variables de clase para que no puedan ser reasignadas, con la clase para evitar la extensión por parte de otras clases y con métodos para evitar la sobrescritura por parte de subclases. La palabra clave finally se usa con bloques try-catch para proporcionar declaraciones que siempre se ejecutarán incluso si surge alguna excepción, generalmente finally se usa para cerrar recursos. El método finalize() es ejecutado por el Recolector de Basura (Garbage Collector) antes de que el objeto sea destruido, es una excelente manera de asegurarse de que todos los recursos globales estén cerrados. De los tres, solo finally está relacionado con el manejo de excepciones en Java.

12. ¿Qué sucede cuando se lanza una excepción por el método principal?

Cuando se lanza una excepción por el método main(), Java Runtime termina el programa e imprime el mensaje de excepción y la traza de la pila en la consola del sistema.

13. ¿Podemos tener un bloque catch vacío?

Podemos tener un bloque catch vacío, pero es un ejemplo de programación deficiente. Nunca deberíamos tener un bloque catch vacío porque si la excepción es capturada por ese bloque, no tendremos información sobre la excepción y será una pesadilla depurarla. Debería haber al menos una declaración de registro para registrar los detalles de la excepción en la consola o en los archivos de registro.

14. ¿Podría proporcionar algunas Mejores Prácticas para el Manejo de Excepciones en Java?

Algunas de las mejores prácticas relacionadas con el Manejo de Excepciones en Java son:

  • Utilizar Excepciones Específicas para facilitar la depuración.
  • Lanza Excepciones Temprano (Falla-Rápido) en el programa.
  • Captura Excepciones tarde en el programa, deja que el llamador maneje la excepción.
  • Utiliza la característica ARM de Java 7 para asegurarte de que los recursos se cierren o utiliza un bloque finally para cerrarlos adecuadamente.
  • Siempre registra mensajes de excepción para propósitos de depuración.
  • Utiliza un bloque multi-catch para un cierre más limpio.
  • Utiliza excepciones personalizadas para lanzar un solo tipo de excepción desde la API de tu aplicación.
  • Sigue la convención de nombres, siempre termina con Exception.
  • Documenta las Excepciones Generadas por un método utilizando @throws en javadoc.
  • Las excepciones son costosas, así que lánzalas solo cuando tenga sentido. De lo contrario, puedes capturarlas y proporcionar una respuesta nula o vacía.

Lee más sobre ellas en detalle en Prácticas Recomendadas para el Manejo de Excepciones en Java.

15. ¿Cuál es el problema con los programas siguientes y cómo lo solucionamos?

En esta sección, analizaremos algunas preguntas de programación relacionadas con excepciones en Java.

  1. ¿Cuál es el problema con el programa a continuación?

    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{
    		
    	}
    }
    

    El programa anterior no se compilará y obtendrás un mensaje de error que dice “La excepción FileNotFoundException ya está capturada por la IOException alternativa”. Esto se debe a que FileNotFoundException es una subclase de IOException, hay dos formas de resolver este problema. La primera manera es usar un único bloque catch para ambas excepciones.

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

    Otra forma es eliminar FileNotFoundException del bloque multi-catch.

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

    Puedes elegir cualquiera de estos enfoques según el código de tu bloque catch.

  2. ¿Cuál es el problema con el siguiente programa?

    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{
    		
    	}
    }
    

    El programa no compilará porque FileNotFoundException es una subclase de IOException, por lo que el bloque catch de FileNotFoundException es inalcanzable y obtendrás un mensaje de error que dice “Bloque catch inalcanzable para FileNotFoundException. Ya está manejado por el bloque catch para IOException”. Necesitas corregir el orden de los bloques catch para resolver este problema.

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

    Observa que JAXBException no está relacionada con IOException o FileNotFoundException y puede ubicarse en cualquier lugar de la jerarquía de bloques catch anterior.

  3. ¿Cuál es el problema con el siguiente programa?

    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{
    		
    	}
    }
    

    El programa no se compilará porque JAXBException es una excepción comprobada y el método foo() debería lanzar esta excepción para capturarla en el método de llamada. Obtendrás un mensaje de error como “Bloque catch inaccesible para JAXBException. Esta excepción nunca se lanza desde el cuerpo de la instrucción try”. Para resolver este problema, tendrás que eliminar el bloque catch de JAXBException. Observa que capturar NullPointerException es válido porque es una excepción no comprobada.

  4. ¿Cuál es el problema con el siguiente programa?

    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 es una pregunta tramposa, no hay problema con el código y se compilará correctamente. Siempre podemos capturar una Exception o cualquier excepción no comprobada incluso si no está en la cláusula throws del método. De manera similar, si un método (foo) declara una excepción no comprobada en la cláusula throws, no es obligatorio manejarla en el programa.

  5. ¿Cuál es el problema con el programa a continuación?

    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{
    		
    	}
    }
    

    El programa anterior no se compilará porque la firma del método start() no es la misma en la subclase. Para solucionar este problema, podemos cambiar la firma del método en la subclase para que sea exactamente la misma que en la superclase o podemos eliminar la cláusula throws del método de la subclase, como se muestra a continuación.

    @Override
    	public void start(){
    	}
    
  6. ¿Cuál es el problema con el programa a continuación?

    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{
    		
    	}
    }
    

    El programa anterior no se compilará porque el objeto de excepción en el bloque de captura múltiple es final y no podemos cambiar su valor. Obtendrás un error de tiempo de compilación como “No se puede asignar el parámetro e de un bloque de captura múltiple”. Debemos eliminar la asignación de “e” a un nuevo objeto de excepción para resolver este error. Lee más en Java 7 bloque de captura múltiple.

Eso es todo para las preguntas de entrevista de excepción en Java, espero que te gusten. Estaré agregando más a la lista en el futuro, asegúrate de marcarla para uso futuro.

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