Está en la página 1de 7

Resumen PAV (3)

Excepciones (exceptions)
Este resumen ha sido elaborado para ser ledo despus de haber asistido a clase: pretende fijar y profundizar en los conceptos ya explicados.

Introduccin
Un tema frecuente en los lenguajes de programacin es cmo se manejan los errores durante la ejecucin del programa. u sucedera si las condiciones !ue he supuesto cuando escriba el cdigo no se cumplen" Un enfo!ue com#n es dise$ar los mtodos o funciones de modo !ue de%uel%an un %alor pre%iamente determinado si se da una condicin de error. &onsideremos por ejemplo la funcin fopen del &''. Esta funcin abre un archi%o y de%uel%e un (file descriptor) *un %alor !ue representa ese archi%o+. ,i por alg#n moti%o se produjo un error- fopen de%uel%e NULL. &onsideremos el siguiente ejemplo.:
#include <stdio.h> int main() { File *pFile = fopen(readme.t t!" r#!)$ %% a&rimos el archi'o fputs((sto se escri&ir) al archi'o...!" pFile)$ %% escri&imos una cadena al archi'o fclose()$ %% cerramos el archi'o *

Puede ser !ue fopen no pueda abrir el archi%o por alg#n moti%o !ue desconocemos. ,i eso sucede- pFile ser/ igual a NULL *pues es el %alor !ue fopen de%uel%e cuando hay alg#n error+ y cuando se ejecute la siguiente lnea- el programa se detendr/- por!ue fputs necesita un archi%o %/lido para poder escribir la cadena. 0o importante del ejemplo es !ue el lenguaje no nos fuerza a enfrentar el hecho de que podra producirse un error. ueda a la responsabilidad del programador- si es !ue ste est/ dispuesto a tomarse la molestia y acabar las cosas hasta los #ltimos detalles. 1a%a y otros lenguajes m/s recientes adoptan otro enfo!ue al manejo de errores. 0as posibles condiciones de error dejan de considerarse un situacin casual. El manejo de errores se ha formalizado como parte del lenguaje 2con palabras reser%adas especficas33- de modo !ue si hay la posibilidad de !ue se produzca un error el programador deba tenerlo en cuenta.

Excepciones y errores
,e considera una excepcin en 1a%a a una situacin que est fuera de las condiciones previsibles del problema que estoy resolviendo. 4o se trata de errores !ue- por el problema tengo entre manos- se pueden pre%er y deben pre%er. 5or ejemplo- la funcin readLine() de la clase 6uffered7eader lee la lnea siguiente de un stream8. ,i estoy leyendo un archi%o- es pre%isible !ue en alg#n momento leer la #ltima lnea del archi%o. Esto no es una excepcin- sino una condicin !ue si no manejo dar/ un error. 5ero es un error perfectamente pre%isible. 5or eso- la definicin de funcin readLine() dice !ue readLine() de%uel%e un +trin, con la
. 4o interesa tanto entender exactamente !u hace cada funcin del ejemplo- sino fijarmos en cmo se maneja el error. 8 Un stream... un archi%o- del teclado- de cual!uier flujo de datos.

3.3

siguiente lnea del stream *archi%o o lo !ue sea+ y null si ya no hay ms lneas que leer. En cambioproduce una excepcin si se da otro tipo de error. Este segundo tipo de error- excepcional- es tal !ue el programa no puede continuar si alguien no lo arregla. 9 1a%a fuerza a !ue esto sea as. El programa sera algo as::
+trin, s$ %% nuestra 'aria&le para ,uardar la l-nea s = in.readLine()$ #hile (s .= null) { +/stem.out.println(s)$ s = in.readLine()$ *

;e modo m/s sinttico- se suele escribir as:


+trin, s$ #hile ( (s = in.readLine()) .= null) { +/stem.out.println(s)$ *

,in embargo- este cdigo no compilar/- pues readLine() puede generar una excepcin y 1a%a nos fuerza a !ue encaremos ese hecho. <eremos m/s adelante cmo se hace. =nteresa notar en el ejemplo cmo se est/ manejando la posibilidad de !ue no hayan m/s lneas !ue leer: comparando s con null- y terminando elegantemente el loop- si se %erifica esa condicin. 5ero- repetimos- esa no es una situacin excepcional. Excepcional sera !ue en plena lectura del archi%o derramemos una jarra de agua en el teclado o- menos dram/tico- se malograse el &; o el disco duro del !ue estamos leyendo. >tro ejemplo es la divisin entre cero. ?ay problemas en los !ue el denominador de una frmula pre%isiblemente puede ser cero *por ejemplo- en un sistema amortiguado- hay una frecuencia de resonancia. ,i el sistema opera a esa frecuencia- se autodestruye... justamente so !uiere decir denominador cero en este caso+. 5ero esto no es una excepcin: es el comportamiento normal *y !ue corresponde a lo !ue sucede en la realidad+ del modelo matem/tico. 5or eso- antes de hacer la operacin sera normal !ue %erifi!uemos !ue el denominador no es cero. En cambio- en otros casos el denominador cero es un tema %erdaderamente excepcional: no estaba pre%isto en el curso de la resolucin del problema. 4o podemos estar comparando el denominador con cero cada %ez !ue hacemos una di%isin. Es m/s sencillo dejar !ue se produzca una excepcin- y manejarla.

Generacin y manejo de excepciones


&uando un programa est/ ejecut/ndose- hay !ue considerar dos (momentos) distintos: a+ la situacin e cepcional- !ue es detectada por el cdigo !ue alguien o uno mismo ha escrito. El programador decide generar una excepcin por!ue su cdigo- en lo !ue a l respecta- no puede continuar. @enerar una excepcin es disparar una bengala de emergencia para indicarle al compilador !ue nuestro bu!ue *nuestro programa+ se est/ hundiendo. b+ el manejo de la e cepcin: la operacin de rescate del bu!ue. ;e esto se encargar/n
: ,uponiendo !ue in es un objeto 0uffered1eader.

383

otros- no los !ue est/n en el bu!ueA.

Generacin de excepciones
Una excepcin se genera en 1a%a usando la palabra reser%ada thro#. En el siguiente cdigo- el programador se !uiere asegurar de !ue last1oom no sea null antes de proseguir con su programa:
if (last1oom == null) { thro# ne# ( ception()$ * else { %* si,ue la e2ecuci3n normalmente *% * last1oom == null es un hecho !ue no debi darse- seg#n la lgica de resolucin del problema. ,i se da- no podemos seguir. Estamos desconcertados: cmo es !ue ha pasado esto" 4o nos !ueda otra alternati%a !ue generar una excepcin.

&uando escribimos un mtodo !ue puede generar una excepcion *en trminos pr/cticoscuando en alguna de sus lneas el mtodo usa la expresin thro#+ hay !ue decirlo as en la declaracin del mtodo. ;e lo contrario- el compilador se !uejar/. 5ara esto- se usa otra palabra reser%ada: thro#s. ,iguiendo con el cdigo anteriorB:
pu&lic 'oid 'ol'er( ) thro#s ( ception { if ( last1oom == null) { %% imposi&le... last1oom nunca de&er-a ser null thro#s ne# ( ception( )$ * else { %% (l c3di,o si,ue e2ecut)ndose normalmente 1oom tmp = current1oom$ current1oom = last1oom$ last1oom = tmp$ * *

0a lnea thro# ne# ( ception( ) merecera un poco m/s de comentarios. &omo se puede deducir- ( ception( ) es simplemente el constructor de la claseC ( ception- !ue est/ definida en la librera est/ndar de 1a%aD. 5or tanto- cuando decimos ne# ( ception( )estamos creando un nue%o objeto de tipo ( ception4. ?ablaremos m/s de esto despus de explicar cmo se manejan las excepciones.

A ;entro de los lmites de esta analoga- por supuesto. En la pr/ctica puede ser !ue el !ue genere la excepcin y escriba el cdigo para manejarla sean la misma persona. B ,e sobreentiende !ue last1oom y current1oom son %ariables de la clase !ue contiene al mtodo 'ol'er- !ue ya est/n definidas. C Es una clase !ue implementa una serie de mtodos determinados- y por eso se puede usar con thro#. 5ero no es tema de nuestro resumen- de modo !ue no nos detendremos en este detallito. ,implemente diremos !ue no todas las clases se pueden usar con thro#- y nosotros nos limitaremos por ahora a usar a ( ception y sus parientes. D Est/ definida en 2a'a.lan,- !ue se importa siempre autom/ticamente- de modo !ue Exception est/ siempre disponible en los programas 1a%a. E ;e hecho- podramos escribir el cdigo as- pues ( ception es una clase com#n y sil%estre: ( ception e = ne# ( ception( )$ thro# e$

3:3

Manejo de las excepciones


u sucede cuando en un programa !ue est/ ejecut/ndose 1a%a se encuentra con la lnea
thro# ne# ( ception( )"

.. El programa se detiene. 8. 1a%a toma el objeto ( ception( ) y (lo saca del contexto actual). ,acar del contexto !uiere decir !ue sube a un nivel de anidamiento superior en el programa- lo !ue en la pr/ctica normalmente significa salir del mtodo !ue se est/ ejecutando. :. El mecanismo de manejo de excepciones trata de buscar un lugar adecuado para !ue contin#e la ejecucin del programa. Este lugar adecuado se llama exception handler *manejador de excepciones+. ,u cometido es !ue el programa se recupere del accidente y pueda continuar normalmente- o termine de un modo elegante. El manejador de excepciones recibe el objeto ( ception !ue cre la parte del programa !ue gener la excepcin. 5ara entender cmo se maneja la excepcin en la pr/ctica- %amos a %er las cosas desde el punto de %ista del !ue usa un mtodo !ue puede generar una excepcin *en %ez del punto de %ista del !ue escribe el mtodo !ue genera la excepcin+. 5ara capturar la excepcin- debemos poner el cdigo !ue podra generarla dentro de una regin protegida *guarded region+. ,e usa la palabra reser%ada tr/ para se$alar la regin. 0a (regin protegida) no es m/s !ue un blo!ue de cdigo encerrado entre lla%es *F G+. H continuacin debe ir otra regin marcada con la palabra reser%ada catch. Iste es el blo!ue al !ue saltar/ el programa si se genera una excepcin dentro del blo!ue tr/. ,iguiendo con el ejemplo anterior- si !ueremos usar el mtodo 'ol'er() en nuestro cdigo- no podemos ob%iar el hecho de !ue puede generar una excepcinJ. 4ecesitamos encerrar la zona del cdigo !ue llame a %ol%er en un /rea restringida:
%* ... arri&a m)s c3di,o *% if ( command5ord.e6uals( 'ol'er! ) { tr/ { 'ol'er( )$ * catch (( ception e) { +/stem.out.println((rror inesperado.!)$ * * Este blo!ue de cdigo slo se ejecutar/ si %ol%er produce una excepcin..

'ol'er( ) est/ encerrada en un blo!ue tr/- pues puede generar una excepcin.

5or supuesto- podemos escribir todo el cdigo !ue necesitemos *y llamar a otros mtodos- crear objetos- etc.+ dentro de los blo!ues tr/ y catch. 5ero lo ideal es restringir el blo!ue a las lneas de cdigo !ue pueden generar la excepcin- y no a todo el mtodo. ?ay otro modo de lidiar con la excepcin !ue puede arrojar 'ol'er( ) : podramos no manejarla- sino pas/rsela al mtodo !ue llam a nuestro mtodo. ,i el cdigo anterior estu%iera dentro de un mtodo procces7ommand- tendramos !ue declarar !ue se mtodo arroja una excepcin. antes:
pu&lic &oolean process7ommand( 7ommand command) { ... *.

J 5ues est/ definido como pu&lic 'oid 'ol'er( ) thro#s ( ception {

3A3

(...)

ahora.K:

pu&lic &oolean process7ommand( 7ommand command) thro#s ( ception { (...) *

La clase Exception
9a hemos dicho !ue ( ception es una clase- y por eso podemos decir ne# ( ception( ). ;e este hecho se deri%an algunas consecuencias interesantes: a+ La clase Exception puede tener ms de un constructor- y de hecho los tiene:
pu&lic ( ception( )-

el constructor sin par/metros !ue hemos %enido usando.

pu&lic ( ception( +trin, messa,e )- un constructor !ue permite almacenar un ,tring dentro del objeto ( ception. Este (mensaje) se puede recuperar usando el mtodo +trin, ,et8essa,e( ) de la clase ( ception.

;e !u nos ser%ira poder almacenar un mensaje dentro del objeto !ue se crea cuando se lanza una excepcin" 5or ejemplo- para !ue el cdigo !ue maneje la excepcin pueda tener alguna informacin adicional sobre el error producido. &omo el mtodo !ue genera la excepcin no tiene idea ni de !uin lo ha llamado ni de !uin %a a manejar la excepcin- el #nico modo de pasar alguna informacin es el objeto
( ception.

5odramos reescribir el cdigo de 'ol'er( ) y process7ommand( ):


pu&lic 'oid 'ol'er( ) thro#s ( ception { if ( last1oom == null) { %% imposi&le... last1oom nunca de&er-a ser null thro#s ne# ( ception((rror. last1oom es null! )$ * else { %% (l c3di,o si,ue e2ecut)ndose normalmente 1oom tmp = current1oom$ current1oom = last1oom$ last1oom = tmp$ * * pu&lic &oolean process7ommand( 7ommand command ) { %* ... arri&a m)s c3di,o *% if ( command5ord.e6uals( 'ol'er! ) { tr/ { 'ol'er( )$ * catch (( ception e) { +/stem.out.println( e.,et8essa,e() )$ * *

G b+ ( ception tiene subclases. El hecho de !ue las situaciones !ue lle%an a generar una excepcin sean inesperadas no !uiere decir !ue estas situaciones no se puedan ordenar de alg#n modo. &uando creamos una sublcase- estamos especializando la superclase- delimitando con
.K 0a palabra throL puede ir tambin en la misma lnea !ue process7ommand( ...). H 1a%a realmente no le importa.

3B3

m/s precisin su campo de accin. Es justamente lo !ue hace 1a%a al definir subclases de ( ception. 5or ejemplo- los errores !ue pueden producirse cuando se lee o escribe a un archi%o om/s en general- a un stream- son errores de entradaMsalida *=nputM>utput+. 1a%a define- por tantouna excepcin llamada 9:( ception. Esta subclase de ( ception no a$ade ninguna funcionalidad- simplemente nos permite identificar- por su nombre- el tipo de error !ue se ha producido. ;e hecho read0ine* + de 6uffered7eader no dice thro# ne# ( ception( )$- sino thro# ne# 9:( ception( )N 1a%a define en su librera est/ndar muchsimos tipos de excepciones. 4osotros podemos saber !u tipo de excepcin genera un mtodo determinado fij/ndonos en la documentacin de la librera de clases. Hs- la documentacin del mtodo readLine( ) dice:

read!ine
public String readLine() throws IOException

7ead a line of text. H line is considered to be terminated by any one of a line feed *OPnO+- a carriage return *OPrO+- or a carriage return folloLed immediately by a linefeed. Returns" H ,tring containing the contents of the line- not including any line3termination characters- or null if the end of the stream has been reached #hro$s" IOException 3 =f an =M> error occurs c+ Podemos definir nuestras propias clases de excepciones derivando subclases de ( ception o de sus subclases. 4o necesitamos a$adir ninguna funcionalidad- basta crear un nombre #nico:
pu&lic ;ol'er( ception e tends ( ception { %* 'ac-o *% *

;ado !ue hay distintos tipos de excepciones- un mtodo puede generar distintos tipos de excepciones dependiendo de problema !ue se haya producido:
pu&lic un8etodo( ) thro#s ( ception { if ( ) { %* en al,<n punto del m=todo" se da un error" / entonces... ,eneramos la e cepci3n" / a6u- termina la e2ecuci3n del m=todo *% thro# ne# 9:( ception ( )$ * %* en otro punto del m=todo" podr-a ocurrir un error distinto 6ue nos fuer>a a ,enerar una e cepci3n" distinta a la anterior *% thro# ne# Null?ointer( ception( )$ *

El cdigo !ue maneja la excepcin puede manejar cada excepcin por separado: 3C3

tr/

{ +/stem.out.println(;amos un8etodo( )$ * catch( 9:( ception e) { %* este &lo6ue se e2ecuta (...) * catch(Null?ointer( ception e) %* este &lo6ue se e2ecuta una Null?ointer( ception

a e2ecutar un m=todo!)$ si se produ2o una 9:( ception *% { si se produ2o *%

* catch(( ception e) { %* por <ltimo" podr-amos comparar si ha/ al,<n tipo de e cepci3n 6ue no es de nin,uno de los tipos anteriores. (sto funciona por6ue ( ception es la e cepci3n m)s ,eneral@@... pero tiene 6ue ser la <ltima comparaci3n *% *

Un ejemplo m/s- usando readLine y 0uffered1eader:


pu&lic +trin, leeArchi'o(+trin, filename) thro#s 9:( ception { +trin, s$ tr/ { 0uffered1eader in = ne# 0uffered1eader( ne# File1eader( filename ) )$ * catch (9:( ception e) { %% esta e cepci3n ha sido arro2ada por File1eader. thro# ne# 9:( ception(No pude a&rir el archi'o! B filename B . +e produ2o una e cepci3n B e.,et8essa,e( ) )$ * tr/ { #hile ( (s B= in.readLine( ) ) .= null ) { $ %% no ha,o nada" s3lo acumulo... o2o con el B= en %% la l-nea de arri&a * * catch (9:( ception e) { %% (sta e cepci3n ha sido arro2ada por readLine( )$ %% A6u- imprimo el error" pero no ,enero una e cepci3nC %% decido de'ol'er s c3mo est=! +/stem.out.println(+e produ2o un error al leer el archi'o! B e.,et8essa,e( ) )$ * in.close( )$ return s$ %% cierro el stream

&omentarios- correcciones y sugerencias: 7oberto Qoia *roberto.zoiaRgmail.com+ Shis LorT is licensed under the &reati%e &ommons Httribution3,hareHliTe 8.B 0icense. So %ieL a copy of this license- %isit http:MMcreati%ecommons.orgMlicensesMby3saM8.BM or send a letter to &reati%e &ommons- BA: ?oLard ,treet- Bth Uloor- ,an Urancisco- &alifornia- JA.KB- U,H. .. Hs como todos los Datos son Animales- todos las clases deri%adas de ( ception son tambin de tipo ( ception.

3D3