## ¿Qué es una Excepción y por qué debo tener cuidado?

El término excepción es un forma corta da la frase "suceso excepcional" y puede definirse de la siguiente forma:

Definición: Una excepción es un evento que ocurre durante la ejecución del programa que interrumpe el flujo normal de las sentencias.

Muchas clases de errores pueden utilizar excepciones -- desde serios problemas de hardware, como la avería de un disco duro, a los simples errores de programación, como tratar de acceder a un elemento de un array fuera de sus límites. Cuando dicho error ocurre dentro de un método Java, el método crea un objeto 'exception' y lo maneja fuera, en el sistema de ejecución. Este objeto contiene información sobre la excepción, incluyendo su tipo y el estado del programa cuando ocurrió el error. El sistema de ejecución es el responsable de buscar algún código para manejar el error. En terminología java, crear una objeto exception y manejarlo por el sistema de ejecución se llama lanzar una excepción.

Después de que un método lance una excepción, el sistema de ejecución entra en acción para buscar el manejador de la excepción. El conjunto de "algunos" métodos posibles para manejar la excepción es el conjunto de métodos de la pila de llamadas del método donde ocurrió el error. El sistema de ejecución busca hacia atrás en la pila de llamadas, empezando por el método en el que ocurrió el error, hasta que encuentra un método que contiene el "manejador de excepción" adecuado. Un manejador de excepción es considerado adecuado si el tipo de la excepción lanzada es el mismo que el de la excepción manejada por el manejador. Así la excepción sube sobre la pila de llamadas hasta que encuentra el manejador apropiado y una de las llamadas a métodos maneja la excepción, se dice que el manejador de excepción elegido captura la excepción.

Si el sistema de ejecución busca exhaustivamente por todos los métodos de la pila de llamadas sin encontrar el manejador de excepción adecuado, el sistema de ejecución finaliza (y consecuentemente y el programa Java también).

Mediante el uso de excepciones para manejar errores, los programas Java tienen las siguientes ventajas frente a las técnicas de manejo de errores tradicionales:

- Ventaja 1: Separar el Manejo de Errores del Código "Normal"
- Ventaja 2: Propagar los Errores sobre la Pila de Llamadas
- Ventaja 3: Agrupar los Tipos de Errores y la Diferenciación de éstos

### Ventaja 1: Separar el Manejo de Errores del Código "Normal"

> En la programación tradicional, la detección, el informe y el manejo de errores se convierte en un código muy liado. Por ejemplo, supongamos que tenemos una función que lee un fichero completo dentro de la memeoria. En pseudo-código, la función se podría parecer a esto:

>     leerFichero {
>         abrir el fichero;
>         determinar su tamaño;
>         asignar suficiente memoria;
>         leer el fichero a la memoria;
>         cerrar el fichero;
>     }

> A primera vista esta función parece bastante sencilla, pero ignora todos aquello errores potenciales:
> - ¿Qué sucede si no se puede abrir el fichero?
> - ¿Qué sucede si no se puede determinar la longitud del fichero?
> - ¿Qué sucede si no hay suficiente memoria libre?
> - ¿Qué sucede si la lectura falla?
> - ¿Qué sucede si no se puede cerrar el fichero?
> Para responder a estas cuestiones dentro de la función, tendríamos que añadir mucho código para la detección y el manejo de errores. El aspecto final de la función se parecería esto:

>     codigodeError leerFichero {
>         inicializar codigodeError = 0;
>         abrir el fichero;
>         if (ficheroAbierto) {
>             determinar la longitud del fichero;
>             if (obtenerLongitudDelFichero) {
>                 asignar suficiente memoria;
>                 if (obtenerSuficienteMemoria) {
>                     leer el fichero a memoria;
>                     if (falloDeLectura) {
>                         codigodeError = -1;
>                     }
>                 } else {
>                     codigodeError = -2;
>                 }
>             } else {
>                 codigodeError = -3;
>             }
>             cerrar el fichero;
>             if (ficheroNoCerrado && codigodeError == 0) {
>                 codigodeError = -4;
>             } else {
>                 codigodeError = codigodeError and -4;
>             }
>         } else {
>             codigodeError = -5;
>         }
>         return codigodeError;
>     }

> Con la detección de errores, las 7 líneas originales (en negrita) se han covertido en 29 líneas de código-- a aumentado casi un 400 %. Lo peor, existe tanta detección y manejo de errores y de retorno que en las 7 líneas originales y el código está totalmente atestado. Y aún peor, el flujo lógico del código también se pierde, haciendo díficil poder decir si el código hace lo correcto (si ¿se cierra el fichero realmente si falla la asignación de memoria?) e incluso es díficil asegurar que el código continue haciendo las cosas correctas cuando se modifique la función tres meses después de haberla escrito. Muchos programadores "resuelven" este problema ignorádolo-- se informa de los errores cuando el programa no funciona.

> Java proporciona una solución elegante al problema del tratamiento de errores: las excepciones. Las excepciones le permiten escribir el flujo principal de su código y tratar los casos excepcionales en otro lugar. Si la función leerFcihero utilizara excepciones en lugar de las técnicas de manejo de errores tradicionales se podría parecer a esto:

>     leerFichero {
>         try {
>                abrir el fichero;
>                determinar su tamaño;
>                asignar suficiente memoria;
>                leer el fichero a la memoria;
>                cerrar el fichero;
>         } catch (falloAbrirFichero) {
>             hacerAlgo;
>         } catch (falloDeterminacionTamaño) {
>             hacerAlgo;
>         } catch (falloAsignaciondeMemoria) {
>             hacerAlgo;
>         } catch (falloLectura) {
>             hacerAlgo;
>         } catch (falloCerrarFichero) {
>             hacerAlgo;
>         }
>     }

Observa que las excepciones no evitan el esfuerzo de hacer el trabajo de detectar, informar y manejar errores. Lo que proporcionan las excepciones es la posibilidad de separar los detalles oscuros de qué hacer cuando ocurre algo fuera de la normal.

Además, el factor de aumento de cáodigo de este es programa es de un 250% -- comparado con el 400% del ejemplo anterior.

### Ventaja 2: Propagar los Errores sobre la Pila de Llamadas

> Una segunda ventaja de las exepciones es la posibilidad del propagar el error encontrado sobre la pila de llamadas a métodos. Supongamos que el método leerFichero es el cuarto método en una serie de llamadas a métodos anidadas realizadas por un programa principal: metodo1 llama a metodo2, que llama a metodo3, que finalmente llama a leerFichero.

>     metodo1 {
>         call metodo2;
>     }
>     metodo2 {
>         call metodo3;
>     }
>     metodo3 {
>         call leerFichero;
>     }

> Supongamos también que metodo1 es el único método interesado en el error que ocurre dentro de leerFichero. Tradicionalmente las técnicas de notificación del error forzarían a metodo2 y metodo3 a propagar el código de error devuelto por leerFichero sobre la pila de llamadas hasta que el código de error llegue finalmente a metodo1 -- el único método que está interesado en él.

>     metodo1 {
>         codigodeErrorType error;
>         error = call metodo2;
>         if (error)
>             procesodelError;
>         else
>             proceder;
>     }
>     codigodeErrorType metodo2 {
>         codigodeErrorType error;
>         error = call metodo3;
>         if (error)
>             return error;
>         else
>             proceder;
>     }
>     codigodeErrorType metodo3 {
>         codigodeErrorType error;
>         error = call leerFichero;
>         if (error)
>             return error;
>         else
>             proceder;
>     }

> Como se aprendió anteriormente, el sistema de ejecución Java busca hacia atrás en la pila de llamadas para encontrar cualquier método que esté interesado en manejar una excepción particular. Un método Java puede "esquivar" cualquier excepción lanzada dentro de él, por lo tanto permite a los métodos que están por encima de él en la pila de llamadas poder capturarlo. Sólo los métodos interesados en el error deben preocuparse de detectarlo.

>     metodo1 {
>         try {
>             call metodo2;
>         } catch (excepcion) {
>             procesodelError;
>         }
>     }
>     metodo2 throws excepcion {
>         call metodo3;
>     }
>     metodo3 throws excepcion {
>         call leerFichero;
>     }

> Sin embargo, como se puede ver desde este pseudo-código, requiere cierto esfuerzo por parte de los métodos centrales. Cualquier excepción chequeada que pueda ser lanzada dentro de un método forma parte del interface de programación público del método y debe ser especificado en la clausula throws del método. Así el método informa a su llamador sobre las excepciones que puede lanzar, para que el llamador pueda decidir concienzuda e inteligentemente qué hacer con esa excepción.

> Observa de nuevo la diferencia del factor de aumento de código y el factor de ofuscación entre las dos técnicas de manejo de errores. El código que utiliza excepciones es más compacto y más fácil de entender.

### Ventaja 3: Agrupar Errores y Diferenciación

> Frecuentemente las excepciones se dividen en categorias o grupos. Por ejemplo, podríamos imaginar un grupo de excepciones, cada una de las cuales representara un tipo de error específico que pudiera ocurrir durante la manipulación de un array: el índice está fuera del rango del tamaño del array, el elemento que se quiere insertar en el array no es del tipo correcto, o el elemento que se está buscando no está en el array. Además, podemos imaginar que algunos métodos querrían manejar todas las excepciones de esa categoria (todas las excepciones de array), y otros métodos podría manejar sólo algunas excepciones específicas (como la excepción de índice no válido).

> Como todas las excepciones lanzadas dentro de los programas Java son objetos de primera clase, agrupar o categorizar las excepciones es una salida natural de las clases y las superclases. Las excepciones Java deben ser ejemplares de la clase Throwable, o de cualquier descendiente de ésta. Como de las otras clases Java, se pueden crear subclases de la clase Throwable y subclases de estas subclases. Cada clase 'hoja' (una clase sin subclases) representa un tipo específico de excepción y cada clase 'nodo' (una clase con una o más subclases) representa un grupo de excepciones relacionadas.

> InvalidIndexException, ElementTypeException, y NoSuchElementException son todas clases hojas. Cada una representa un tipo específico de error que puede ocurrir cuando se manipula un array. Un método puede capturar una excepción basada en su tipo específico (su clase inmediata o interface). Por ejemplo, una manejador de excepción que sólo controle la excepción de índice no válido, tiene una sentencia catch como esta:

>     catch (InvalidIndexException e) {
>         . . .
>     }

> ArrayException es una clase nodo y representa cualquier error que pueda ocurrir durante la manipulación de un objeto array, incluyendo aquellos errores representados específicamente por una de sus subclases. Un método puede capturar una excepción basada en este grupo o tipo general especificando cualquiera de las superclases de la excepción en la sentencia catch. Por ejemplo, para capturar todas las excepciones de array, sin importar sus tipos específicos, un manejador de excepción especificaría un argumento ArrayException:

>     catch (ArrayException e) {
>         . . .
>     }

> Este manejador podría capturar todas las excepciones de array, incluyendo InvalidIndexException, ElementTypeException, y NoSuchElementException. Se puede descubrir el tipo de excepción preciso que ha ocurrido comprobando el parámtero del manejador e. Incluso podríamos seleccionar un manejador de excepciones que controlara cualquier excepción con este manejador:

>     catch (Exception e) {
>         . . .
>     }

> Los manejadores de excepciones que son demasiado generales, como el mostrado aquí, pueden hacer que el código sea propenso a errores mediante la captura y manejo de excepciones que no se hubieran anticipado y por lo tanto no son manejadas correctamente dentro de manejador. Como regla no se recomienda escribir manejadores de excepciones generales.

> Como has visto, se pueden crear grupos de excepciones y manejarlas de una forma general, o se puede especificar un tipo de excepción específico para diferenciar excepciones y manejarlas de un modo exacto.

### ¿ Y ahora qué?

> Ahora que has entendido qué son las excepciones y las ventajas de su utilizanción en los programas Java, es hora de aprender cómo utilizarlas.

Ozito