Bienvenidos sean a este post, hoy hablaremos sobre las excepciones o evento excepcional los cuales son problemas que pueden surgir durante la ejecucion de nuestro programa, cuando una excepcion ocurre se interrumpe la normal ejecucion de nuestro programa y termina de forma anormal pero afortunadamente podemos manejarlas. Las excepciones pueden ocurrir por las siguientes situaciones:

  • El usuario entro un dato invalido
  • Un archivo que necesita usarse no es encontrado
  • Se perdio la conexion de red en una transaccion
  • La VM (Maquina Virtual) se quedo sin memoria

Las excepciones ademas de ser por el usuario, tambien pueden ser por un error del programador, es decir logica, o por un problema de Hardware (de orden fisico), podriamos decir igualmente que las excepciones pueden ser divididas en tres categorias:

  • Excepciones chequeadas, son las notificaciones que nos aparecen en el momento de la compilacion, tomen nota cual es la excepcion para corregirla en el programa porque no permitira compilarlo hasta que este resuelto
  • Excepciones no-chequeadas, son las notificaciones que ocurren en el momento de ejecucion, tambien son llamados Excepciones de rutina, estos son errores de programacion, mal uso de una API y lamentablemente no pueden ser notificadas en la compilacion
  • Errores, estas excepciones son aquellas que estan mas alla del alcance de los usuarios o programadores, como puede ser un desbordamiento de memoria, error de hardware, etc, y obviamente no van a ser notificadas por el compilador

Todas las excepciones son un subtipo de la clase java.lang.Exception, la cual a su vez es una subclase de la clase Throwable. Los errores son situaciones anormales de nuestro programa y como no pueden ser manejados por el mismo para notificarlos debemos hacerlo desde la clase Throwable, en cambio de la clase Exception tendremos dos subclases encargadas de manejar las excepciones chequeadas (IOException) y las excepciones no chequeadas (RuntimeException), pasemos a ver algunos metodos disponibles en la clase Throwable:

  • public String getMessage(), nos devuelve una informacion detallada de la excepcion
  • public Throwable getCause(), devuelve la causa de la excepcion por medio de un objeto Throwable
  • public String toString(), devuelve el nombre de la clase concatenada con el resultado de getMessage()
  • public void printStackTrace(), imprime el resultado de toString() junto con el seguimiento de la pila a System.err
  • public StackTraceElement[] getStackTrace(), devuelve un array conteniendo los elementos del seguimiento de la pila, donde el indice cero sera el elemento de arriba de la pila y el ultimo indice el del ultimo elemento de la pila
  • public Throwable fillInStackTrace(), Rellena el seguimiento de pila de este objeto Throwable con el seguimiento de pila actual, agregando a cualquier información anterior en el seguimiento de pila.

Ahora hablemos sobre unos metodos utilizados en algunos ejemplos anteriores pero no comentamos mucho y estos son try y catch, estos sirven basicamente para capturar las excepciones, donde por medio del try monitroreamos el codigo y con el catch mostraremos una notificacion de excepcion, su estructura es:

try {
.... codigo de nuestro programa ...
} catch( nombreException nombreObjeto ) {
.... bloque del catch ....
}

El metodo try siempre debe ser seguido por un metodo catch o un metodo finally, podemos decir que try es la encargada de generar la excepcion y con el catch o finally podemos manipular esta excepcion, antes de ver un ejemplo nosotros tambien podemos utilizar varios bloques de catch para poder interceptar varias excepciones, al igual que sucede en switch() si la primera excepcion no coincide pasara a la siguiente, y sino a la siguiente y asi sucesivamente, en los bloques podemos mostrar una notificacion con el tipo de excepcion y luego hacerlo salir con return -1 dando a entender que hubo un error o excepcion, otra caracteristica agregada a esto a partir de JDK 7 es la posibilidad de utilizar varios tipos de excepciones, por ejemplo:

catch (IOException | FileNotFoundException e) {
.... instrucciones ....
}

Aunque ya en los posts anteriores hemos visto al bloque try y al bloque catch vamos a hacer un ejemplo para entenderlo mejor:

import java.io.*;

public class Excepcion
{
public static void main(String[] args)
{
        try
        {
        int a[] = new int[2];
        System.out.println("Acceder al elemento tres: " + a[3]);
        }
        catch (ArrayIndexOutOfBoundsException e)
        {
                System.out.println(" Exception lanzo: " + e);
        }
        System.out.println("Fuera del bloque");
}
}

Este es un ejemplo simple donde tendremos un error de tipo logico, porque generamos un Array de tres elementos pero al querer recuperar el tercer elemento nos equivocamos y le solicitamos el cuarto, esto del metodo try, como genera un error ira al metodo donde tendra una excepcion donde verificara si el array esta fuera de su indice y lo llamaremos e, dentro de ese bloque mostraremos el mensaje con el error y por ultimo mostraremos un mensaje que esta fuera de estos bloques, si lo compilamos y ejecutamos obtendremos esta salida:

tinchicus@dbn001vrt:~/programacion/java/program$ java Excepcion
Exception: java.lang.ArrayIndexOutOfBounds Exception lanzo: 3
Fuera del bloque
tinchicus@dbn001vrt:~/programacion/java/program$

Como pueden ver nos devolvio el tipo de excepcion y cual fue el problema, despues fuera de todo bloque mostro el mensaje final si nosotros corrigieramos el error solamente debera aparecer el ultimo mensaje, pasemos a hablar sobre los metodos throw y throws.

Estos metodos son usados para cuando el metodo no puede manejar las excepciones chequeadas, las mismas deben estar al final de la declaracion del metodo por ejemplo:

public static void main(String[] args) throws IOException {

La diferencia entre throws y throw es la forma en la que se utiliza, throws se usa en la declaracion del metodo y nos permitira posponer el uso del mismo, en cambio throw se usa para invocar directamente a la excepcion, por ejemplo veamos el siguiente caso:

import java.io.*;

public class className {
	
	public void deposit(double amount) throws RemoteException {
		
		throw new RemoteException();
	}
	... El resto de la clase ...
}

Observen como podemos tenerlo en el mismo metodo pero estara a la expectativa si ocurre la excepcion, en cambio el segundo llamado si invocara directamente a la excepcion, por ultimo podemos definir mas de una excepcion en throws para ello podemos usar la coma, modifiquemos el ejemplo anterior de la siguiente manera:

import java.io.*;

public class className {
	
	public void deposit(double amount) throws RemoteException,
				InsufficientFundsException {
		
		throw new RemoteException();
	}
	... El resto de la clase ...
}

Aca pudimos agregar otro metodo de excepcion a nuestro metodo con lo cual podremos monitorear mas de un tipo de excepcion. Nuestra siguiente estacion seran los bloques finally.

Esto bloques o metodos, siempre va a estar despues de un bloque try o de un bloque catch y se va a ejecutar haya ocurrido una excepcion o no, como en algun post anterior este bloque se usa para ejecutar las ultimas acciones de limpieza, p.e: lo hemos usado para cerrar nuestros streams, y trabaja de forma similar al catch, la estructura es la siguiente:

try {

	... Instrucciones ...

} catch( tipoExcepcion1 e1 ) {

	... Intrucciones del bloque ...

}  catch( tipoExcepcion2 e2 ) {

	... Intrucciones del bloque ...

}  catch( tipoExcepcion3 e3 ) {

	... Intrucciones del bloque ...

} finally {

	... Instrucciones del bloque finally ...

}

Con todo esto podemos decir que:

  • Un bloque catch no puede existir sin el bloque try
  • No es necesario el bloque finally si existen los bloques try y catch
  • El bloque try no puede existir sin el bloque catch o finally
  • Ningun codigo puede estar entre estos tres bloques, pueden estar antes o despues

Como hemos mencionado antes, cuando usamos streams, cualquier de los vistos en este post o en este post, es necesario cerrar el mismo y en los ejemplos lo haciamos por medio del bloque finally. A partir de JAVA 7 se agrego una nueva modalidad de try llamada try con recursos, la cual tambien puede ser referenciada como administracion automatica de recursos, la cual permite cerrar automaticamente los recursos utilizados en los bloques try/catch, veamos su sintaxis:

try (FileReader fr = new FileReader(archivoNombre)) {
.... Instrucciones ....
} catch (nombreExcepcion nombreObjeto) {
.... Instrucciones bloque catch ....
}

Para entender un poco mejor el concepto veamos el siguiente ejemplo:

import java.io.FileReader;
import java.io.IOException;

public class Try_withDemo
{
        public static void main(String[] args)
        {
                try (FileReader fr = new FileReader("entrada.txt"))
                {
                        char [] a = new char[150];
                        fr.read(a);
                        for(char c : a)
                                System.out.print(c);
                }
                catch (IOException e)
                {
                        e.printStackTrace();
                }
        }
}

En este caso llamaremos a dos paquetes: java.io.FileReader y java.io.IOException, despues en el try observen como por medio de un FileReader le asignamos un objeto y un archivo, con esto sabe que al finalizar su uso debe hacer automaticamente todas las tareas para liberar los recursos utilizados, luego por medio del metodo read() iremos llenando al array llamado a con todo el contenido de nuestro archivo, luego por medio del for mostraremos todo el contenido del array, luego tendremos un catch para que try funcione correctamente pero nos mostrara un mensaje en caso de ser necesario, si lo compilamos y ejecutamos obtendremos la siguiente salida:

tinchicus@dbn001vrt:~/programacion/java/program$ java Try_withDemo
Este sera un año espectacular para aprender algo nuevo!
tinchicus@dbn001vrt:~/programacion/java/program$

Deberemos tener en cuenta las siguientes cosas cuando trabajemos con el try con recursos:

  • Se debe implementar la interfaz AutoCloseable y esto implementara el close() automaticamente
  • Se puede declarar mas de una clase
  • Cuando se declara mas de una clase esta se cierran en modo invertido
  • Excepto por la declaracion entre parentesis el resto es igual a los bloques try/catch
  • Lo declarado entre parentesis se instancia antes de entrar en el bloque
  • El recurso declarado dentro del bloque es declarado como final implicitamente

La ultima particularidad que veremos es la posibilidad que disponemos para crear nuestras propias excepciones pero debemos tener los siguientes puntos en mente:

  • Todas las excepciones deben ser hijas de la clase Throwable
  • Si quieres escribir una excepcion que se implemente automaticamente , deberas extender la clase de Excepcion
  • Si quieres una excepcion de rutina, deberas extender la clase de Excepcion de rutina

Para definir nuestra propia excepcion deberemos hacerlo de la siguiente forma:

class MiExcepcion extends Exception {
... Instrucciones ...
}

Para entender un poco mejor esto, vamos a hacer un ejemplo donde primero crearemos nuestras clases de excepcion, para nuestro ejemplo vamos a crear nuestra primera clase sera:

InsufficientFundsException.java

import java.io.*;

public class InsufficientFundsException extends Exception
{
        private double amount;

        public InsufficientFundsException( double amount )
        {
                this.amount = amount;
        }

        public double getAmount()
        {
                return amount;
        }
}

Esta clase debe ser declarada con la extension de la clase Exception por medio del metodo extends y por ahora no hara ninguna funcion sino solamente devolvernos el valor en amount, pasemos a la siguiente clase para la cual crearemos otro archivo:

CheckingAccount.java

import java.io.*;

public class CheckingAccount
{
        private double balance;
        private int numero;

        public CheckingAccount(int numero)
        {
                this.numero = numero;
        }

        public void deposito(double cantidad)
        {
                balance += cantidad;
        }

        public void retiro(double cantidad)
                        throws InsufficientFundsException
        {
                if (cantidad <= balance)
                {
                        balance -= cantidad;
                } else {
                        double needs = cantidad - balance;
                        throw new InsufficientFundsException(needs);
                }
        }

        public double getBalance()
        {
                return balance;
        }

        public int getNumero()
        {
                return numero;
        }
}

En esta clase tendremos cuatro metodos, uno llamado getBalance() para devolver el valor de balance, otro llamado getNumero() para devolver el valor de numero, otro llamado deposito(), el cual incrementara el valor de balance con la cantidad que enviemos en cantidad, nuestro ultimo metodo es retiro() el cual tendra el metodo throws para conectarse con la clase anterior (InsufficientFundsException) y donde tendremos un condicional para chequear si cantidad es menor o igual a balance donde procede a hacer la resta, en caso contrario genera una variable de tipo double llamada needs donde efectua la resta de cantidad menos balance y luego lo envia a la clase anterior el valor almacenado en needs y con esto modificara el valor de amount de la clase a donde se envia la informacion y ahora procederemos a generar el programa para chequear:

BankDemo.java

public class BankDemo
{

        public static void main(String[] args)
        {
                CheckingAccount c = new CheckingAccount(101);
                System.out.println("Depositando $500...");
                c.deposito(500.0);

                try
                {
                        System.out.println("\nRetirando $100...");
                        c.retiro(100.0);
                        System.out.println("\nRetirando $600...");
                        c.retiro(600.0);
                }
                catch (InsufficientFundsException e)
                {
                        System.out.println("Perdon pero te faltan: "
                                        + e.getAmount());
                        e.printStackTrace();
                }
        }
}

En este programa primero crearemos un objeto de la clase creada por nosotros llamada CheckingAccount a la cual le enviaremos un valor de 101 para tener un balance de ese valor, luego le agregaremos $500 mas a nuestra cuenta, despues en el bloque de try retiraremos $100 por medio del metodo retiro, para luego retirar $600 de nuestra cuenta, en el metodo catch usaremos la clase InsufficientFundsExeption a la cual llamaremos e, para luego mostrar en pantalla por medio del metodo getAmount() la cantidad que nos falta para poder retirar ese valor y por ultimo imprime la pista de la pila, si compilamos este programa y lo probamos ocurrira esto:

tinchicus@dbn001vrt:~/programacion/java/program$ java BankDemo
Depositando $500...

Retirando $100...

Retirando $600...
Perdon pero te faltan: 200.0
InsufficientFundsException
        at CheckingAccount.retiro(CheckingAccount.java:26)
        at BankDemo.main(BankDemo.java:15)
tinchicus@dbn001vrt:~/programacion/java/program$
Nota: No es necesario compilar las clases anteriores a BankDemo.java porque cuando compilemos este el mismo compilador lo hara automaticamente para poder utilizarlas, les recomiendo este post para entender el concepto.

Observen como funciono perfectamente la utilizacion de una excepcion creada por nosotros, ya que nos permite calcular la diferencia y por ende informar correctamente porque no se pudo efectuar la accion, como pueden deducir esto nos permite crear devoluciones mas customizadas para la interfaz y por ende dando una mejor pista del problema, y para ir finalizando podemos decir que existen dos tipos comunes de excepciones:

  • JVM excepciones, son excepciones/errores exclusivamente de la JVM (Java Virtual Machine) los cuales puede ser ocasionados por: NullPointerException, ArrayIndexOutOfBoundsException, etc
  • Excepciones de programacion, son las excepciones explicitas del programa o lo hecho por parte del programador, error humano, IllegalArgumentException, IllegalStateException

En resumen, hoy hemos aprendido que son las excepciones, para que se utilizan, los bloques try, catch, finally, try con recursos, como crear nuestras propias excepciones, los metodos throws y throw, para que sirven, hemos varios ejemplos de distintas indoles y algunas cosillas mas, espero les haya sido util sigueme en Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios