Excepciones y errores
java.lang
de J2SE, pero la mayoría de las clases de error se han eliminado, dejando sólo las siguientes:
java.lang.Error
java.lang.OutOfMemoryError
java.lang.VirtualMachineError
Throwable.printStackTrace()
es parte de la especificación de CLDC (aunque no lo es la versión sobrecargada que redirige el trazado de la pila (stack) a otro sitio diferente al flujo de salida de error).
Sin embargo el formato de salida de este método es dependiente de la implementación. En KVM este método sólo escribe el nombre de la excepción.
Este método puede gestionar la excepción él mismo o bien pasarla al método llamante. En cualquier caso, la excepción es capturada y procesada en algún punto.
Forma general de un bloque de gestión de excepciones
try { // bloque de código } catch (TipoExcepcion1 exOb){ // gestor de excepciones para TipoExcepcion1 } catch (TipoExcepcion2 exOb){ // gestor de excepciones para TipoExcepcion2 } // ... finally { // bloque de código que se ejecutara antes de // que termine el bloque try }
Esta clase tiene como subclase a RuntimeException, que representa excepciones definidas automáticamente por los programas (división por 0, índice inválido de matriz, etc). Además tiene otras subclases comoClassNotFoundException
,InterruptedException
, etc.
Suelen ser fallos catastróficos no gestionados por nuestros programas. Ejemplo: desbordamiento de la pila. En CLDC 1.1 hay dos subclases deError
:VirtualMachineError
yNoClassDefFoundError
.
class Exc1 { static void subroutine() { int d = 0; int a = 10 / d; } public static void main(String args[]) { System.out.println("Antes de Exc1.subroutine"); Exc1.subroutine(); System.out.println("Despues de Exc1.subroutine"); } }
Exc1
, ya que no hemos incluido un gestor de la excepción.
En el caso de J2SE, el gestor por defecto muestra la excepción y el trazado de la pila en la salida estándar, y termina el programa.
Salida del programa
Antes de Exc1.subroutine Exception in thread "main" java.lang.ArithmeticException: / by zero at Exc1.subroutine(Exc1.java:4) at Exc1.main(Exc1.java:8)
En el caso de CLDC lo único que hace es terminar el programa.
Salida del programa
Antes de Exc1.subroutine
kvm_g
) para obtener el trazado de la pila cuando se lanza la excepción. Necesitamos además hacer uso del método Trowable.printStackTrace()
en la siguiente forma (P32/Exc1.version2.java):
try{ int a = 10 / d; } catch (Exception ex){ ex.printStackTrace(); }
Ahora la salida sería la siguiente:
Antes de Exc1.subroutine java.lang.ArithmeticException at Exc1.subroutine(+5) at Exc1.main(+11) Despues de Exc1.subroutine
class Exc2 { public static void main(String args[]) { int d, a; try { // controla un bloque de código. d = 0; a = 42 / d; System.out.println("Esto no se imprimirá."); } catch (ArithmeticException e) {// captura el error de división System.out.println("División por cero."); } System.out.println("Después de la sentencia catch."); } }
El objetivo de una sentencia catch bien diseñada debería ser resolver la condición de excepción y continuar.
Otro ejemplo: P34/HandleError.java
// Gestiona una excepción y continua. import java.util.Random; class HandleError { public static void main(String args[]) { int a=0, b=0, c=0; Random r = new Random(); for(int i=0; i<32000; i++) { try { b = r.nextInt(); c = r.nextInt(); a = 12345 / (b/c); } catch (ArithmeticException e) { System.out.println("Division por cero."); a = 0; // asigna a la variable el valor 0 y continua } System.out.println("a: " + a); } } }
catch (ArithmeticException e) { System.out.println("Excepcion: " + e); a = 0; // hacer a=0 y continuar }
Salida producida cuando se produce la excepción
Excepcion: java.lang.ArithmeticException
El siguiente programa produce una excepción si se ejecuta sin parámetros y otra distinta si se ejecuta con un parámetro.
class MultiCatch { public static void main(String args[]) { try { int a = args.length; System.out.println("a = " + a); int b = 42 / a; int c[] = { 1 }; c[42] = 99; } catch(ArithmeticException e) { System.out.println("Division por 0: " + e); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("Indice fuera de limites: " + e); } System.out.println("Despues del bloque try/catch."); } }
Al ordenar los bloques catch, las subclases de excepción deben ir antes que la superclase (en caso contrario no se ejecutarían nunca y daría error de compilación por código no alcanzable).
Ejemplo: P36/SuperSubCatch.java
class SuperSubCatch { public static void main(String args[]) { try { int a = 0; int b = 42 / a; } catch(Exception e) { System.out.println("catch para cualquier tipo de excepción."); } /* Este catch nunca se ejecutará */ catch(ArithmeticException e) { // ERROR - no alcanzable System.out.println("Esto nunca se ejecutará."); } } }
class NestTry { public static void main(String args[]) { try { int a = args.length; /* Si no hay ningún argumento en la línea de órdenes se generará una excepción de división por cero. */ int b = 42 / a; System.out.println("a = " + a); try { // bloque try anidado /* Si se utiliza un argumento en la línea de órdenes se generará una excepción de división por cero. */ if(a==1) a = a/(a-a); // división por cero /* Si se le pasan dos argumentos en la línea de órdenes, se genera una excepción al sobrepasar los límites del tamaño de la matriz. */ if(a==2) { int c[] = { 1 }; c[42] = 99; // genera una excepción de fuera de límites } } catch(ArrayIndexOutOfBoundsException e) { System.out.println("Indice fuera de limites: " + e); } } catch(ArithmeticException e) { System.out.println("División por 0: " + e); } } }
Sentencias try anidadas en forma menos obvia
/* Las sentencias try pueden estar implícitamente anidadas a través de llamadas a métodos. */ class MethNestTry { static void nesttry(int a) { try { // bloque try anidado /* Si se utiliza un argumento en la línea de órdenes, la siguiente sentencia efectúa división por cero */ if(a==1) a = a/(a-a); // división por zero /* Si se le pasan dos argumentos en la línea de órdenes, se sobrepasan los límites de la matriz */ if(a==2) { int c[] = { 1 }; c[42] = 99; // genera una excepción de fuera de límites } } catch(ArrayIndexOutOfBoundsException e) { System.out.println("Indice fuera de limites: " + e); } }
public static void main(String args[]) { try { int a = args.length; /* Si no hay ningún argumento en la línea de órdenes, la siguiente sentencia generará una excepción de división por cero */ int b = 42 / a; System.out.println("a = " + a); nesttry(a); } catch(ArithmeticException e) { System.out.println("División por 0: " + e); } } }
throw objetoThrowable;
El objetoThrowable
puede obtenerse mediante:
class ThrowDemo { static void demoproc() { try { throw new NullPointerException("demo"); } catch(NullPointerException e) { System.out.println("Captura dentro de demoproc."); throw e; // relanza la excepción } }
public static void main(String args[]) { try { demoproc(); } catch(NullPointerException e) { System.out.println("Nueva captura: " + e); } } }
El flujo de ejecución se detiene tras la sentencia throw (cualquier sentencia posterior no se ejecuta).
Salida del anterior ejemplo:
Captura dentro de demoproc. Nueva captura: java.lang.NullPointerException: demo
Forma general de declaración de método con throws
tipo metodo(lista_de_parametros) throws lista_de_excepciones { // cuerpo del metodo }
// Programa erróneo que no compila class ThrowsDemo { static void throwOne() { System.out.println("Dentro de throwOne."); throw new IllegalAccessException("demo"); } public static void main(String args[]) { throwOne(); } }
El método que use al del throws debe capturar todas las excepciones listada con el throws.
// Programa correcto class ThrowsDemo { static void throwOne() throws IllegalAccessException { System.out.println("Dentro de throwOne."); throw new IllegalAccessException("demo"); } public static void main(String args[]) { try { throwOne(); } catch (IllegalAccessException e) { System.out.println("Captura " + e); } } }
Ejemplo de uso de finally: P42/FinallyDemo.java
class FinallyDemo { static void procA() {// Lanza una excepción fuera del metodo try { System.out.println("Dentro de procA"); throw new RuntimeException("demo"); } finally { System.out.println("Sentencia finally de procA"); } } static void procB() {// Ejecuta la sentencia return // dentro del try try { System.out.println("Dentro de procB"); return; } finally { System.out.println("Sentencia finally de procB"); } } static void procC() {// Ejecuta un bloque try normalmente try { System.out.println("Dentro de procC"); } finally { System.out.println("Sentencia finally de procC"); } }
public static void main(String args[]) { try {procA(); } catch (Exception e) { System.out.println("Excepción capturada"); } procB(); procC(); } }
Salida del anterior programa
Dentro de procA Sentencia finally de procA Excepción capturada Dentro de procB Sentencia finally de procB Dentro de procC Sentencia finally de procC
Ejemplo: P43/ExceptionDemo.java
class MyException extends Exception { private int detail; MyException(int a) { detail = a; } public String toString() { return "MyException[" + detail + "]"; } } class ExceptionDemo { static void compute(int a) throws MyException { System.out.println("Ejecuta compute(" + a + ")"); if(a > 10) throw new MyException(a); System.out.println("Finalización normal"); } public static void main(String args[]) { try { compute(1); compute(20); } catch (MyException e) { System.out.println("Captura " + e); } } }La salida de este programa sería:
Ejecuta compute(1) Finalización normal Ejecuta compute(20) Captura MyException[20]