ERRORES en Java
GESTIÓN DE EXCEPCIONES Y ERRORES en Java A. Introducción El control de flujo en un proyecto Java puede hacerse mediante las ya conocidas sentencias estructuradas (if, while, return). Pero Java va mucho mas alla, mediante una tecnica de proyectocion denominada gestion de excepciones. Mediante las excepciones se podra eludir repetir continuamente codigo, en busca de un probable error, y avisar a otros objetos de una clausula anormal de ejecucion mientras un programa. Mientras este capitulo estudiaremos la gestion de excepciones y errores, sin pretender profundizar demasiado, pero si fijando la fundamento conceptual de lo que este modo de programacion supone. Mediante la gestión de excepciones se prescindirá de sentencias de control de errores del tipo: if ( yerro == true ) return ERROR; B. Tipos de excepciones Tienen lugar varios tipos fundamentales de excepciones: Error: Excepciones que indican dificultades muy graves, que suelen ser no recuperables y no deben casi jamás ser capturadas. Exception: Excepciones no definitivas, pero que se detectan afuera del tiempo de ejecucion. RuntimeException: Excepciones que se dan mientras la ejecucion del programa. Imagen 5: Herencia de excepciones Java Todas las excepciones tienen como clase fundamento la clase Throwable, que esta incluida en el paquete java.lang, y sus metodos son: Trowable( String mensaje ); Constructor. La cadena es opcional Throwable fillInStackTrace(); Llena la pila de dibuja de ejecución. String getLocalizedMessage(); Crea una descripción local de este objeto. String getMessage(); Devuelve la cadena de yerro del objeto. void printStackTrace( PrintStream_o_PrintWriter s ); Imprime este objeto y su dibuja en el flujo del parámetro s, o en la salida estándar (por defecto). String toString; Devuelve una breve descripción del objeto. C. Funcionamiento a.) Introducción Para que el sistema de gestion de excepciones funcione, se ha de laborar en dos fracciónes de los programas: Definir que fracciones de los proyectos crean una excepcion y debajo que condiciones. Para ello se emplean las palabras reservadas throw y throws. Verificar en ciertas fracciones de los proyectos si una excepcion se ha producido, y actuar en consecuencia. Para ello se emplean las palabras reservadas try, catch y finally. b.) Empleo de excepciones: try - catch - finally Cuando el programador va a ejecutar un pedazo de codigo que pueda provocar una excepcion (por ejemplo, una lectura en un fichero), debe incluir este pedazo de codigo dentro de un bloque try: try { // Código seguramente problemático } Pero lo significativo es como controlar que realizar con la probable excepcion que se cree. Para ello se emplean las condiciones catch, en las que se especifica que accion realizar: try { // Código seguramente 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 realizarlo señalando en ultimo espacio las excepciones mas globales (es decir, que se encuentren mas encima en el arbol de herencia de excepciones), porque el interprete Java ejecutara aquel bloque de codigo catch cuyo parametro sea del tipo de una excepcion lanzada. Si por ejemplo se intentase capturar primero una excepcion Throwable, jamas llegariamos a gestionar una excepcion Runtime, ya que que cualquier clase hija de Runtime es tambien 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 hacer una accion general a todas las opciones?. Para insertar pedazos de codigo que se ejecuten tras la gestion de las excepciones. Este codigo se ejecutara tanto si se ha tratado una excepcion (catch) como sino. Este tipo de codigo se inserta en una sentencia finally, que sera 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 algun metodo debera verificar si cierta clausula de excepcion se cumple, y si es asi lanzarla. Para ello se emplean las palabras reservadas throw y throws. Por una fracción la excepcion se lanza mediante la sentencia throw: if ( condicion_de_excepcion == true ) throw new miExcepcion(); Se puede contemplar que hemos creado un objeto de la clase miExcepcion, ya que que las excepciones son objetos y por tanto deberan ser instanciadas antes de ser lanzadas. Aquellos metodos que pueden arrojar excepciones, deben cuales son esas excepciones en su declaracion. Para ello se emplea la sentencia throws: tipo_devuelto miMetodoLanzador() throws miExcep1, miExcep2 { // Codigo capaz de arrojar excepciones miExcep1 y miExcep2 } Se puede contemplar que cuando se pueden arrojar en el metodo mas de una excepcion se deben indicar en su declaracion separadas por comas. d.) Ejemplo de gestión de excepciones Ahora que ya conocemos como funciona este sistema, conviene ver al menos un chico ejemplo, que instruído 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 acceso = null; int leo; try { acceso = new FileInputStream( "fich.txt" ); while ( ( leo = entrada.read() ) != -1 ) lanza.lanzaSiNegativo( leo ); entrada.close(); System.out.println( "Todo fuese bien" ); } catch ( MiExcepcion e ){ // Personalizada System.out.println( "Excepcion: " + e.getMessage() ); } catch ( IOException e ){ // Estándar System.out.println( "Excepcion: " + e.getMessage() ); } finally { if ( acceso != 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 acceso = null; Lanzadora lanza = new Lanzadora(); int leo; try { acceso = new FileInputStream("fich.txt"); while ( ( leo = entrada.read() ) != -1 ) lanza.lanzaSiNegativo( leo ); System.out.println( "Todo fuese 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 proyecto lee un fichero (fichero.txt), y lee su contenido en manera de numeros. Si sdeterminados de los numeros leidos es negativo, lanza una excepcion MiExcepcion, Asimismo gestiona la excepcion IOException, que es una excepcion de las que Java incluye y que se lanza si hay determinado asunto en una operacion 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 cerrado En el caso de que no debiera ningun numero negativo seria: Todo fuese bien Fichero cerrado En el caso de que se produjese un yerro de E/S, al leer el primer número, sería: Excepcion: java.io.IOException Fichero cerrado e.) Conclusiones En cualquier caso se recomienda al proyectodor no abusar de este sistema como control de flujos simples, sino usarlo solo en aquellos estados del proyecto que realmente creen un asunto de ejecucion que pueda ser letal para el proyecto. 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 Yerro LinkageError: Una clase no satisface la dependencia que tiene respecto a otra. ClassCircularityError: Se detectó una herencia circular entre clases. ClassFormatError: Una clase cargada no ha sido incompletamente descrita. UnsupportedClassVersionError: La versión de una clase no es correcta. ExceptionInInitializerYerro: Yerro al empezar un miembro static. IncompatibleClassChangeError: En una clase, su interfaz no es idéntico al declarado AbstractMethodError: Se ha invocado un método abstracto. IllegalAccessError: La aplicacion intento alcanzar a determinado miembro no visible. InstantiationError: Se intentó instanciar una clase abstracta o interfaz. NoSuchFieldError: No se encontró algun atributo. NoSuchMethodError: No se encontró algun método. NoClassDefFoundError: No se encontró una clase cuando se necesitaba. UnsatisfiedLinkError: Se encontró un enlace insatisfecho en un método nativo. VerifyError: Se ha producido un yerro de verificación al cargar una clase. 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. InternalYerro: Yerro interno en tiempo de ejecución. OutOfMemoryError: El lector ha agotado la memoria. StackOverflowError: Desbordamiento de pila. ¿Recursión infinita?. UnknownError: Gravisimo yerro desconocido. b.) Clases de Exception 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 RuntimeException ArithmeticException: Yerro de cálculo (como división por cero...). ArrayStoreException: Intento de Guardar 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: IllegalThreadStateException: Un thread no estaba en el estado adecuado. NumberFormatException: Una cadena contenedora de un número, no lo contiene. IllegalMonitorStateException: Se ha usado wait/notify afuera de codigo sincronizado. IllegalStateException: Método invocado en un momento inapropiado. IndexOutOfBoundsException: Entrada a un vector afuera de sus limites: ArrayIndexOutOfBoundsException: Idem, para una matriz. StringIndexOutOfBoundsException: Idem, para una cadena. NegativeArraySizeException: Intento de creacion de un vector de dimensión negativo. NullPointerException: Se ha usado una referencia null para alcanzar 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.