Ir al contenido

Excepciones

Para comprender completamente el manejo de errores en Java, es fundamental conocer la jerarquía de clases que definen las excepciones y errores en el lenguaje. Esta arquitectura no solo determina qué tipo de fallos se pueden capturar, sino también cuándo es obligatorio manejarlos y qué ocurre si no se hace.


En Java todas las clases relacionadas con errores y excepciones vienen de una clase base llamada java.lang.Throwable.
Esta clase define el comportamiento común de todos los objetos que representan situaciones anómalas.

Desde Throwable se derivan dos grandes ramas:

java.lang.Throwable
├── java.lang.Error
└── java.lang.Exception
  • java.lang.Error: representa problemas graves del sistema, como fallos internos de la JVM (por ejemplo, OutOfMemoryError).

No están pensados para ser capturados ni manejados por el programa.

  • java.lang.Exception: representa condiciones que pueden ser previstas y manejadas por el programador, por lo que es aquí donde se concentra el enfoque del control de excepciones.

Ejemplo: al intentar leer un archivo que no existe, puede lanzarse una FileNotFoundException. Este tipo de problema se puede prever y manejar desde el código, por ejemplo mostrando un mensaje al usuario o creando el archivo vacío desde un inicio.


Dentro de la clase Exception, Java define una subclase especial llamada java.lang.RuntimeException, que agrupa errores comunes producidos por fallos de programación o lógica.

A diferencia de otras excepciones, las que derivan de RuntimeException no obligan al programador a capturarlas ni declararlas, porque se asume que pueden y deben prevenirse escribiendo buen código.


Con base en lo anterior, se distinguen dos grandes tipos de excepciones bajo Exception:

java.lang.Exception
├── (Checked Exceptions) ← Excepciones verificadas
└── java.lang.RuntimeException
└── (Unchecked Exceptions) ← Excepciones no verificadas
Excepciones verificadas (checked exceptions)
Sección titulada «Excepciones verificadas (checked exceptions)»
  • Son excepciones que heredan directamente de Exception, sin pasar por RuntimeException.
  • Representan errores esperados pero externos al control del programa (como la ausencia de un archivo o una conexión de red fallida).
  • El compilador obliga a tratarlas porque, si se ignoran, el programa puede fallar en condiciones normales y previsibles.

El compilador obliga a manejarlas ya que si un método puede lanzar una excepción verificada, el programador debe decidir cómo reaccionar ante ella, lo que fuerza al diseño consciente de cómo se comporta el software ante situaciones previstas.

Si no se captura ni se declara con throws, el código no compila como medida de protección para evitar fallos silenciosos.

  • IOException
  • SQLException
  • ParseException
Excepciones no verificadas (unchecked exceptions)
Sección titulada «Excepciones no verificadas (unchecked exceptions)»
  • Son aquellas que heredan de RuntimeException.
  • Representan errores que ocurren por fallos del programador, como acceder a una posición inválida en un arreglo o llamar a un método sobre un objeto nulo.
  • No se exige tratarlas ya que estas idealmente deben evitarse mediante una correcta lógica de programación.
  • NullPointerException
  • ArithmeticException
  • ArrayIndexOutOfBoundsException

Si bien desde un bloque catch`` se puede capturar cualquier clase que herede de Throwable, lo más común y recomendable es capturar **subtipos de Exception`**, ya que reflejan condiciones manejables.

Capturar directamente Throwable o Error es una mala práctica al estos errores indicar condiciones fuera del control del programa.

  • Capturar solo las excepciones que se espera manejar.
  • Evitar escribir catch (Exception e) de forma genérica sin saber qué se está atrapando, ya que eso puede:
    • Ocultar errores graves o inesperados.
    • Dificultar la depuración.
    • Generar comportamiento impredecible si se capturan excepciones que deberían propagarse.

¿Cuándo es obligatorio capturar una excepción?

Sección titulada «¿Cuándo es obligatorio capturar una excepción?»

Es obligatorio capturar todas las excepciones verificadas al ser una exigencia del compilador, lo cual se puede hacer de dos maneras:

  1. Capturándola con un bloque try-catch.
  2. Declarando que el método lo lanza usando throws.

Si no se captura la excepción el código no compilará, ya que el compilador marca un error porque se está ignorando una condición excepcional que, por contrato, el método puede producir.


¿Qué sucede si no se capturan las excepciones?

Sección titulada «¿Qué sucede si no se capturan las excepciones?»

El comportamiento depende del tipo de excepción:

  • Si es una excepción verificada (checked), el compilador no permite compilar el programa a menos que se maneje.
  • Si es una excepción no verificada (unchecked), y no se captura:
    • La excepción se propaga al método que llamó al actual.
    • Si ningún método la maneja, llega hasta el método main.
    • Si tampoco se maneja en main el programa se detiene y se imprime en consola un stack trace que muestra el recorrido de llamadas hasta el error.

Si un método divide dos números y recibe un divisor igual a cero, se lanza una ArithmeticException. Si no se captura en ningún punto, el programa finaliza abruptamente mostrando el error en consola.