Libro
Libro
Libro
1
5.1 DEFINICIN DE EXCEPCIONES EN PROGRAMACIN.........................2
5.2 TIPOS DE EXCEPCIONES / ERRORES Y CMO TRATARLOS.............10
5.3 TRABAJANDO CON EXCEPCIONES: DECLARACIN, CONSTRUCCIN,
LANZAMIENTO Y GESTIN DE EXCEPCIONES ................................. 14
5.3.1 PROPAGACIN DE EXCEPCIONES...............................................15
5.3.2. CAPTURA Y GESTIN DE EXCEPCIONES....................................18
5.3.3CAPTURA, GESTIN Y PROPAGACIN DE EXCEPCIONES.............21
5.3.4CMO TRABAJAR CON EXCEPCIONES..........................................22
5.4 PROGRAMACIN DE EXCEPCIONES EN JAVA. UTILIZACIN DE
EXCEPCIONES DE LA LIBRERA Y DEFINICIN DE EXCEPCIONES
PROPIAS 23
5.4.1PROGRAMACIN DE EXCEPCIONES PROPIAS EN JAVA.................23
5.4.2UN EJEMPLO DESARROLLADO DE TRABAJO CON EXCEPCIONES DE LA
API DE JAVA .................................................................................... 26
}
El resultado de compilar y ejecutar el anterior programa sera:
El resultado de la division real de 15.0 entre 3.0 es 5.0
}
Como siempre que plantemos la anterior situacin, la pregunta que debemos
responder ahora es, qu comportamiento esperaremos de nuestro mtodo en
caso de que el divisor sea igual a cero? Qu valor de aux debera devolver
el mtodo?
Lo primero que debemos tener claro es que el mtodo debe devolver un valor
double, con lo cual no podemos devolver, por ejemplo, un mensaje de error
advirtiendo al usuario de que est intentando dividir por cero. Una alternativa es
devolver lo que se suele denominar como un valor sealado, que nos permita
distinguir que en el mtodo ha sucedido algo anmalo. Un candidato a ser
devuelto sera el propio cero, pero cero tambin es el resultado de dividir 0.0
entre 3.0, por lo que los clientes del mtodo division_real (double, double):
double difcilmente podran distinguir entre una situacin y la otra. Con
cualquier otro valor real encontraramos el mismo problema. Optaremos por
devolver 0.0 en caso de que el divisor sea igual a cero:
public static double division_real (double dividendo, double divisor)
{ double aux;
if (divisor != 0){
aux = dividendo/divisor;
}
else { aux = 0.0;
}
return aux;
}
Ahora el resultado de ejecutar el anterior fragmento de cdigo sera:
El resultado de la division real de 15.0 entre 0.0 es 0.0
La forma anterior de tratar el caso excepcional, aunque pueda parecer un poco
deficiente, es ampliamente utilizada por mltiples lenguajes de programacin.
Por ejemplo, en las libreras de C y C++ podemos encontrar las siguientes
especificaciones de funciones encargadas de abrir o cerrar ficheros. En
particular, nos vamos a preocupar de la funcin fclose (FILE *): int, que es
propia de la librera cstdio. La especificacin de la funcin ha sido extrada de
http://www.cplusplus.com/reference/clibrary/cstdio/fclose.html:
int fclose ( FILE * stream );
Return Value:
If the stream is successfully closed, a zero value is returned.
On failure, EOF is returned.
La funcin fclose (FILE *): int tiene por cometido cerrar el fichero que se le
pasa como parmetro. Como se puede observar, el valor de retorno de la
misma es un entero. Al leer detenidamente la especificacin de su Return
funcin nos avisar por medio del valor EOF (en lugar de, por ejemplo,
mandarnos un mensaje o una seal advirtiendo de tal situacin).
Si el programador que hace uso de la funcin fclose (FILE *): int tiene en
cuenta que dicha funcin puede devolver un valor negativo, es probable que
sea capaz de reaccionar ante tal hecho, y proponer un comportamiento
especfico para este caso alternativo. En caso contrario, el flujo del programa
seguir siendo el mismo, si bien el fichero no estar cerrado, tal y como
nosotros esperbamos.
Aqu es donde las excepciones suponen una diferencia sustancial con respecto
a la forma de gestionar los errores por medio de valores sealados (como un
valor negativo, en el caso de la funcin fclose (FILE *): int, o devolver 0.0 en
el caso de una divisin entre 0.0 con la funcin division_real(double, double):
double).
Las excepciones tienen dos caractersticas principales a las cuales debemos
prestar atencin:
1) En primer lugar, las excepciones nos van a permitir enriquecer los mtodos, en
el sentido de que un mtodo no slo va a poder devolver un valor de un
determinado tipo (por ejemplo, un double o un int en las funciones
anteriores), sino que nos van a permitir que un mtodo lance una excepcin.
2) En segundo lugar, nos van a permitir alterar el flujo natural de un programa y
que ste no sea nico. Siguiendo con la propiedad que hemos citado en 1), si
un mtodo va a poder devolver un valor de un tipo determinado o lanzar una
excepcin, el programa cliente de dicho mtodo debe estar preparado para
ambos comportamientos, y eso hace que la ejecucin de un programa no vaya
a ser lineal, tal y como la entendamos hasta ahora, sino que puede variar
dependiendo de los valores o excepciones que produzca cada llamada a un
mtodo.
Veamos en primer lugar la caracterstica que mencionamos en 1). Vamos a
hacer que nuestro mtodo division_real(double, double): double sea capaz de
enviar una seal de que se ha producido una situacin anmala a sus clientes
(en este caso, slo el mtodo main) cuando el divisor sea igual a 0.0. El
cdigo del mtodo auxiliar division_real(double, double): double pasa ahora a
ser:
public static double division_real (double dividendo, double divisor) throws
Exception{ double aux;
if (divisor != 0){
aux = dividendo/divisor;
}
else {
Veamos los cambios que han tenido lugar en el mtodo con respecto a su
versin anterior:
1) En primer lugar, la cabecera del mismo ha sufrido una modificacin, pasando
ahora a ser:
public static double division_real (double dividendo, double divisor) throws Exception{}
Dicha cabecera expresa el siguiente comportamiento:
El mtodo division_real (que es pblico, esttico, y tiene como parmetros
dos valores de tipo double) puede, o bien devolver un valor double, o bien
lanzar un objeto de la clase Exception.
Vemos aqu una primera gran diferencia derivada de trabajar con excepciones.
El flujo del cdigo puede adoptar distintas direcciones. O bien el mtodo
division_real (double, double): double puede devolver un nmero real, o bien
puede lanzar una excepcin, en la forma de un objeto de la clase Exception.
Los clientes de dicho mtodo deben ser capaces de soportar ambos
comportamientos, lo cual veremos un poco ms adelante.
2) La segunda diferencia sustancial del mtodo division_real(double, double):
double con respecto a su versin anterior la encontramos, precisamente, al
estudiar el caso excepcional:
double aux;
if (divisor != 0){
aux = dividendo/divisor;
}
else { throw new Exception();
}
return aux;
Como se puede observar en la parte correspondiente al else {} (es decir,
cuando el divisor es igual a 0), lo que se hace no es darle un valor sealado
(o arbitrario) a la variable aux, sino que lo que hacemos es, primero construir
un objeto de la clase Exception (propia de Java, y de la que luego daremos
detalles), y posteriormente lanzarlo por medio del comando throw (insistimos
una vez ms, lo que hacemos es lanzar la excepcin a nuestro cliente, en
este caso el mtodo main).
El bloque try {...} puede ser entendido como una forma de decirle a nuestro
cdigo intenta ejecutar las rdenes dentro de este bloque, de las cuales
los
Por lo dems, la clase no cuenta con mtodos propios, pero s con algunos
mtodos heredados de sus superclases que nos sern de utilidad.
Especialmente tiles nos resultarn:
-RuntimeException
la clase RuntimeException representa el conjunto de las excepciones que
pueden tener lugar durante el proceso de ejecucin de un programa sobre la
JVM, con la peculiaridad de que el usuario no tiene que prestar atencin al
hecho de capturarlas (todas
las
dems
excepciones
debernser capturadas
o gestionadas en algn modo
por el usuario).
Dentro
de
la
especificacin
de
la
clase
RunTimeException
podemos encontrar un numeroso grupo de excepciones, con algunas de las
cuales ya estamos familiarizados, y que conviene citar:
-ClassCastException
excepcin que tiene lugar cuando intentamos hacer un cast de un objeto a
una clase de la que no es subclase. Un ejemplo sencillo est en la propia API
de Java:
Object x = new Integer(0);
System.out.println((String)x);
Al no ser la clase Integer una subclase de String, no podemos hacer un
cast de forma directa. La situacin anterior producira una
ClassCastException en tiempo de ejecucin, no antes, que no es necesario
que el usuario gestione.
-IndexOutOfBoundsException
excepcin que tiene lugar cuando intentamos acceder a un ndice de un
array, String o vector mayor que el nmero de elementos de dicha
estructura. Veamos tambin un ejemplo sencillo:
int array_enteros [] = new int [50];
System.out.println (array_enteros [67]);
Al intentar acceder a una posicin del array mayor que la dimensin del
mismo, podemos observar como Java lanza una excepcin para advertirnos de
que dicha direccin de memoria no ha sido reservada. De nuevo, estamos ante
una excepcin que aparecer en tiempo de ejecucin, y que no es necesario
que el usuario gestione. Qu habra sucedido en C++ ante esta misma
situacin?
-NegativeArraySizeException
excepcin que tiene lugar cuando intentamos crear un array con longitud
negativa. Un ejemplo de una situacin donde aparecera tal excepcin sera el
siguiente:
De nuevo es una excepcin que el usuario no debe gestionar (pero debe ser
consciente de que puede surgir en sus programas). Repetimos la pregunta que
lanzbamos con la excepcin anterior, qu habra pasado en C++ ante esa
situacin?
-InputMismatchException
excepcin que lanzan varios mtodos de la librera Scanner de Java cuando
estn recorriendo una entrada en busca de un dato y ste no existe en la
entrada. Por ejemplo, si tenemos la siguiente entrada abierta:
Hola
Esto es un scanner
Que slo contiene texto
Y sobre el mismo tratamos de ejecutar el mtodo nextDouble(): double,
obtendremos dicha excepcin. Una vez ms, no es obligatorio gestionarla,
aunque dependiendo de nuestra aplicacin puede ser importante hacerlo.
-NumberFormatException
esta excepcin tiene lugar cuando intentamos convertir un dato de tipo
String a algn tipo de dato numrico, pero el dato de tipo String no tiene el
formato adecuado.
Es usada, por ejemplo, en los mtodos parseDouble(): double, parseInt():
int, parseFloat(): float, ... .
-NullPointerException
quiz sta sea la excepcin que ms comnmente aparece cuando se trabaja
con Java. Surge siempre que intentamos acceder a cualquier atributo o mtodo
de un objeto al que le hemos asignado valor null. Un caso sencillo donde
aparecera sera el siguiente:
Object ds = null;
ds.toString();
Como se puede observar, el objeto ds ha sido inicializado (con el valor null),
lo que nos previene de obtener un error de compilacin en Java, y al intentar
acceder a cualquiera de sus mtodos (por ejemplo, el mtodo toString():
String), al invocar a un mtodo sobre la referencia nula, obtenemos la
excepcin NullPointerException. Similar excepcin se obtiene tambin en la
siguiente situacin:
Object array_ob [] = new Object [25];
array_ob[1].toString();
Una vez conocemos algunas de las excepciones ms comunes con las que nos
podemos encontrar al programar en Java, en esta Seccin pasaremos a ver
cmo debemos trabajar con las mismas, es decir, cmo debemos gestionarlas.
En general, cuando dentro de un mtodo aparece una excepcin es porque el
propio mtodo la ha creado y lanzado, o porque dicha excepcin ha venido
lanzada de algn otro mtodo que ha sido invocado desde ste.
A partir de esa situacin, y como norma general se puede enunciar que, un
mtodo cualquiera, cuando se encuentra con una excepcin (por cualquiera de
los dos motivos anteriores) que no sea de tipo RunTimeException, puede
propagar la misma, o puede capturarla y gestionarla (si es de tipo
RunTimeException, el hecho de gestionarla es opcional, depende del
programador).
En la Seccin 5.1 ya hemos visto un sencillo ejemplo en el cual hemos lanzado
y capturado una excepcin, que es una de las formas habituales de
gestionarlas, pero no la nica. En esta Seccin veremos tres formas distintas
de gestionar una excepcin que se podran declarar como propagar la misma
(Seccin 5.3.1), capturar y gestionar la misma (Seccin 5.3.2), y, por ltimo,
una combinacin de los dos mtodos anteriores, capturar, gestionar y
propagar la excepcin.
Para ello vamos a escoger un ejemplo sencillo. Imagina que tenemos un
mtodo auxiliar que recibe como parmetro un array de nmeros reales y un
entero. Dicho mtodo devolver la posicin del array indicada por el entero.
Como ya hemos indicado antes, Java cuenta con excepciones propias
(IndexOutOfBoundsException) que se encargan de esta misin, pero dicha
excepcin es de tipo RunTimeException, por lo cual no es obligatorio que sea
gestionada por el usuario. Lo que haremos ser gestionar el caso excepcional
anterior (que no exista el ndice que se nos solicita) por medio de una
excepcin de la clase Exception, que s debe ser gestionada por el usuario.
PROPAGACIN DE EXCEPCIONES
Veamos en primer lugar qu aspecto podra tener el mtodo auxiliar que nos
solicita el problema:
public static double acceso_por_indice (double [] v, int indice){
return v [indice];
}
Un sencillo programa cliente que hace uso del mtodo auxiliar podra ser el
siguiente:
}
for (int i = 0; i < 500; i = i + 25){
System.out.println ("El elemento en " + i + " es "
+ acceso_por_indice (array_doubles, i));
}
}
Presentaremos en primer lugar la forma de hacer que el mtodo auxiliar
acceso_por_indice (double [], int): double sea capaz de lanzar una excepcin
cuando no se cumple la condicin que hemos exigido.
public static double acceso_por_indice (double [] v, int indice) throws Exception{
if ((0<=indice) && (indice <v.length)){
return v [indice];
}
else { //Caso excepcional:
throw new Exception (El indice + indice +
no es una posicion valida);
}
}
defecto propagarla (al mtodo que haya invocado este mtodo). En este caso,
nos hemos decantado porque nuestro mtodo acceso_por_indice (double [],
Por ltimo, podemos prestar atencin a la salida que produce la ejecucin del
fragmento de cdigo anterior. En este caso sera:
El elemento en 0 es 0.0
El elemento en 25 es 175.0
El elemento en 50 es 350.0
El elemento en 75 es 525.0
El elemento en 100 es 700.0
El elemento en 125 es 875.0
El elemento en 150 es 1050.0
El elemento en 175 es 1225.0
El elemento en 200 es 1400.0
El elemento en 225 es 1575.0
El elemento en 250 es 1750.0
El elemento en 275 es 1925.0
El elemento en 300 es 2100.0
El elemento en 325 es 2275.0
El elemento en 350 es 2450.0
El elemento en 375 es 2625.0
El elemento en 400 es 2800.0
El elemento en 425 es 2975.0
El elemento en 450 es 3150.0
El elemento en 475 es 3325.0
java.lang.Exception: El indice 500 no es una posicion
valida java.lang.Exception: El indice 525 no es una
posicion valida java.lang.Exception: El indice 550 no es
una posicion valida java.lang.Exception: El indice 575 no
es una posicion valida
Como se puede observar, todas y cada una de las veces que hemos llamado al
mtodo acceso_por_indice (double [], int): double con un valor fuera del rango
del array la respuesta que hemos obtenido es la excepcin correspondiente
que nos avisa de que se ha producido dicha situacin excepcional (y el mtodo
acceso_por_indice (double[], int): double no ha tenido que devolver un valor
double, sino que simplemente ha lanzado la excepcin mostrada).
else {
//Caso excepcional
throw new Exception ("El indice " + indice +
" no es una posicion valida");
}
}
catch (Exception mi_excepcion){
System.out.println(mi_excepcion.toString());
System.out.println(mi_excepcion.getMessage());
}
finally {
return 0.0;
}
}
Veamos algunas peculiaridades sobre la sintaxis de la captura y gestin de la
excepcin:
1. En primer lugar, la estructura try {...} catch(...){...} con la que ya nos hemos
encontrado con anterioridad.
Dentro de la parte try{...} es donde debe surgir la excepcin, en este caso,
donde debe estar el throw. En este caso, la excepcin que puede surgir de
este fragmento de cdigo es de tipo Exception, y se debe ser el tipo de
excepciones que capturemos dentro de la estructura catch(...){...}.
En la estructura catch (...){...} hay dos partes diferenciadas que debemos
destacar:
- En primer lugar, entre parntesis, como si fuera la cabecera de un mtodo,
debemos situar el tipo de excepcin que queremos capturar (en nuestro caso,
Exception), seguido de un identificador para el mismo, por ejemplo
mi_excepcion, que ser el identificador por el que nos podamos referir a la
misma en el bloque entre llaves:
catch (Exception mi_excepcion){...}
- En segundo lugar, entre llaves, nos podemos encontrar con la parte en la que
gestionamos la excepcin. Qu queremos hacer en caso de que surja la
excepcin? Por ejemplo, en nuestro caso, lo que vamos a hacer es
simplemente mostrar un mensaje por pantalla que nos permita saber de qu
tipo es la excepcin generada, y mostrar el mensaje que le hemos asignado a
la misma:
catch (Exception mi_excepcion){
System.out.println(mi_excepcion.toString());
System.out.println(mi_excepcion.getMessage());
}
2. En segundo lugar, podemos observar el bloque finally {...} tambin propio del
trabajo con excepciones (debe ir siempre a continuacin de un bloque try
{...} catch (...) {...}), que nos permite realizar ciertas acciones que nos permitan
salir del mtodo acceso_por_indice (double [], int): double de una forma ms
correcta o elegante. Por ejemplo, dicho bloque se podra utilizar para cerrar
la conexin con un fichero que tuviramos abierto, o con una base de datos, o
para vaciar los bferes, ... .
}
}
//Caso excepcional:
throw new Exception ("El indice " + indice
+ " no es una posicion valida");
try {
}
else { //Caso excepcional:
throw new IndiceFueraDeRangoExcepcion ("El indice " + indice + " no es
una posicion valida");
}
}
catch (IndiceFueraDeRangoExcepcion mi_excepcion)
{ System.out.println(mi_excepcion.toString());
System.out.println(mi_excepcion.getMessage()); throw mi_excepcion;
}
}
Como podemos observar, el mtodo no ha cambiado, salvo en que donde antes utilizbamos
Exception ahora hemos pasado a usar IndiceFueraDeRangoExcepcion.
Por supuesto, al definir la clase IndiceFueraDeRangoExcepcion podamos haber redefinido alguno
de los mtodos de la misma, o haber aadido mtodos nuevos. Por ejemplo, podamos haber
redefinido el mtodo toString (): String en la misma para que mostrase un mensaje distinto al que
muestra en su definicin por defecto:
class IndiceFueraDeRangoExcepcion extends Exception{ public
IndiceFueraDeRangoExcepcion (){
super();
}
public IndiceFueraDeRangoExcepcion (String s){ super(s);
}
public String toString (){
return ("Se ha producido la excepcion " +
this.getClass().getName() + "\n" +
"Con el siguiente mensaje: " + this.getMessage() + "\n");
}
}
Cuando ahora invoquemos al mtodo toString (): String sobre algn objeto de la clase
IndiceFueraDeRangoExcepcion obtendremos una cadena como la siguiente:
Se ha producido la excepcion IndiceFueraDeRangoExcepcion Con el siguiente
mensaje: El indice 575 no es una posicion valida
Por ltimo, recordamos de nuevo que si hubiramos declarado la clase por herencia de la clase
RunTimeException no hubiera sido necesario gestionar o capturar la misma.
//o NoSuchElementException
//Intentamos convertir cada cadena
//a su tipo de dato original:
double leer_double;
int leer_int;
leer_double = Double.parseDouble(leer2);
//Lanza NumberFormatException
leer_int = Integer.parseInt (leer3);
//Mostramos por la consola las diversas cadenas:
System.out.println ("La cadena leida es " + leer);
System.out.println ("El double leido " + leer_double);
System.out.println ("El entero leido " +
leer_int);
}
else {
// El fichero ya existe:
System.out.println("El fichero ya existe y no se creo");
}
}
catch (FileNotFoundException f_exception) {
//Excepcion que surge si no encontramos el fichero
//al crear el Scanner
System.out.println ("Las operaciones de lectura no
se han podido llevar a cabo,");
System.out.println ("ya que ha sucedido un problema
al buscar el fichero para lectura");
System.out.println (f_exception.toString());
}
catch (IOException io_exception){
//Excepcion que puede surgir en
//alguna de las operaciones de escritura
System.out.println("Ocurrio algun error de entrad y salida");
System.out.println (io_exception.toString());
}
catch (NumberFormatException nb_exception){
//Excepcion que ocurre al realizar una conversion de una cadena
//de caracteres a un tipo numerico
System.out.println ("Ha ocurrido un error de
conversin de cadenas a numeros");
System.out.println (nb_exception.toString());
}
catch (NoSuchElementException nse_exception){