About this entry
You’re currently reading the article “Esperando lo Inesperado: Excepciones en Java (Exceptions in Java, Spanish only).”
- Published:
- November 25th 01:27 AM
- Updated:
- June 11th 05:35 AM
- Sections:
- Tutorial de Java
Esperando lo Inesperado: Excepciones en Java (Exceptions in Java, Spanish only)
No siempre estará todo lo que el programa necesita para correr o la entrada no siempre vendrá como nosotros quisiéramos. Algunas veces los métodos que nosotros escribimos no pueden completar y necesitamos "avisarle" al que lo mando a llamar que este no ha terminado con éxito. Para estos casos utilizamos las excepciones. Este artículo explica como.
Liked it? !
Antes de leer este artículo se recomienda haber leído Extendiendo el Comportamiento de las Clases.
Índice
- Introducción
- Levantando Excepciones
- Excepciones Chequeadas versus Excepciones en Tiempo de Corrida
- Manejando Excepciones
- Agregando Información Adicional a las Excepciones
Introducción
Normalmente, la información de un método es es comunicada a quien lo invoca a través del valor de retorno. Podríamos comunicar esta situación no esperada a través del valor de retorno, pero esto presenta muchas limitaciones:
- No habría forma de diferenciar entre un valor de retorno valido y un código de error. Una función que devuelve un entero podría devolver -1 cuando por un error calcular una respuesta. Como podría diferenciarse este de una respuesta verdadera de -1 ?
- No tenemos una forma clara de diferenciar los distintos errores que se pueden producir. Si escogemos un código de retorno distinto para cada posible error, se convierte en una función complicada de llamar.
Pensemos en la clase SistemaDeEcuaciones que diseñado para resolver un sistema de n ecuaciones lineales con n incógnitas. El método incluía un método resolver con el encabezado:
public float[] resolver()Que devolvía un arreglo con el resultado de las distintas incógnitas. Que pasaría si el sistema de ecuaciones no se puede resolver? Que devolvería el método? Podríamos pensar que si el sistema no tiene una solución exacta el método podría devolver null. Como podríamos diferenciar si no se pudo resolver porque hay infinitas soluciones o porque no hay ninguna solución?
Levantando Excepciones
Para permitirle a los métodos responder a lo inesperado, Java provee un medio alternativo de terminar la ejecución de un método. Esto se realiza a través de enunciado throw:
throw referencia-a-excepcion;Cuando un método ejecuta el enunciado throw, decimos que levanta este levanta una excepcion. La referencia puede ser una instancia de Exception o una subclase de Exception.
Imagínese un cadena de invocaciones que lleva hasta la invocación de un método en particular. Es decir, el primero en ejecutarse es main. main invoca un método. Este método a su vez invoca otro método y así sucesivamente hasta que la situación inesperada se invoca. El levantar una excepción provoca que el método termine inmediatamente. Sin embargo, el enunciado throw no devuelve un resultado ni la ejecución continua desde donde la invocación ocurrió. En vez de esto, la excepción pasa a través de cada invocación de la cadena forzando a que cada método termine. Para cada método de la cadena parecería ser que el método al cual este invoco hubiera levantado la excepción. Eventualmente esta cascada llega hasta main que empezó la ejecución del programa, y en este punto el programa termina.
Supongamos el siguiente ejemplo de una implementación de una clase Pila:
class Pila { private int arreglo[], top; public static void main(String []args) { Pila p = new Pila(5); p.push(10); p.push(20); p.push(30); System.out.println(p.pop()); System.out.println(p.pop()); System.out.println(p.pop()); } public Pila(int n) { arreglo = new int[n]; top=0; } public void push(int elem) { top++; arreglo[top]=elem; } public int pop() { int elem=arreglo[top]; top--; return elem; } }El constructor recibe el tamaño de la Pila y se utiliza push para meter elementos y pop para sacar elementos. Este código podría potencialmente fallar, ya que se se insertan mas elementos de la capacidad de la Pila podría haber problema. Podemos validar esta condición y se en dado caso el que utiliza nuestra Pila trata de meter mas elementos de los que caben, levantar una excepción. El push modificado quedaría así:
public boolean isStackFull() { return (top==arreglo.length); } public void push(int elem) throws Exception { if (isStackFull()) throw new Exception("Pila llena!"); top++; arreglo[top]=elem; }El constructor de Exception recibe una hilera describiendo la excepción que se esta levantando. La cláusula throws no es del todo desconocida para usted, ya que la hemos utilizado varias veces:
public static void main(String args[]) throws Exception {Ahora podemos, finalmente podemos explicar este requerimiento: Cualquier método que pueda levantar una excepción debe de advertirlo en su declaración con la cláusula throws. Esta cláusula consiste en la palabra reservada throws seguido por una lista de las subclases de Exception que pueden ser levantadas por este método. Este requerimiento aplica no solo a métodos que incluyen el enunciado throws sino que a todos los métodos que pueden levantar una excepción al invocar otros métodos que incluyen throws Exception. Por lo que en el ejemplo de la Pila, como el método main llama a métodos que pueden levantar una excepción (como push) también debe de advertirlo con la cláusula throws Exception. El ejemplo completo quedaría así:
class Pila { private int arreglo[], top; public static void main(String []args) throws Exception { Pila p = new Pila(5); p.push(10); p.push(20); p.push(30); System.out.println(p.pop()); System.out.println(p.pop()); System.out.println(p.pop()); } public Pila(int n) { arreglo = new int[n]; top=0; } public boolean isStackEmpty() { return (top==0); } public boolean isStackFull() { return (top==arreglo.length); } public void push(int elem) throws Exception { if (isStackFull()) throw new Exception("Pila llena!"); top++; arreglo[top]=elem; } public int pop() throws Exception { if (isStackEmpty()) throw new Exception("Pila vacia!"); int elem=arreglo[top]; top--; return elem; } }Ejercicio de clase
Agréguele al método resolver de las clases GaussJordan y Cramer una validación para que en el caso que el sistema de ecuaciones no se pueda resolver levante una excepción con el mensaje "No existe solución exacta!".
Excepciones Chequeadas versus Excepciones en Tiempo de Corrida
Java define una clase RuntimeException que es una extensión de Exception. Algunas subclases de RuntimeException son ArithmeticException, ClassCastException, IndexOutOfBoundsException, NullPointerException. A diferencia de Exception y otras subclases de Exception, el compilador no requiere que estas se mencionen en una cláusula throws. La razón es que estas excepciones representan errores de código que pudieran ocurrir en cualquier método, por lo que prácticamente todos los métodos tendrían que mencionarlas. Ha estas excepciones se les llama excepciones en tiempo de corrida.
Las excepciones que deben ser mencionadas en la cláusula throws se les llama excepciones chequeadas porque el compilador chequea que la cláusula throws coincida con cualquier sentencia throw del método o throws de los métodos invocados.
Manejando Excepciones
Por defecto, la cláusula throws levanta una cadena de terminaciones de métodos, comenzando desde el método que levanto la excepción hasta el método main provocando que el programa termine. Algunas veces esto es un poco extremo para manejar situaciones inesperadas. Algunas veces es posible que el programa responda a la situación inesperada y se recupere. Como puede un método responder a una excepción levantada por otro método que este invoco?
Java provee métodos de "agarrar" la excepción que es levantada por métodos que hemos invocado. "Agarrar" una excepción quiere decir romper con la cadena de terminaciones que lleva a que el programa termine y poder manejar la situación de una forma menos abrupta. Para "agarrar" excepciones necesitamos encerrar las invocaciones de los métodos que pudieran levantarlas en un bloque try,
try { p.push(10); }y precederlo de un bloque catch que incluya las instrucciones para manejar esta excepción. El método main del ejemplo de la pila podríamos modificarlo de la siguiente manera:
public static void main(String []args) { Pila p = new Pila(5); try { p.push(10); } catch (Exception e) { System.out.println("No se pudo insertar el elemento 10."); } try { p.push(20); } catch (Exception e) { System.out.println("No se pudo insertar el elemento 20."); } try { p.push(30); } catch (Exception e) { System.out.println("No se pudo insertar el elemento 30."); } try { System.out.println(p.pop()); System.out.println(p.pop()); System.out.println(p.pop()); } catch(Exception e) { System.out.println("No se pudieron sacar todos los elementos."); } }Agregando Información Adicional a las Excepciones
Algunas veces nosotros deseamos devolver información adicional al levantar una excepción que un simple mensaje. Por ejemplo si nosotros al levantar una excepción al no poder meter un elemento a una Pila quisiéramos devolver cual fue el elemento que no se pudo insertar correctamente. Podemos hacer esto heredando a la clase Exception, agregando todo el estado necesario para poder almacenar la información adicional. Podemos entonces definir la excepción:
class StackIsFullException extends Exception { private int elem; public StackIsFullException(int elem) { super(); this.elem=elem; } public String toString() { return "Pila llena: no se pudo insertar el elemento " + elem; } }El método push entonces quedaría así:
public void push(int elem) throws StackIsFullException { if (isStackFull()) throw new Exception(elem); top++; arreglo[top]=elem; }y la llamada quedaría así:
try { p.push(10); } catch (StackIsFullException e) { System.out.println(e.toString()); }Ejercicio de clase
Diseñe las clases InfiniteSolutionsException y NoSolutionException. Diséñelas de tal forma que el compilador no necesite que sean advertidas.

1 comment
Jump to comment form | comments rss [?]