El control de flujo en un programa Java puede hacerse mediante las ya conocidas sentencias estructuradas (if, while, return). Pero Java va mucho más allá, mediante una técnica de programación denominada gestión de excepciones.
Mediante las excepciones se podrá evitar repetir continuamente código, en busca de un posible error, y avisar a otros objetos de una condición anormal de ejecución durante un programa.
Durante este capítulo estudiaremos la gestión de excepciones y errores, sin pretender profundizar demasiado, pero sí fijando la base conceptual de lo que este modo de programación supone.
Mediante la gestión de excepciones se prescindirá de sentencias de control de errores del tipo:
if ( error == true ) return ERROR;B. Tipos de excepciones
Existen varios tipos fundamentales de excepciones:
Imagen 5: Herencia de excepciones Java
Todas las excepciones tienen como clase base la clase Throwable, que está incluida en el paquete java.lang, y sus métodos son:
Para que el sistema de gestión de excepciones funcione, se ha de trabajar en dos partes de los programas:
Cuando el programador va a ejecutar un trozo de código que pueda provocar una excepción (por ejemplo, una lectura en un fichero), debe incluir este fragmento de código dentro de un bloque try:
try { // Código posiblemente problemático }
Pero lo importante es cómo controlar qué hacer con la posible excepción que se cree. Para ello se utilizan las clausulas catch, en las que se especifica que acción realizar:
try { // Código posiblemente problemático } catch( tipo_de_excepcion e) { // Código para solucionar la excepción e } catch( tipo_de_excepcion_mas_general e) { // Código para solucionar la excepción e }
En el ejemplo se observa que se pueden anidar sentencias catch, pero conviene hacerlo indicando en último lugar las excepciones más generales (es decir, que se encuentren más arriba en el árbol de herencia de excepciones), porque el intérprete Java ejecutará aquel bloque de código catch cuyo parámetro sea del tipo de una excepción lanzada.
Si por ejemplo se intentase capturar primero una excepción Throwable, nunca llegaríamos a gestionar una excepción Runtime, puesto que cualquier clase hija de Runtime es también hija de Throwable, por herencia.
Si no se ha lanzado ninguna excepción el código continúa sin ejecutar ninguna sentencia catch.
Pero, ¿y si quiero realizar una acción común a todas las opciones?. Para insertar fragmentos de código que se ejecuten tras la gestión de las excepciones. Este código se ejecutará tanto si se ha tratado una excepción (catch) como sino. Este tipo de código se inserta en una sentencia finally, que será ejecutada tras el bloque try o catch:
try { } catch( Exception e ) { } finally { // Se ejecutara tras try o catch }c.) Lanzamiento de excepciones: throw - throws
Muchas veces el programador dentro de un determinado método deberá comprobar si alguna condición de excepción se cumple, y si es así lanzarla. Para ello se utilizan las palabras reservadas throw y throws.
Por una parte la excepción se lanza mediante la sentencia throw:
if ( condicion_de_excepcion == true ) throw new miExcepcion();
Se puede observar que hemos creado un objeto de la clase miExcepcion, puesto que las excepciones son objetos y por tanto deberán ser instanciadas antes de ser lanzadas.
Aquellos métodos que pueden lanzar excepciones, deben cuáles son esas excepciones en su declaración. Para ello se utiliza la sentencia throws:
tipo_devuelto miMetodoLanzador() throws miExcep1, miExcep2 { // Codigo capaz de lanzar excepciones miExcep1 y miExcep2 }
Se puede observar que cuando se pueden lanzar en el método más de una excepción se deben indicar en su declaración separadas por comas.
d.) Ejemplo de gestión de excepcionesAhora que ya sabemos cómo funciona este sistema, conviene ver al menos un pequeño ejemplo, que ilustre al lector en el uso de las excepciones:
// Creo una excepción personalizada class MiExcepcion extends Exception { MiExcepcion(){ super(); // constructor por defecto de Exception } MiExcepcion( String cadena ){ super( cadena ); // constructor param. de Exception } } // Esta clase lanzará la excepción class Lanzadora { void lanzaSiNegativo( int param ) throws MiExcepcion { if ( param < 0 ) throw new MiExcepcion( "Numero negativo" ); } } class Excepciones { public static void main( String[] args ) { // Para leer un fichero Lanzadora lanza = new Lanzadora(); FileInputStream entrada = null; int leo; try { entrada = new FileInputStream( "fich.txt" ); while ( ( leo = entrada.read() ) != -1 ) lanza.lanzaSiNegativo( leo ); entrada.close(); System.out.println( "Todo fue bien" ); } catch ( MiExcepcion e ){ // Personalizada System.out.println( "Excepcion: " + e.getMessage() ); } catch ( IOException e ){ // Estándar System.out.println( "Excepcion: " + e.getMessage() ); } finally { if ( entrada != null ) try { entrada.close(); // Siempre queda cerrado } catch ( Exception e ) { System.out.println( "Excepcion: " + e.getMessage() ); } System.out.println( "Fichero cerrado." ); } } } class Excepciones { public static void main( String[] args ) { // Para leer un fichero FileInputStream entrada = null; Lanzadora lanza = new Lanzadora(); int leo; try { entrada = new FileInputStream("fich.txt"); while ( ( leo = entrada.read() ) != -1 ) lanza.lanzaSiNegativo( leo ); System.out.println( "Todo fue bien" ); } catch ( MiExcepcion e ){ // Personalizada System.out.println( "Excepcion: " + e.getMessage() ); } catch ( IOException e ){ // Estándar System.out.println( "Excepcion: " + e.getMessage() ); } finally { entrada.close(); // Así el fichero siempre queda cerrado System.out.println( "Fichero cerrado" ); } } }
Este programa lee un fichero (fichero.txt), y lee su contenido en forma de números.
Si alguno de los números leídos es negativo, lanza una excepción MiExcepcion, Además gestiona la excepción IOException, que es una excepción de las que Java incluye y que se lanza si hay algún problema en una operación de entrada/salida.
Ambas excepciones son gestionadas, imprimiendo su contenido (cadena de error) por pantalla.
La salida de este programa, suponiendo un número negativo sería:
Excepcion: Numero negativo Fichero cerradoEn el caso de que no hubiera ningún número negativo sería:
Todo fue bien Fichero cerradoEn el caso de que se produjese un error de E/S, al leer el primer número, sería:
Excepcion: java.io.IOException Fichero cerrado e.) ConclusionesEn cualquier caso se recomienda al programador no abusar de este sistema como control de flujos simples, sino utilizarlo sólo en aquellos estados del programa que realmente creen un problema de ejecución que pueda ser letal para el programa.
Para más información sobre las excepciones Java, véanse [Zolli, 1997] y [Naughton, 1996].
D. Excepciones que incorpora Java 1.2 a.) Clases de ErrorLinkageError: Una clase no satisface la dependencia que tiene respecto a otra.
ThreadDeath: Se ha lanzado en el thread víctima tras llamar a stop().
VirtualMachineError: La máquina virtual se ha averiado o quedado sin recursos.
CloneNotSupportedException: No se pudo copiar un objeto mediante clone().
IllegalAccessException: Algún método invocado es no visible.
InstantiationException: Se ha intentado instanciar una interfaz o una clase abstracta.
InterruptedException: Cuando se invoca a interrupt() sobre un thread dormido.
NoSuchFieldException: La clase no tiene un atributo con ese nombre.
NoSuchMethodException: La clase no tiene un método con ese nombre.
c.) Clases de RuntimeExceptionArithmeticException: Error de cálculo (como división por cero...).
ArrayStoreException: Intento de almacenar un objeto equivocado en un vector.
ClassCastException: Intento de conversión inválida.
IllegalArgumentException: Se ha pasado un argumento inválido a un método:
IllegalMonitorStateException: Se ha usado wait/notify fuera de código sincronizado.
IllegalStateException: Método invocado en un momento inapropiado.
IndexOutOfBoundsException: Acceso a un vector fuera de sus límites:
NegativeArraySizeException: Intento de creación de un vector de tamaño negativo.
NullPointerException: Se ha usado una referencia null para acceder a un campo.
SecurityException: Algo ha sido vedado por el sistema de seguridad.
UnsupportedOperationException: Una operación invocada no se soporta.
Para más información véase la documentación del JDK que usted vaya a utilizar.