CAPITULO
12
Mane,
Principales conceptos que se abordan en este
capitulo:
§ programacién defensiva @ informe de errores
& lanzamiento y manejo de excepciones ™ procesamiento simple de
archivos
Construcciones Java que se abordan en este
capitulo
TreeMap, TreeSet, SortedMap, assert, excepcidn, throw, throws, try,
catch, FileReader, FileWriter, Scanner, flujo
En el Capitulo 6 hemos visto que los errores légicos de los programas son mas difi-
ciles de descubrir que los errores sinticticos porque el compilador no los detecta. Los
errores ldgicos surgen por diversos motivos y en algunas situaciones pueden estar encu-
biertos:
@ La solucién de un problema puede estar implementada incorrectamente. Por ejemplo,
un problema que genera algunas estadisticas sobre los datos se puede haber pro-
gramado de tal manera que calcula el valor de la media en lugar del valor de la
mediana (el valor del medio).
Se puede haber solicitado a un objeto que haga algo que es incapaz de hacer. Por
ejemplo, se puede haber invocado al método get de una coleccién de objetos con
un indice que esta fuera del rango valido.
1m Se puede haber usado un objeto de maneras tales que no coinciden con las anti
padas por el diseiador de la clase, dejando al objeto en un estado inapropiado o
inconsistente. Esto ocurre con frecuencia cuando se reutiliza una clase en un
ambiente diferente de su ambiente original, probablemente mediante herencia.
Aungue las distintas estrategias de prueba discutidas en el Capitulo 6 nos pueden ayudar
a identificar y eliminar muchos errores l6gicos antes de que nuestros programas estén
listos para su uso, la experiencia nos sugiere que continuaran ocurriendo fallos en el
programa, Ademas, aun cuando un programa se pruebe exhaustivamente puede fallar
debido a circunstancias que estin mas all del control del programador. Considere, por
ejemplo, el caso de un navegador al que se le pide que muestre un sitio web que no384 Capitulo 12 lll Manejo de errores
Codigo 12.1
La clase
LibretaDeDirecciones
existe, 0 el de un programa que intenta grabar en un disco que no tiene mas espacio.
Estos problemas no son consecuencias de errores légicos, pero pueden facilmente hacer
que un programa falle si es que no se anticipé la posibilidad de que surjan,
En este capitulo veremos cémo anticiparse y responder a las posibles situaciones de
error que pueden surgir durante la ejecucién de un programa, Ademds, ofrecemos
algunas sugerencias sobre la manera de informar de los errores cuando éstos ocurren.
También brindamos una breve introduccién sobre los procesos de entrada y salida de
texto como una de las situaciones en la que pueden aparecer ficilmente errores durante
el tratamiento de los archivos.
El proyecto /libreta-de-direcciones
Usaremos la familia de proyectos libreta-de-direcciones para ilustrar algunos de los
principios de informe y manejo de los errores que surgen en muchas aplicaciones. Los
proyectos representan una aplicacién que almacena datos de contacto (nombre, direc-
cién y mimero de teléfono) de un nimero arbitrario de personas. En la libreta, los datos
de los contactos se ordenan alfabéticamente tanto por nombre como por nimero de
teléfono. Las clases principales que discutiremos son LibretaDeDirecciones
(Cédigo 12.1) y DatosDe1Contacto. Ademis, se proporciona la clase LibretaDe-
DireccionesDemo como un medio conveniente de preparar una libreta de direcciones
con algunos datos de ejemplo.
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.set;
import java.util.SortedMap;
import java.util. TreeMap;
import java.util.Treeset;
[**
* Una clase para mantener un némero arbitrario de
contactos.
* Los datos de los contactos se ordenan por nombre y
por
* ndmero de teléfono.
* @author David J. Barnes and Michael Kélling.
* @version 2006.03.30
ay
public class LibretaDeDirecciones
{
// Espacio para almacenar un numero arbitrario de
contactos.
private TreeMap libreta;
private int numeroDeEntradas;
i
* Inicializa la libreta de direcciones.
*/Codigo 12.1
(continuacion)
La clase
LibretadeDirecciones
12.1 El proyecto libreta-de-direcciones 385
public LibretaDeDirecciones()
{
libreta = new TreeMap();
numeroDeEntradas = 0;
}
je
* Busca un nombre o un numero de teléfono y
devuelve
* los correspondientes datos de ese contacto.
* @param clave El nombre o el numero a buscar.
* @return Los datos del contacto correspondiente a
la clave.
me
public DatosDelContacto getContacto(String clave)
{
return libreta.get (clave) ;
+
je
* Return si la clave actual est4 o no en uso.
* @param clave £1 nombre o el teléfono a buscar.
* @return true si la clave esta en uso, false en
caso contrario.
|
public boolean claveEnUso(String clave)
Hi
return libreta.containsKey (clave) ;
}
ye
* Agrega un nuevo contacto a la libreta de
direcciones.
* @param contacto Los datos de contacto asociados
con una persona.
ef
public void agregarContacto(DatosDelContacto contacto)
{
libreta.put(contacto.getNombre(), contacto) ;
libreta.put(contacto.getTelefono(), contacto);
numeroDeEntradas++;
+
[se
* Cambia los datos del contacto almacenados
previamente bajo
* la clave dada.
* @param claveVieja Una de las claves que se usd
para almacenar los
. datos del contacto.386 Capitulo 12 lll Manejo de errores
Cédigo 12.1
(continuacién)
La clase
LibretaDeDirecciones
* @param contacto Los datos del contacto que
reemplazarén a los
* existentes.
yy
public void modificarContacto(String claveVieja,
DatosDelContacto contacto)
{
eliminarContacto(claveVieja);
agregarContacto(contacto) ;
+
yee
* Busca todos los datos de los contactos
almacenados bajo
* una clave que comienza con un prefijo
determinado.
* @param prefijo 1 prefijo a buscar entre las
claves.
* @return Un arreglo con los contactos que se
encontraron.
of
public DatosDelContacto[] buscar(String prefijo)
{
List coincidencias =
new LinkedList();
// Busca las claves iguales o mayores que el
prefijo dato.
SortedMap cola =
libreta.tailwap(prefijo) ;
Iterator it = cola.keySet().iterator();
boolean finDeBusqueda = false;
while(!finDeBusqueda && it.hasNext()) {
String clave = it.next();
if(clave.startsWith(prefijo)) {
coincidencias.add(libreta.get (clave) );
}
else {
finDeBusqueda = true;
+
+
DatosDelContacto[] resultados =
new
DatosDelContacto[ coincidencias.size()];
coincidencias.toArray (resultados) ;
return resultados;
[eeCédigo 12.4
(continuacién)
La clase
LibretaDeDirecciones
12.1 El proyecto libreta-de-direcciones 387
* @return E1 numero de entradas que hay
actualmente en la libreta.
Mi
public int getNumeroDeEntradas()
{
return numeroDeEntradas;
}
**
* Elimina de 1a libreta, la entrada que tiene la
clave dada.
* @param clave Una de las claves de entrada a
eliminar.
xy
public void eliminarContacto(String clave)
{
DatosDelContacto contacto = libreta.get(clave);
libreta.remove(contacto.getNombre()) ;
libreta.remove(contacto.getTelefono());
numeroDeEntradas- -;
}
se
* @return Los datos de todos los contactos, en el
orden que
2 los almacena la clase
DatosDelContacto.
oy)
public String listarContactos()
iG
// Dado que cada entrada se almacena mediante
dos claves,
// es necesario construir un conjunto de
DatosDelContacto que
/] elimina los contactos duplicados.
StringBuffer todasLasEntradas = new
StringBuffer ();
Set contactosOrdenados =
new
TreeSet(libreta.values());
for(DatosDelContacto contacto :
contactosOrdenados) {
todasLasEntradas. append (contacto) ;
todasLasEntradas.append('\n')
todasLasEntradas.append('\n');
}
return todasLasEntradas.toString();388
Capitulo 12 lll Manejo de errores
Se pueden almacenar nuevos contactos en la libreta mediante el método agregar-
Contacto. Este método asume que los datos representan un nuevo contacto y no la
modificacién de los datos de un contacto que ya existe. Para cubrir este ultimo caso,
el método modificarContacto elimina una entrada anterior y la reemplaza por los
datos revisados. La libreta de direcciones proporciona dos maneras de obtener los datos
de los contactos: el método getContacto, que toma un nombre o un numero de telé-
fono como clave y devuelve los datos del contacto que coincide con la clave, y el
método buscar, que devuelve un arreglo con todos los contactos que comienzan con
determinada cadena de bisqueda. Por ejemplo, la cadena de busqueda «08459» devol-
vera todas las entradas cuyos mimeros de teléfono tengan ese prefijo de area.
Hay dos versiones introductorias del proyecto libreta-de-direcciones que se pueden
explorar, ambas proporcionan acceso a la misma versién de la clase LibretaDeDi-
recciones que se muestra en el Cédigo 12.1. El proyecto libreta-de-direcciones -vIt
proporciona una interfaz de usuario basada en texto, de estilo similar al de la interfaz
del juego zu! que tratamos en el Capitulo 7. Los comandos actualmente disponibles
en esta interfaz son los que permiten listar el contenido de la libreta, buscar algin con-
tacto y agregar una nueva entrada, Probablemente, la interfaz de la version libreta-de-
direcciones-vlg sea més interesante ya que incorpora una IGU sencilla, Experimente
con ambas versiones para obtener un poco de experiencia sobre la funcionalidad de la
aplicacién,
Ejercicio 12.1 Abra el proyecto libreta-de-direcciones-v1g y cree un objeto
LibretaDeDireccionesDemo. Invoque su método mostrarInterfaz para
visualizar la IGU e interactuar con la libreta de direcciones de ejemplo.
Ejercicio 12.2 Repita su experimentacién utilizando la interfaz de texto del
Proyecto libreta-de-direcciones-v1t
Ejercicio 12.3 Examine la implementacién de la clase LibretaDeDirec-
ciones y evaltie si considera que esta bien escrita o no. ¢Tiene alguna critica
especifica acerca de esta clase?
Ejercicio 12.4 La clase LibretaDeDirecciones usa varias clases del
paquete java.util; si no esta familiarizado con algunas de ellas, busque la
documentacién API para completar los baches que pueda tener. {Piensa que
se justifica el uso de tantas clases de utilidad diferentes? ¢Se podria usar un
HashMap en lugar de un TreeMap?
Ejercicio 12.5 Modifique las clases PalabrasComando y LibretaDeDi-
reccionesInterfazDeTexto del proyecto libreta-cle-direcciones-v1t de modo
que proporcionen acceso interactivo a los métodos getContacto y elimi-
narContacto de LibretaDeDirecciones.
Ejercicio 12.6 La clase LibretaDeDirecciones define un atributo para
registrar el numero de entradas. ,Considera que seria mas adecuado calcular
este valor a partir del nimero de entradas en el TreeMap? Por ejemplo,
gencuentra alguna situacién en la que el siguiente calculo no produciria el mismo
valor?
return libreta.size() / 2;