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.