Download Programación Orientada a Objetos Proyecto Curricular de
Document related concepts
no text concepts found
Transcript
7 - Excepciones en Java Programación Orientada a Objetos Proyecto Curricular de Ingeniería de Sistemas Introducción. Las excepciones en Java están destinadas, al igual que en el resto de los lenguajes que las soportan, para la detección y corrección de errores. Si hay un error, la aplicación no debería morirse y generar un core (o un crash en caso del DOS). Se debería lanzar (throw) una excepción que nosotros deberíamos capturar (catch) y resolver la situación de error. Java sigue el mismo modelo de excepciones que se utiliza en C++. Utilizadas en forma adecuada, las excepciones aumentan en gran medida la robustez de las aplicaciones. Manejo de Excepciones En todo programa existen errores inesperados en tiempo de ejecución, y también errores que no consideramos debido a nuestra propia inexperiencia como programadores. Unos de estos errores ocurren por ejemplo, al intentar acceder a un elemento del arreglo que está fuera del límite de nuestro arreglo, o cuando intentamos acceder a un archivo inexistente, entre otros. Normalmente estos errores interrumpen el flujo de ejecución de nuestros programas, hasta el extremo de provocar la terminación del programa en forma inmediata. Java hace uso de las excepciones para poder controlar los errores en tiempo de ejecución. En Java, casi todos los tipos de errores que puedan surgir en tiempo de ejecución lanzan excepciones, es decir, cuando ocurre un error dentro de un método de JAVA, este método crea un objeto Exception, dicho objeto contiene información sobre la excepción, que incluye su tipo y el estado del programa cuando ocurrió el error. El sistema de ejecución es el responsable de buscar algún código para manejar el error. El manejo de excepciones en Java sigue una estructura como esta: try { //Codigo donde puede ocurrir un error } catch (ExcepcionA ex) { // Que se va a hacer en caso que se lanze una Excepcion A } ... catch (ExcepcionZ ex) { // Que se va a hacer en caso que se lanze una Excepcion Z } Dentro del bloque try{ } viene encerrado la parte del programa que se desea manejar sus excepciones. El código dentro de algún catch (TipoExcepcion e) se ejecuta en caso se que lance una excepción TipoExcepcion o que pertenezca al grupo TipoExcepcion. El sistema de ejecución Java busca hacia atrás en la pila de llamadas para encontrar el método que esté interesado en manejar una excepción particular. Es decir si se lanza una excepción en el método A, pero si A no está interesado en manejar dicha excepción, entonces el sistema de ejecución Java ve quién llamó a A (supongamos que si existe, y es el método B), entonces se regresa a B y ve si está interesado en dicha excepción, y así consecutivamente hasta llegar al método principal de nuestra aplicación. En caso de no encontrar alguien que quiera manejarlo, comúnmente Java manda una lista de mensajes en nuestra ventana de consola, y en muchos casos se termina la ejecución de nuestro programa. Cuando manejamos excepciones, podemos manejar excepciones en forma específica (por ejemplo, usar un índice que está fuera de los límites de nuestro arreglo), o manejar una excepción de cierta categoría (por ejemplo, una excepción lanzada por mal uso de un arreglo de datos), o manejar todas las excepciones en su conjunto. Para el primer caso, es necesario este código catch(ArrayIndexOutOfBoundsException e){ }, para el segundo catch (ArrayException e){} y para el último catch (Exception e){ }. En la práctica no es recomendable hacer usar de manejadores de excepciones demasiado generales, como la del último caso. Las excepciones son parte de Java, y es muy común usarlos en las operaciones E/S, ya que es donde más probabilidad hay de que se lance una. Conforme se vean los temas, se consideran muchas excepciones que son obligaciones de capturar por la propia sintaxis de java. Pero estos se considerarán conforme se vean los temas. Throws Todos los métodos Java utilizan la sentencia throw para lanzar una excepción. Esta sentencia requiere un sólo argumento (un objeto Throwable (se le dice un objeto Throwable a la instancia de una clase que hereda de la clase Throwable (la superclase de todos los errores y excepciones en Java) Veamos el siguiente código de la función pop() cuyo propósito es sacar el elemento superior de la pila. public Objectpop() throws EmptyStackException { Object obj; if (size == 0) throw new EmptyStackException(); obj = objectAt(size - 1); setObjectAt(size - 1, null); size--; return obj; } El método pop() comprueba si la pila no está vacía. Si lo está, crea un nuevo objeto de la clase EmptyStackException y lo lanza, aunque en el método no se genere alguna excepción debido a lo bien validado que se encuentra, nosotros somos quienes lo lanzamos. Además por lógica, la clase EmpyStackException es una subclase de Thowable, ya que en cualquier otro caso, no se podría lanzar dicha excepción. Algo que se debe considerar aquí, es que en la declaración del procedimiento añade el siguiente código throws EmptyStackException, throws es una palabra reservada de java, y EmpyStackException es una subclase de Throwable. El uso de throws permite evitarnos la molestia de capturar las excepciones del tipo de excepciones indicadas después de esta palabra (las clases van separadas por coma), esto debido a que deja al sistema de ejecución Java que decida cuál sería la mejor opción en caso de que ocurriera una excepción de los tipos indicados. Veamos un Ejemplo completo: import java.awt.*; import java.applet.Applet; public class HolaIte extends Applet { private int i = 0; private String Saludos[] = { "Hola Mundo!", "HOLA Mundo!", "HOLA MUNDO!!" }; public void paint( Graphics g ) { g.drawString( Saludos[i],25,25 ); i++; } } Normalmente, un programa termina con un mensaje de error cuando se lanza una excepción. Sin embargo, Java tiene mecanismos para excepciones que permiten ver qué excepción se ha producido e intentar recuperarse de ella. Para ver la ejecución de todo el programa cundo se ejecute el applet de clic en la opción subprograma y reinicio el applet varias veces. Vamos a reescribir el método paint() de nuestra versión iterativa del saludo: public void paint( Graphics g ) { try { g.drawString( Saludos[i],25,25 ); } catch( ArrayIndexOutOfBoundsException e ) { g.drawString( "Saludos desbordado",25,25 ); } catch( Exception e ) { // Cualquier otra excepción System.out.println( e.toString() ); } finally { System.out.println( "Esto se imprime siempre!" ); } i++; } La palabra clave finally define un bloque de código que se quiere que sea ejecutado siempre, de acuerdo a si se capturó la excepción o no. En el ejemplo anterior, la salida en la consola, con i=4 sería: Saludos desbordado ¡Esto se imprime siempre! Lego de reiniciar varias veces el applet esto esta a la vista de el. Generar Excepciones en JAVA Cuando se produce un error se debería generar, o lanzar, una excepción. Para que un método en Java, pueda lanzar excepciones, hay que indicarlo expresamente. void MetodoAsesino() throws NullPointerException,CaidaException Se pueden definir excepciones propias, no hay por qué limitarse a las predefinidas; bastará con extender la clase Exception y proporcionar la funcionalidad extra que requiera el tratamiento de esa excepción. También pueden producirse excepciones no de forma explícita como en el caso anterior, sino de forma implícita cuando se realiza alguna acción ilegal o no válida. Las excepciones, pues, pueden originarse de dos modos: el programa hace algo ilegal (caso normal), o el programa explícitamente genera una excepción ejecutando la sentencia throw (caso menos normal). La sentencia throw tiene la siguiente forma: throw ObtejoExcepction; El objeto ObjetoException es un objeto de una clase que extiende la clase Exception. El siguiente código de ejemplo origina una excepción de división por cero: package exeptions; class Melon { public static void main( String[] a ) { int i=0, j=0, k; k = i/j; // Origina un error de division-by-zero System.out.println (k); } } Si compilamos y ejecutamos esta aplicación Java, obtendremos la siguiente salida por pantalla: Exception in thread "main" java.lang.ArithmeticException: / by zero at exeptions.Melon.main(Melon.java:7) Las excepciones predefinidas, como ArithmeticException, se conocen como excepciones runtime. Actualmente, como todas las excepciones son eventos runtime, sería mejor llamarlas excepciones irrecuperables. Esto contrasta con las excepciones que generamos explícitamente, que suelen ser mucho menos severas y en la mayoría de los casos podemos recuperarnos de ellas. Por ejemplo, si un fichero no puede abrirse, preguntamos al usuario que nos indique otro fichero; o si una estructura de datos se encuentra completa, podremos sobrescribir algún elemento que ya no se necesite. Las excepciones predefinidas y su jerarquía de clases es la que se muestra en la figura: Los nombres de las excepciones indican la condición de error que representan. Las siguientes son las excepciones predefinidas más frecuentes que se pueden encontrar: ArithmeticException Las excepciones aritméticas son típicamente el resultado de una división por 0: int i = 12 / 0; NullPointerException Se produce cuando se intenta acceder a una variable o método antes de ser definido: class Hola extends Applet { Image img; paint( Graphics g ) { g.drawImage( img,25,25,this ); } } IncompatibleClassChangeException El intento de cambiar una clase afectada por referencias en otros objetos, específicamente cuando esos objetos todavía no han sido recompilados. ClassCastException El intento de convertir un objeto a otra clase que no es válida. y = (Prueba)x; // donde x no es de tipo Prueba NegativeArraySizeException Puede ocurrir si hay un error aritmético al intentar cambiar el tamaño de un array. OutOfMemoryException ¡No debería producirse nunca! El intento de crear un objeto con el operador new ha fallado por falta de memoria. Y siempre tendría que haber memoria suficiente porque el garbage collector se encarga de proporcionarla al ir liberando objetos que no se usan y devolviendo memoria al sistema. NoClassDefFoundException Se referenció una clase que el sistema es incapaz de encontrar. ArrayIndexOutOfBoundsException Es la excepción que más frecuentemente se produce. Se genera al intentar acceder a un elemento de un array más allá de los límites definidos inicialmente para ese array. UnsatisfiedLinkException Se hizo el intento de acceder a un método nativo que no existe. Aquí no existe un método a.kk class A { native void kk(); } y se llama a a.kk(), cuando debería llamar a A.kk(). InternalException Este error se reserva para eventos que no deberían ocurrir. Por definición, el usuario nunca debería ver este error y esta excepción no debería lanzarse.