Una Excepción es una condición de error generada durante la ejecución de un programa. El lenguaje Java provee los mecanismos que permiten separar, en un método, el código relacionado con el manejo de la Excepción del código principal. Entonces, cuando el código de un método se encuentra con una condición de error la java (o el programa) “lanza” (throwing) una excepción.
Jerarquía de excepciones
Java provee una extensa jerarquía de clases para representar diversos tipos de errores, en la raíz de esta jerarquía se encuentra la clase java.lang.Throwable; es decir, toda excepción, sea parte o no de la librería estándar de Java, se representa como una instancia que directa o indirectamente extiende la clase Throwable. Además, dependiendo si una clase hereda de manera directa o indirecta de la clase java.lang.Exception las excepciones se clasifican en excepciones verificadas o excepciones no-verificadas de acuerdo al siguiente diagrama[1]:
Para cada sentencia en un método que pueda generar una excepción verificada, el compilador demanda que ésta sea gestionada de algunas de estas dos maneras:
- Atrapar la Excepción.
- Declarar la excepción.
Para el caso de las excepciones no-verificadas, el programador puede gestionarlas de la misma manera pero el compilador no lo demanda como en el caso de las excepciones verificadas.
La idea detrás de la distinción entre excepciones verificadas y no-verificadas es que las primeras representan (al menos en teoría) condiciones de error imprevistas de las cuales el programa podría recuperarse; por ejemplo si se intenta acceder a un recurso en una red y hay una falla se puede intentar acceder nuevamente. Las excepciones no-verificadas, por el contrario, deberían representar condiciones de error de las cuales el programa no se podría recuperar y que usualmente tienen su origen en alguna falla de programación que podría haberse evitado como cuando se trata de acceder a un elemento en una posición mayor al tamaño de un arreglo.
Gestión de excepciones
Atrapar la excepción
Atrapar la excepción consiste en encerrar las sentencias que podrían lanzar o generar una excepción en un bloque try{} seguido de uno o más bloques catch{} más un bloque opcional finally{} al final. El bloque try{} contiene el código principal del método que potencialmente podría lanzar una excepción. El bloque catch{} incluye la declaración de una variable de algún tipo excepción y contiene el código encargado de gestionar ese tipo de excepción. El bloque finally{} se ejecuta siempre de manera incondicional después de la ejecución de algún bloque catch{} y contiene usualmente sentencias encargadas liberar recursos que podrían haberse reservado en el bloque try{} como una conexión a una BD. Entonces, una vez ejecutados el bloque catch{} y el bloque finally{} (si existe), el sistema deberá estar nuevamente en un estado consistente o, de no ser posible, deberá liberar todos los recursos adquiridos antes de terminar.
Declarar la excepción
Declarar una excepción consiste en incluir en la declaración del método la lista de excepciones que el método podría lanzar empleando la palabra reservada throws. Básicamente se trata de un mecanismo que permite a un método delegar la gestión de las excepciones que pueda generar al método que lo llamó. Si eventualmente ningún método atrapa la excepción el programa termina[2].
En el siguiente video, se muestran los fundamentos del manejo de excepciones así como el concepto de Stack Trace:
Encadenamiento de excepciones
El encadenamiento de excepciones consiste en atrapar una excepción y lanzar una nueva excepción utilizando la excepción atrapada como causa de la nueva excepción. Esta es una técnica muy utilizada en el desarrollo de librerías para encapsular diversas excepciones en excepciones creadas en una jerarquía propia de la librería.
Try-with-resources y la interfaz AutoCloseable
En el siguiente video se muestra una manera más sucinta de gestionar excepciones disponible desde Java 7. Para mayores detalles revisar esta referencia.
Definición de nuevas clases de excepciones
Antes de crear nuevas clases de excepciones, es recomendable primero ver si alguna de las clases existentes puede describir adecuadamente la condición de error que queremos representar. En caso de ser necesario crear una nueva clase de excepción se recomienda seguir los siguientes lineamientos:
- Decidir si la nueva clase será verificada o no-verificada, se recomienda emplear excepciones no verificadas (ver justificación más adelante)
- La clase deberá terminar en la palabra “Exception”; por ejemplo, InvalidSystemStatusException[3]
- Incluir cuatros constructores
o Sin parámetros
o Con un parámetro de tipo String
o Con dos parámetros, el primero de tipo String y el segundo de tipo Throwable
o Con un parámetro de tipo Throwable
Buenas prácticas en el uso de Excepciones
Si crea nuevas excepciones, prefiera las no-verificadas
Las excepciones verificadas crean dependencias que obligan a modificar la definición de los métodos que llaman al método que declaran la excepción verificada. Esto implica una clara violación del principio de diseño “Open/Closed” que a su vez implica que el sistema es más frágil.
Devuelva una excepción en lugar de null
Si por ejemplo, si está desarrollando un método que consulta registros desde algún repositorio no es recomendable devolver null si no se encuentran registros porque esto obliga al método que llama a verificar si el resultado es null antes de poder acceder al resultado y si esa verificación no se realiza las condiciones están dadas para que eventualmente se genere una java.lang.NullPointerException. Es mucho mejor en estos casos generar excepción o aplicar el patrón del objeto nulo (en este caso devolver una lista vacía)
[1]Si un programa necesita definir nuevas clases de Excepciones deberá extender alguna clase debajo de la jerarquía que tiene como raíz a java.lang.Exception.
[2]El comportamiento por defecto para una excepción no atrapada puede personalizarse y aunque la descripción de este tipo de tarea está fuera de alcance del contenido de este artículo pueden ver mayores detalles sobre este tema aquí.
[3] No es necesario que el nombre de la clase esté en idioma inglés.