Tutorial de Java

Generar Excepciones en Java

Anterior | Siguiente
Cuando se produce una condición excepcional en el transcurso de la ejecución de un programa, se debería generar, o lanzar, una excepción. Esta excepción es un objeto derivado directa, o indirectamente, de la clase Throwable. Tanto el intérprete Java como muchos métodos de las múltiples clases de Java pueden lanzar excepciones y errores.

La clase Throwable tiene dos subclases: Error y Exception. Un Error indica que se ha producido un fallo no recuperable, del que no se puede recuperar la ejecución normal del programa, por lo tanto, en este caso no hay nada que hacer. Los errores, normalmente, hacen que el intérprete Java presente un mensaje en el dispositivo estándar de salida y concluya la ejecución del programa. El único caso en que esto no es así, es cuando se produce la muerte de un thread, en cuyo caso se genera el error ThreadDead, que lo que hace es concluir la ejecución de ese hilo, pero ni presenta mensajes en pantalla ni afecto a otros hilos que se estén ejecutando.

Una Exception indicará una condición anormal que puede ser subsanada para evitar la terminación de la ejecución del programa. Hay nueve subclases de la clase Exception ya predefinidas, y cada una de ellas, a su vez, tiene numerosas subclases.

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 nueve predefinidas y a sus subclases; 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, java901.java, origina una excepción de división por cero:

class java901 {
    public static void main( String[] a ) {
        int i=0, j=0, k;

        k = i/j;    // Origina un error de division-by-zero
        }
    }

Si compilamos y ejecutamos esta aplicación Java, obtendremos la siguiente salida por pantalla:

% javac java901.java
% java java901
java.lang.ArithmeticException: / by zero
        at java901.main(java901.java:25)

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 se generan explícitamente, a petición del programador, que suelen ser mucho menos severas y en la mayoría de los casos no resulta complicado recuperarse de ellas. Por ejemplo, si un fichero no puede abrirse, se puede preguntar al usuario que indique otro fichero; o si una estructura de datos se encuentra completa, siempre se podrá sobreescribir algún elemento que ya no se necesite.

Todas las excepciones deben llevar un mensaje asociado a ellas al que se puede acceder utilizando el método getMessage(), que presentará un mensaj describiendo el error o la excepción que se ha producido.

Si se desea, se pueden invocar otros métodos de la clase Throwable que presentan un traceado de la pila en donde se ha producido la excepción, o también se pueden invocar para convertir el objeto Exception en una cadena, que siempre es más intelegible y agradable a la vista.

Excepciones Predefinidas

Las excepciones predefinidas por la implementación actual del lenguaje Java y su jerarquía interna de clases son las que se representan en el esquema de la figura que aparece a continuación:

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 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 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.

El compilador Java obliga al programador a proporcionar el código de manejo o control de algunas de las excepciones predefinidas por el lenguaje. Por ejemplo, el siguiente programa java902.java, no compilará porque no se captura la excepción InterruptedException que puede lanzar el método sleep().

import java.lang.Thread;

class java902 {
    public static void main( String args[] ) {
        java902 obj = new java902();
        obj.miMetodo();
        }
  
    void miMetodo() {
        // Aqui se produce el error de compilacion, porque no se esta
        // declarando la excepcion que genera este metodo
        Thread.currentThread().sleep( 1000 ); // currentThread() genera
                                              // una excepcion
        }
    }

Este es un programa muy simple, que al intentar compilar, producirá el siguiente error de compilación que se visualizará en la pantalla tal como se reproduce a continuación:

% javac java902.java
java902.java:41: Exception java.lang.InterruptedException must be caught,
or it must be declared in the throws clause of this method.
    Thread.currentThread().sleep( 1000 ); // currentThread() genera
                                ^

Como no se ha previsto la captura de la excepción, el programa no compila. El error identifica la llamada al método sleep() como origen del problema. Así que, la siguiente versión del programa, java903.java, soluciona el problema generado por esta llamada.

import java.lang.Thread;

class java903 {
    public static void main( String args[] ) {
        // Se instancia un objeto 
        java903 obj = new java903();
        // Se crea la secuencia try/catch que llamara al metodo que
        // lanza la excepcion
        try { 
            // Llamada al metodo que genera la excepcion
            obj.miMetodo();
            }catch(InterruptedException e){} // Procesa la excepcion
        }
  
    // Este es el metodo que va a lanzar la excepcion
    void miMetodo() throws InterruptedException {
        Thread.currentThread().sleep( 1000 ); // currentThread() genera
                                              // una excepcion
        }
    }

Lo único que se ha hecho es indicar al compilador que el método miMetodo() puede lanzar excepciones de tipo InterruptedException. Con ello conseguimos propagar las excepción que genera el método sleep() al nivel siguiente de la jerarquía de clases. Es decir, en realidad no se resuelve el problema sino que se está pasando a otro método para que lo resuelva él.

En el método main() se proporciona la estructura que resuelve el problema de compilación, aunque no haga nada, por el momento. Esta estructura consta de un bloque try y un bloque catch, que se puede interpretar como que intentará ejecutar el código del bloque try y si hubiese una nueva excepción del tipo que indica el bloque catch, se ejecutaría el código de este bloque, si ejecutar nada del try.

La transferencia de control al bloque catch no es una llamada a un método, es una transferencia incondicional, es decir, no hay un retorno de un bloque catch.

Crear Excepciones Propias

También el programador puede lanzar sus propias excepciones, extendiendo la clase System.exception. Por ejemplo, considérese un programa cliente/servidor. El código cliente se intenta conectar al servidor, y durante 5 segundos se espera a que conteste el servidor. Si el servidor no responde, el servidor lanzaría la excepción de time-out:

class ServerTimeOutException extends Exception {}

public void conectame( String nombreServidor ) throws Exception {
    int exito;
    int puerto = 80;

    exito = open( nombreServidor,puerto );
    if( exito == -1 )
        throw ServerTimeOutException;
    }

Si se quieren capturar las propias excepciones, se deberá utilizar la sentencia try:

public void encuentraServidor() {
   ...
   try {
        conectame( servidorDefecto );
        catch( ServerTimeOutException e ) {
            g.drawString( 
                "Time-out del Servidor, intentando alternativa",5,5 );
            conectame( servidorAlterno );
            }
    ...
    }

Cualquier método que lance una excepción también debe capturarla, o declararla como parte del interfaz del método. Cabe preguntarse entonces, el porqué de lanzar una excepción si hay que capturarla en el mismo método. La respuesta es que las excepciones no simplifican el trabajo del control de errores. Tienen la ventaja de que se puede tener muy localizado el control de errores y no hay que controlar millones de valores de retorno, pero no van más allá.

Y todavía se puede plantear una pregunta más, al respecto de cuándo crear excepciones propias y no utilizar las múltiples que ya proporciona Java. Como guía, se pueden plantear las siguientes cuestiones, y si la respuesta es afirmativa, lo más adecuado será implementar una clase Exception nueva y, en caso contrario, utilizar una del sistema.

  • ¿Se necesita un tipo de excepción no representado en las que proporciona el entorno de desarrollo Java?
  • ¿Ayudaría a los usuarios si pudiesen diferenciar las excepciones propias de las que lanzan las clases de otros desarrolladores?
  • ¿Si se lanzan las excepciones propias, los usuarios tendrán acceso a esas excepciones?
  • ¿El package propio debe ser independiente y auto-contenido?

Navegador

Home | Anterior | Siguiente | Indice | Correo