Uso de excepciones en Java

Hace muchos años, cuando todavía ni pensaba en dedicarme a esto de la programación, recuerdo un anuncio de Pirelli que rezaba “la potencia sin control no sirve de nada”. Y mira qué recuerdo, que cada vez que trabajo en un proyecto no dejo de pensar lo acertado que es este slogan para nuestro mundillo. De nada sirve que tengas el desarrollo de un programa fantástico si no sabemos controlar qué ocurre cuando se producen errores inesperados, y más importante aún, cómo depurarlos.

Por fortuna en Java cuando se produce un error en un método “se lanza” un objeto que recoge la excepción y que luego podemos manipular para extraer información importante que nos ayude a resolver el error producido. Este objeto "Throwable" sirve para “capturar la excepción” a partir del método que haya hecho la llamada y tomar las medidas oportunas. Tras capturar la excepción, el control no vuelve al método en el que se produjo la excepción, sino que la ejecución del programa continúa en el punto donde se haya capturado. Así, la principal ventaja es que el objeto ya lo hace todo por nosotros, o casi, y no tendremos que preocuparnos por “diseñar códigos de error”.

 

Jerarquía de clases

Todo en Java está sujeto a una jerarquía. Algo completamente lógico si lo pensamos. De no ser así, el código podría ser imposible de mantener a largo plazo. En este caso la clase Throwable es una clase que representa todo lo que se pueda “lanzar” en Java. Contiene una instantánea del estado de la pila de memoria en el momento que se creó el objeto, lo que también llamamos stack trace o call chain. En el proceso almacena un mensaje de tipo String que podemos utilizar para detallar qué error se produjo.

 

¿Qué es un error y qué es una excepción?

Dentro de la clase principal derivan otras dos subclases. No son pocas las personas que tratan ambas por igual pero en realidad habría que diferenciarlas claramente. Por ejemplo, cuando hablamos de un Error nos referimos a una Subclase que indica problemas graves en nuestra aplicación. Este problema no puede ser resuelto de ninguna manera por lo que el programa normalmente se detiene. Mientras una excepción y sus subclases indican situaciones en donde una aplicación debería tratar de resolver un “error” de manera transparente al usuario lanzando advertencias.

Existen dos tipos de excepciones: Runtime Exception (errores de programa), como por ejemplo acceder fuera de los límites de un array o algo tan simple como tratar de dividir por cero.Y las IOException, errores de entrada y salida que suelen ser ajenos al programador.

 

Captura de excepciones: Bloques try... catch

Capturar excepciones es casi como ir a pescar. En este caso debemos tender las redes para poder estar preparados. En Java para capturar las excepciones que se hayan podido producir utilizamos las expresiones “try” y “catch” para delimitar el código que queremos controlar. Cuando se produce una excepción “try” termina y “catch” recibe como argumento el objeto Throwable. Por ejemplo:

// Bloque 1
try {
// Bloque 2
} catch (Exception error) {
// Bloque 3
}
// Bloque 4

 

Seguro que ahora surge la pregunta de cuántos tipos de excepción podemos capturar o filtrar. La respuesta es tantos como necesitemos. Por ejemplo:


// Bloque 1
try {
// Bloque 2
} catch (ArithmeticException ae) {
// Bloque 3
} catch (NullPointerException ne) {
// Bloque 4
}
// Bloque 5

 

También podemos necesitar ejecutar un fragmento de código con independencia de la excepción, por ejemplo para cerrar un fichero que estemos manipulando o cerrar una conexión a una base de datos. Para ello podemos añadir la cláusula “finally” del siguiente modo:


// Bloque1
try {
// Fragmento de código que puede
// lanzar una excepción de tipo IOException
// (p.ej. Acceso a un fichero)
} catch (IOException error) {
// Tratamiento de la excepción
} finally {
// Liberar recursos (siempre se hace)
}

 

Aunque quizá lo mejor sea verlo sobre un ejemplo habitual de código:


public void transferir
(String IDorigen, String IDdestino, int cantidad)
{
Cuenta origen;
Cuenta destino;
// Comenzar transacción
database.startTransaction();
try {
   origen = database.find(IDorigen);
   if (origen == null){
   throw new Exception("No existe ” + IDorigen);
   origen.setBalance (origen.getBalance() – cantidad);
   database.store(origen);
   destino = database.find(IDdestino);
   }
  if (destino == null){
  throw new Exception("No existe ” + IDdestino);
   destino.setBalance(destino.getBalance()+cantidad);
   database.store(destino)
   // Confirmar la transacción
   database.commit();
  }
} catch (Exception error) {
  // Cancelar la transacción
  database.rollback();
 }
}

 

Conclusiones

Parafraseando: un programa sin control no es un programa. Tan importante es mantener limpio y ordenado nuestro código como aprender a manejar las excepciones y mensajes de error que se produzcan. Solo así podemos asegurarnos que nuestro desarrollo es estable y listo para ser introducido en producción. No hacerlo, puede generar una enorme pérdida de recursos y tiempo o como un compañero decía ayer “me he pasado las dos últimas horas resolviendo un error que no necesitaba solución de hacer las cosas bien”.

 

Guía de Posibilidades Profesionales en el Ecosistema de Java